
こんにちは。清野(@historoid)です。
今回は、Pythonデータ分析に必須のライブラリ、Pandasの紹介です。
Pythonによるデータサイエンスが流行している現在、Pandasを触ったことがある人も多いと思います。
import pandas as pd
を何度書いたことか……、なんて人も多いでしょう。
今回は、そんなPandasの基本的な使い方を紹介します。基本さえ理解していれば複雑なテーブル操作も楽ちんですよ。

さっそくPandasの基本的な使い方を見ていきましょう。
今回選んだのは以下の5つです。
- データの読み込み
- 基本統計量の表示
- 欠損値の確認
- 行と列の指定
- 条件によるデータの抽出
この5つは、私がデータ解析をするときに必ず行う操作です。
そしてこの順番のとおりに作業しています。とくに上の3つは必ず行います。

まずはデータを読み込まないことには始まりません。
Google Drive上のデータをGoogle Colabから読み込む方法
私はGoogle Colabをよく使うので、Google Drive上にあるデータ(エクセルファイル)を読み込む方法を紹介します。
# Google DriveをGoogle Colabにマウント
from google.colab import drive
drive.mount('/content/drive')
この操作で、Google Colabマシンの/content/drive
にあなたのGoogle Driveが接続されますが、認証作業が必要になります。
上記のコードを実行すると以下のようなURLが表示されます。

URLをクリックしてください。すると自分のGoogleアカウント選択画面に遷移します。

そしてデータが置いてあるGoogle Driveのアカウントを選択してください。

そして「許可」します。

そしてこのコードをコピーして、Google Colabの画面に戻ってください。

そして先ほどのフォームにコピペします。これでOKです。
import pandas as pd
df = pd.read_excel('/content/drive/My Drive/xxxx/xxxx.xlsx')
これでdf
にはエクセルファイルの中身が入りました。
/content/drive/My Drive/
まではみんな同じです。Google Driveのマイドライブの中身がMy Drive/
です。
例えば、マイドライブに「実験結果
」というフォルダがあり、「フィールドワーク.xlsx
」というエクセルファイルがあれば、/content/drive/My Drive/実験結果/フィールドワーク.xlsx
と入力してください(ファイル名は基本的には英語で管理した方がいいです)。
フォルダの区切りを示すスラッシュ/
は、Windowsの場合は¥
になります。
もしエクセルファイルが複数シートであれば、pd.read_excel('xxxx/xxxx', sheet_name=None)
と引数を足してください。
これで複数シートからなるdf
が作られます。df['sheet_name_xxxx']
とシート名を指定すれば、その1枚分のシートが表示されます。
df
これだけでセルを実行すると、df
の中身が表示されます。print(df)
でもOKです。
Google Colab上のCSVファイルを読み込む
実はGoogle Colabには、サンプルデータがあらかじめ入っています。
!ls
# sample_data
!ls sample_data/
# anscombe.json mnist_test.csv
# california_housing_test.csv mnist_train_small.csv
# california_housing_train.csv README.md
!ls
はGoogle Colabマシンに対するbashコマンドであり、Pythonコマンドではありません(意味がわからなくてもOKです)。
!
をつけることで、Google Colab(Jupyter)上からシェルコマンドを使えています。
ls
コマンドではフォルダの中身を見ることができます。その結果がsample_data
です。
つまり「sample_dataってフォルダがあるよ」という意味です。それに対して、ls sample_data
とすることで、今度はsample_dataの中身を見ているわけです。
その結果、いくつかのファイルが見つかります。
.csvファイルがあったので、pd.read_csv()
で読み込みます。
import pandas as pd
df = pd.read_csv('sample_data/california_housing_train.csv')
このように、もとからGoogle Colabに入っていれば、from google.colab import drive
のくだりは必要ないわけですね。

さて、データの読み込みが長くなりましたが、あとはさらっと進めましょう。
まずは基本統計量の表示です。これでまずデータ全体をざっと確認します。
df.describe()
たったこれだけです。
これで、列(説明変数)ごとの基本統計量を確認することができます。
count | 欠損値を除くデータの個数 |
mean | 平均値 |
std | 標準偏差 |
min | 最小値 |
25% | 小さい方から25%目の値 |
50% | 中央値 |
75% | 小さい方から75%目の値 |
max | 最大値 |
すごく便利ですよね。
この作業をエクセルでしようと思ったら、結構時間がかかります。
しかもエクセルではデータ量が増えると大変です。エクセルは高機能なアプリですが、データの選択が面倒なのです。
なおこのdf.describe()
で表示されるのは、計算可能な列だけです。
説明変数の中に文字列や時系列データが混ざっていると、

次に重要なのが欠損値の確認です。
口を酸っぱくして言い続けますが、データ解析を始める前に欠損値の確認をしてください。
というのも、欠損値はPandasの内部では、NaN
(Not a Number: 非数)として処理されるためです。
そしてNaNは計算できません。NaN+1
は、NaN
になります。
つまり、計算過程の中にNaNが混ざると、答えはNaNになってしまうのです。
苦労して「このデータとこのデータを抽出して……」とかやった計算がNaN
になったり、エラーになります。
df.isna().any()
これで欠損値の確認を行うことができます。
False
が返ってくると「欠損値はないよ」という意味です。
df.isna()
これだけだと、データフレームのすべての要素に対してTrue
かFalse
が返ってきます。
これを.any()
とすることで、「1つでもあればTrue
を返せ」とするわけです。

さて、今度は必要な行や列の指定を行いましょう。
行数や列数による指定
# 0行目を抽出
df.iloc[0]
数値による指定は、iloc
メソッドを使います。
Pandasでは行の指定が先にきますので、iloc[0]
と書けば、自動的に0行目を意味します。
# 0列目を抽出
df.iloc[:,0]
これは正確には「すべての行の0列目」を指定しています。まずiloc[:]
で「すべての行」という意味です。
そこでカンマで区切って、列の情報を追加します。iloc[:, 0]
とすると、「0列目」という指定が追加されたわけです。
スプライシングによる指定
応用です。iloc
を使って、0行目から100行目までを抜き出しましょう。
# 0行目から100行目まで
df.iloc[0:100]
Pythonのリストも同様ですが、[0:100]
としたときは、\(0\)以上\(100\)未満が選ばれます(つまり\(99\)までが抽出されます)。
ややこしいように感じるかもしれませんが、以下のように考えてください。
数直線上で、「0」の左側に境界線を引き、「100」の左側に境界線を引きます。境界線の間の数字を抽出します。
ほら。こう考えれば、100は含まれませんよね?
列名による指定
# longitude列を抜き出す
df['longitude']
簡単ですね。列名をブラケット[]
の中に入れればOK。シングルクォート''
を忘れずに!!
# 列名を知りたいなら
df.columns
先に書いたdf.describe()
の時点で列名は表示されていますが、念の為。
ドット記法で列名指定
個人的にはあんまり好きではないのですが、込み入ってくるとこれじゃないと書けない場合もあります。
# longitude列を抜き出す
df.longitude
はい。かなり直接的な書き方ですね。列名をアトリビュート(属性)としてコールするわけです。
複合的な書き方
ではこれまで書いた方法を使って、複合的な条件で抜き出してみましょう。
以下はすべて同じ要素が返ってきます。
# はじめの10行のlongitude列を抜き出す。
# 1
df.iloc[0:10, 0]
# 2
df.iloc[0:10]['longitude']
# 3
df.iloc[0:10].longitude
他にも書き方はありますが、基本的にこれでOKです。というかこれで書いておくれ (・・;

では最後の項目です。条件によるデータの抽出をやっていきましょう。
例えば「血圧が120以上のひとの人数を知りたい」とか「価格が1万円以上の商品の購入者の年齢が知りたい」など、データ分析には条件によるサンプリングが不可欠です。
条件はブラケット[]
の中に書く
[]
の中に書くPandasの操作は簡単です。
ではlatitude列の値が34より大きいデータを抜き出してみましょう。
# まずlatitude列を抜き出す(どの列でもいいよ)
row_1 = df['latitude']
# latitude列の値が34.0より大きかの判定
result = row_1 > 34.0
'''
0 True
1 True
2 False
3 False
4 False
...
16995 True
16996 True
16997 True
16998 True
16999 True
'''
途中ですけど、ここまで大丈夫ですか?
row_1 > 34.0
とすることで、row_1
の要素のうち、34より大きいものはTrue
、小さいのもはFalse
になりました。その結果をresult
に代入してます。
df[result]
これで抽出できました。簡単!!
まとめます。せっかくなので違う列で試してみます。
# 条件をつけたい列を取り出す。
row_households = df['households']
# 条件の設定
condition = row_households < 450
# データフレームからの抽出
df[condition]
こんな感じです。
でもわざわざ変数に置き換えるのも面倒です。一行でまとめちゃいましょう。
df[df['households']<450]
すっきりしましたね。これだけでいいんですよ。
多重条件
条件を多重にしても基本は変わりません。「条件はブラケットの中」です。
# 1つ目の条件
c1 = df['households']<450
# 2つ目の条件
c2 = df['latitude']<34.0
# 多重条件で抽出
df[c1 & c2]
つまり&
で条件を足しただけですね。
これも変数に置き換えずに一行で書けます。
df[(df['households']<450) & (df['latitude']<34.0)]
気をつけてください!!
変数に置き換えないときは、括弧()
が必要です。
上記の例はAND
でしたが、OR
も指定できます。
df[(df['households']<450) | (df['latitude']<34.0)]
簡単ですね。&
のかわりに|
を使っただけです。
標本数を数える
条件で抜き出したからには、それがいくつあるのか知りたいでしょう。
# 条件
mask = df['households']<450
# 条件に合致する標本のカウント
mask.sum()
sum()
は合計値を取りますが、True
は1、False
は0としてカウントされます。そのためmask
に対して合計値を求めると標本数がわかるのです。
以下に間違いやすい例を書きます。
# 条件
mask = df['households']<450
# ダメな例
df[mask].sum()
# それぞれの列の合計値が算出される
# 良い例
len(df[mask])
# len()で行列の行数をカウントできる
条件で抽出し終わってからsum()
にしちゃうと、列ごとの合計値が出てしまいます。

他にも使いそうなものを挙げておきます。
すべてが欠損値の行や列を削除する
# 1行(標本を1つ)削除
df.dropna(how='all')
# 1列(説明変数を1つ)削除
df.dropna(how='all', axis=1) # axis=0は行、axis=1は列を示す
1つでも欠損値を含む行や列を削除する
# 1行(標本を1つ)削除
df.dropna(how='any')
欠損値を穴埋めする
# 欠損値を0で穴埋め
df.fillna(0)
# 平均値で穴埋め
df.fillna(df.mean())
# 最頻値で穴埋め
df.fillna(df.median())
欠損値を含む行を抽出する
df.iloc[:, df.isnull().any()]
# 例えば2倍にする
def double(x):
return x*2
df.applymap(double)
# 適用先がSeriesなら
df.map(double)
ユニークな値を取得する
# Seriesに対して
row_xxxx.unique()
ユニークな値をカウントする
# Seriesに対して
row_xxxx.value_counts()
要素の値でソートする
df.sort_values('row_name_xxxx')
# 引数inplace=Trueで元のオブジェクトを変更
# デフォルトではソートされた新しいdfを生成
インデックスでソートする
df.sort_index()
# 列名でソート
df.sort_index(axis=1)
インデックスを振り直す
df.reset_index()
# index列が自動的に生成され、もとのインデックスが保存される
# 新しいデータフレームが生成されるので変数に代入すること
# もとインデックスが不要なら
df.reset_index(drop=True)
# もとのオブジェクトに上書き
df.reset_index(drop=True, inplace=True)
列名の変更
# 変更は辞書で行う
df_new = df_old.rename(columns={'origin':'new_one', 'origin2':'new_one2'})
行名の変更
# 変更は辞書で行う
df_new = df_old.rename(index={'origin':'new_one', 'origin2':'new_one2'})

いやー。長かったですね。
基本だけでこんなに長いんかい、と思うかもしれませんが、そんなに多機能なことをしないと前処理できないってことなんです。
機械学習ライブラリに付属するデータセットで勉強しているうちはあまり感じませんが、実際に他の人から持ち込まれたエクセルを処理するようになると、Pandasのすごさがよく分かります。
今回は以上です。長くなりましたが、辞書的に使ってください。