Pythonデータ処理 Pandasのキホン的な使い方を練習しよう

Pythonデータ処理 Pandasのキホン的な使い方を練習しよう

モモノキ&ナノネと一緒にPandasのキホン的な使い方を練習します。

Pythonデータ処理でよく利用するPandasのキホン的な使い方




Pandasのキホン的な使い方(忘れたときのおさらい)

モモノキ、Pythonをしばらく使っていなかったから、データの扱い方を忘れたかも。Pandasってどう使うんだっけ?

最近、R言語ばっかり使ってたからね...。ナノネ、今回はPadasのキホン的な使い方を復習しておこう。

Pandasだと、SeriesとDataFrameがあったよね?

Serisが一次元のデータ、DataFrameは二次元のデータを扱える型だよ。簡単なデータを作って実際に試してみよう。

まずは必要なライブラリをインポート。PadasとおまけでNumpyがあればとりあえずOKかな。

In [1]:
# ライブラリのインポート
import numpy as np
import pandas as pd

決まり文句でインポートした。

最初はSeriesから。PandasのSeriesメソッドが使うよ。引数に一次元データを指定すればOK。

In [2]:
srs = pd.Series(np.arange(10, 15))
srs
Out[2]:
0    10
1    11
2    12
3    13
4    14
dtype: int32

できた。

オプションでindex、nameも指定できるよ。

In [3]:
srs = pd.Series(data = np.arange(10, 15),
                index = ['a', 'b', 'c', 'd', 'e'],
                name='value')
srs
Out[3]:
a    10
b    11
c    12
d    13
e    14
Name: value, dtype: int32

少し思い出してきたかも。

Serisデータから値を抽出するには、いくつか方法があるけど。例え三番目の12の取得する場合はこんな感じ。

In [4]:
srs[2] # index番号で指定(ゼロ始まりので3番目はindex=2)
Out[4]:
12
In [5]:
srs['c'] # index名で指定
Out[5]:
12

もしくは、ilocやlocを使っても同じように値を取得できるよ。

In [6]:
srs.iloc[2] # ilocでindex番号を指定
Out[6]:
12
In [7]:
srs['c'] # locでindex名を指定
Out[7]:
12

複数指定するとときは:(コロン)だっけ、,(カンマ)だっけ?

連続した範囲を指定するときは:(コロン)で、とびとびのときは:(カンマ)を使ってリスト形式にしてね。

In [8]:
srs.iloc[1:4] # index番号1~3を抽出
Out[8]:
b    11
c    12
d    13
Name: value, dtype: int32
In [9]:
srs.iloc[[0, 2, 4]] # index番号0,2,4を抽出
Out[9]:
a    10
c    12
e    14
Name: value, dtype: int32

抽出できた。

次はDataFrame。Series単独よりも、こっちを利用する機会の方が多いよね。PandasのDataFrameメソットを利用するよ。こんな感じで二次元データを渡すとデータフレームが作成できるよ。

In [10]:
# 二次元データの作榮
data1 = np.array([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])
In [11]:
# 二次元データを使ってデータフレームを作成
df = pd.DataFrame(data1)
In [12]:
df
Out[12]:
0 1 2
0 1 2 3
1 4 5 6
2 7 8 9

インデックス名、列名とか指定する場合は?

オプションのindex、columnsで指定できるよ。

In [13]:
# データフレームの作成(インデックス、列名を指定)
df = pd.DataFrame(data=data1,
                  index=['a', 'b', 'c'],
                  columns=['COL1', 'COL2', 'COL3'])
In [14]:
df
Out[14]:
COL1 COL2 COL3
a 1 2 3
b 4 5 6
c 7 8 9

データフレームをつくるとき、キーと値の形式でデータを指定することもできるよ。たとば、身長、体重、胴囲を列として、6件のデータを作るとこんな感じ。

In [15]:
# 身長、体重、胴囲のデータ(キーと値でデータを指定)
df = pd.DataFrame({"Height": [168, 175, 188, 160, 158, 173],
                   "Weight": [75, 68, 88, 52, 48, 63],
                   "Weist": [76, 77, 90, 68, 58, 80]})
df
Out[15]:
Height Weight Weist
0 168 75 76
1 175 68 77
2 188 88 90
3 160 52 68
4 158 48 58
5 173 63 80

データフレームから値を抽出する方法は?

Serisのときとキホン一緒だけど、二次元データなので行や列を指定する必要があるよ。さっき作った身長・体重・胴囲のデータフレームで試してみよう。

次のケースはheight(身長)列の値だけ抽出する場合だよ。

In [16]:
df['Height'] # Height列の値を抽出
Out[16]:
0    168
1    175
2    188
3    160
4    158
5    173
Name: Height, dtype: int64
In [17]:
df.loc[:, 'Height'] # 全ての行のHeight列を抽出
Out[17]:
0    168
1    175
2    188
3    160
4    158
5    173
Name: Height, dtype: int64
In [18]:
df.iloc[:, 0] # 全ての行、0列目(Height)を抽出
Out[18]:
0    168
1    175
2    188
3    160
4    158
5    173
Name: Height, dtype: int64

抽出結果は同じだけど、書き方がいくつもあるみたい。どれを使うといいの??

ケースバイケースで使いやす表記でOKかも。でも、他の人が書いたコードが読めるように、知識としては複数の書き方を覚えておくのも大事だよ。

ナノネ、次はheight(身長)とWeight(体重)の列を抽出してみて。

In [19]:
df[['Height', 'Weight']] # HeightとWeight列を抽出
Out[19]:
Height Weight
0 168 75
1 175 68
2 188 88
3 160 52
4 158 48
5 173 63
In [20]:
df.loc[:, ['Height', 'Weight']] # 全ての行で、HeightとWeight列を抽出
Out[20]:
Height Weight
0 168 75
1 175 68
2 188 88
3 160 52
4 158 48
5 173 63
In [21]:
df.iloc[:, 0:2] # 全ての行、0~1列(Height、Weight)
Out[21]:
Height Weight
0 168 75
1 175 68
2 188 88
3 160 52
4 158 48
5 173 63

3通りの書き方を試してみたけど、どれもうまく抽出できたみたい。

参考までに、返り値の違いも確認しておこう。最初のケースみたいに1列だけ抽出した場合はSeries型、複数列抽出したときの返り値はDataFrame型になっているよ。

In [22]:
type(df.loc[:, 'Height']) # -> Series型
Out[22]:
pandas.core.series.Series
In [23]:
type(df.loc[:, ['Height', 'Weight']]) # -> DataFrame型
Out[23]:
pandas.core.frame.DataFrame

次は単純な行選択をいくつか試してみよう。

In [24]:
df.iloc[3, :] # 4行目(index3)、全ての列を抽出
Out[24]:
Height    160
Weight     52
Weist      68
Name: 3, dtype: int64
In [25]:
df.iloc[3:5, :] # 4~5行目(index3~4)、全ての列を抽出
Out[25]:
Height Weight Weist
3 160 52 68
4 158 48 58
In [26]:
df.iloc[[1, 3], :] # 2と4行目(index1と3)、全ての列を抽出
Out[26]:
Height Weight Weist
1 175 68 77
3 160 52 68

もし、ピンポイントで値を抽出したい場合は、こんな感じかな?

In [27]:
df.iloc[3, 1] # 4行目(index3)、1列目(weight列)の値を抽出
Out[27]:
52
In [28]:
df.loc[3, 'Weight'] # 4行目(index3)、weight列の値を抽出
Out[28]:
52

次はもう少し複雑な抽出をやってみよう。条件にマッチする行を抽出する方法だよ。

たとえば、Height(身長)が170より大きいデータ行を抽出するには...

In [29]:
df[df['Height'] > 170] # Heightが170より大きいデータ行を抽出
# df.loc[(df['Height'] > 170),:] 
Out[29]:
Height Weight Weist
1 175 68 77
2 188 88 90
5 173 63 80

条件に合うデータを抽出するときは、データフレームを入れ子するの?けっこう面倒かも。

動作的には内側の条件判定のところで、データ数分のbool値(True/False)が返ってくるよ。Heightが170より大きいデータはインデックス1,2,5が該当。インデックス1,2,5はTrueになってるね。

In [30]:
df['Height'] > 170 # 試しに内側の条件指定だけ出力
Out[30]:
0    False
1     True
2     True
3    False
4    False
5     True
Name: Height, dtype: bool

書き換えるとこんな感じかな?

In [31]:
# df[df['Height'] > 170] # インデックス1,2,5がTrue
df[[False, True, True, False, False, True]] # インデックス1,2,5を抽出
Out[31]:
Height Weight Weist
1 175 68 77
2 188 88 90
5 173 63 80

一度に複数の条件を指定したい場合は、&や|も使えるよ。

In [32]:
# Heightが170より大きい、かつHeightが180より小さいデータ行を抽出
df[(df['Height'] > 170) & (df['Height'] < 180)] 
Out[32]:
Height Weight Weist
1 175 68 77
5 173 63 80
In [33]:
# Heightが170より大きい、またはWeightが180より小さいデータ行を抽出
df[(df['Height'] > 170) & (df['Weight'] < 80)] 
Out[33]:
Height Weight Weist
1 175 68 77
5 173 63 80

カッコの付け忘れとか注意すれば、条件指定もなんとかなりそう。

もし、新しデータ列を付け足したいときは?

データフレームに新しい列名を定義して、値を代入すればOKだよ。身長と体重のデータがあるから、BMIを求めて新しい列に追加してみよう。

BMIは体重(kg)を身長(m)の二乗で割るんだっけ...

In [34]:
df['Weight'] / (df['Height'] / 100) ** 2 # BMI計算
Out[34]:
0    26.573129
1    22.204082
2    24.898144
3    20.312500
4    19.227688
5    21.049818
dtype: float64
In [35]:
df['BMI'] = round(df['Weight'] / (df['Height'] / 100) ** 2, 1) # BMI列を追加
In [36]:
df
Out[36]:
Height Weight Weist BMI
0 168 75 76 26.6
1 175 68 77 22.2
2 188 88 90 24.9
3 160 52 68 20.3
4 158 48 58 19.2
5 173 63 80 21.0

新しい列として、BMIを追加できた。

データ列を削除したいときは?

削除したい場合はdropメソッドが使えるよ。列単位で削除をしたい場合は、引数に列名とaxis=1を指定してみて。

In [37]:
df.drop('BMI', axis=1) # BMI列を削除してデータ出力
Out[37]:
Height Weight Weist
0 168 75 76
1 175 68 77
2 188 88 90
3 160 52 68
4 158 48 58
5 173 63 80
In [38]:
df # 元データフレームは更新されていない
Out[38]:
Height Weight Weist BMI
0 168 75 76 26.6
1 175 68 77 22.2
2 188 88 90 24.9
3 160 52 68 20.3
4 158 48 58 19.2
5 173 63 80 21.0

列削除されたデータは出力できたけど、元のデータフレーム自体は更新されないみたい。

元のデータフームを更新したい場合、データフレーム変数に再代入するか、inplace=Trueを指定すれば更新できるよ。

In [39]:
df = df.drop('BMI', axis=1)
# もしくは
# df.drop('BMI', axis=1, inplace=True)
In [40]:
df
Out[40]:
Height Weight Weist
0 168 75 76
1 175 68 77
2 188 88 90
3 160 52 68
4 158 48 58
5 173 63 80

列の平均値とか求めたいときは?

平均値を求めたいときはmean()でOK。統計量をまとめて出力したいときは、describe()が便利だよ。

In [41]:
df.mean() # 列の平均値
Out[41]:
Height    170.333333
Weight     65.666667
Weist      74.833333
dtype: float64
In [42]:
df.describe() # 基本統計量を出力
Out[42]:
Height Weight Weist
count 6.000000 6.000000 6.000000
mean 170.333333 65.666667 74.833333
std 11.003030 14.814407 10.888832
min 158.000000 48.000000 58.000000
25% 162.000000 54.750000 70.000000
50% 170.500000 65.500000 76.500000
75% 174.500000 73.250000 79.250000
max 188.000000 88.000000 90.000000

あと、データによっては欠損値がある場合も多いから、欠損値の扱いも覚えておこう。まずは欠損値があるデータを準備しよう。

In [43]:
# 欠損値を含む身長、体重、胴囲のデータ
df = pd.DataFrame({"Height": [168, 175, 188, 160, 158, 173],
                   "Weight": [75, np.nan, 88, 52, 48, 63],
                   "Weist": [76, np.nan, 90, np.nan, 58, np.nan]})
df
Out[43]:
Height Weight Weist
0 168 75.0 76.0
1 175 NaN NaN
2 188 88.0 90.0
3 160 52.0 NaN
4 158 48.0 58.0
5 173 63.0 NaN

欠損値(Nan)はWeightに1個、Weistに3個ある。

欠損値の確認は、isnull()が使えるよ。sum()で集計すれば、欠損値の個数も簡単に確認できるよ。

In [44]:
# 欠損値の個数を確認
df.isnull().sum()
Out[44]:
Height    0
Weight    1
Weist     3
dtype: int64

最初は欠損値があるデータ行を全部削除してしまう方法だよ。dropna()メソッドで処理できるよ。

In [45]:
df.dropna() # 欠損値がある行を全て削除
Out[45]:
Height Weight Weist
0 168 75.0 76.0
2 188 88.0 90.0
4 158 48.0 58.0

次は特定の列に欠損値がある行だけ削除する方法だよ。たとえばWeightの値が欠損値だったら、その行を削除するケース。

欠損値はisnull()で判定できるから...

In [46]:
df['Weight'].isnull() # Weightが欠損値のときTrueを返す
Out[46]:
0    False
1     True
2    False
3    False
4    False
5    False
Name: Weight, dtype: bool
In [47]:
df[df['Weight'].isnull() == False] # Weightが欠損値では無い行(isnullがFalse)だけ抽出
Out[47]:
Height Weight Weist
0 168 75.0 76.0
2 188 88.0 90.0
3 160 52.0 NaN
4 158 48.0 58.0
5 173 63.0 NaN

特定の値より大きい・小さいとか条件指定した方法とキホン一緒だね。

次は欠損値をなんらかの値で補完する方法だよ。簡易的な処理なら平均値、中央値、最頻値などを使うケースも多いよ。でも厳密な補完が必要なときは結構扱いが難しくて、多角的な分析が必要だよ。今回は練習なので欠損値は平均値で埋めてみよう。

In [48]:
df # 補完前(欠損値を含むデータ)
Out[48]:
Height Weight Weist
0 168 75.0 76.0
1 175 NaN NaN
2 188 88.0 90.0
3 160 52.0 NaN
4 158 48.0 58.0
5 173 63.0 NaN

欠損値を別の値で補完するときは、fillna()メソッドが便利だよ。

In [49]:
df['Weight'].fillna(df['Weight'].mean()) # Weightの欠損値をWeightの平均値で補完
Out[49]:
0    75.0
1    65.2
2    88.0
3    52.0
4    48.0
5    63.0
Name: Weight, dtype: float64
In [50]:
df['Weight'] = df['Weight'].fillna(df['Weight'].mean()) # データフレーム更新
df
Out[50]:
Height Weight Weist
0 168 75.0 76.0
1 175 65.2 NaN
2 188 88.0 90.0
3 160 52.0 NaN
4 158 48.0 58.0
5 173 63.0 NaN

欠損値だったWeightのインデックス1に数値(Weight平均値65.2)が入った。Weistも同じようにやってみる。

In [51]:
# Weistの欠損値を平均値で補完
df['Weist'] = round(df['Weist'].fillna(df['Weist'].mean()), 1)
df
Out[51]:
Height Weight Weist
0 168 75.0 76.0
1 175 65.2 74.7
2 188 88.0 90.0
3 160 52.0 74.7
4 158 48.0 58.0
5 173 63.0 74.7

Weistの欠損値補完も、うまくいったみたい。

次はデータセットを使った練習をやってみよう。データセットは毎度のIrisで。

In [52]:
# データセットのインポート
from sklearn.datasets import load_iris
iris =  load_iris() # irisデータ読み込み

読み込んだirisデータを使って、データフレームを作成してみよう。dataにiris.data、columnsはiris.feature_nameを指定しておこう。

In [53]:
# irisのdata、iris.feature_namesを使ってデータフレームを作成
df_iris = pd.DataFrame(data=iris.data, columns=iris.feature_names)

irisデータは150行あるんだけど。データの中身を一部だけチェックしたいときは、head()やtail()を使うと便利だよ。

In [54]:
df_iris.head() # 先頭の5行
Out[54]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
In [55]:
df_iris.tail() # 末尾の5行
Out[55]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
145 6.7 3.0 5.2 2.3
146 6.3 2.5 5.0 1.9
147 6.5 3.0 5.2 2.0
148 6.2 3.4 5.4 2.3
149 5.9 3.0 5.1 1.8
In [56]:
df_iris.head(n=8) # 表示するデータ数を指定(デフォルトは5)
Out[56]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
5 5.4 3.9 1.7 0.4
6 4.6 3.4 1.4 0.3
7 5.0 3.4 1.5 0.2

花の種類もあったはずだから、データフレームに追加してみる。花の種類はiris.targetに入っていたので...。

In [57]:
# 花の種類(Target)をデータフレームに追加
df_iris['target'] = iris.target
In [58]:
df_iris.head()
Out[58]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target
0 5.1 3.5 1.4 0.2 0
1 4.9 3.0 1.4 0.2 0
2 4.7 3.2 1.3 0.2 0
3 4.6 3.1 1.5 0.2 0
4 5.0 3.6 1.4 0.2 0
In [59]:
df_iris.tail()
Out[59]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target
145 6.7 3.0 5.2 2.3 2
146 6.3 2.5 5.0 1.9 2
147 6.5 3.0 5.2 2.0 2
148 6.2 3.4 5.4 2.3 2
149 5.9 3.0 5.1 1.8 2

花の種類(数値で0,1,2)をtarget列として追加できた!次は花の名前も出力してみる。

In [60]:
# 花の名前
iris.target_names
Out[60]:
array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

targetの0がsetosa、1がversicolor、2がvirginicaみたいだから...。こんな感じで、花の種類の数値を名前に変換できそう。

In [61]:
iris.target_names[iris.target] # target(0~2)を花の名前に変換
Out[61]:
array(['setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
       'setosa', 'setosa', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'versicolor',
       'versicolor', 'versicolor', 'versicolor', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
       'virginica', 'virginica', 'virginica'], dtype='<U10')
In [62]:
# 花の種類の名前をspecies列として追加
df_iris['species'] = iris.target_names[iris.target]
In [63]:
df_iris[0:3]
Out[63]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target species
0 5.1 3.5 1.4 0.2 0 setosa
1 4.9 3.0 1.4 0.2 0 setosa
2 4.7 3.2 1.3 0.2 0 setosa
In [64]:
df_iris[50:53]
Out[64]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target species
50 7.0 3.2 4.7 1.4 1 versicolor
51 6.4 3.2 4.5 1.5 1 versicolor
52 6.9 3.1 4.9 1.5 1 versicolor
In [65]:
df_iris[100:103]
Out[65]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target species
100 6.3 3.3 6.0 2.5 2 virginica
101 5.8 2.7 5.1 1.9 2 virginica
102 7.1 3.0 5.9 2.1 2 virginica

うまくできたみたいだね。

Pandasって他にも、いろいろ操作や便利機能があったよね。

うん、いっぱいあるね。でも一度にとても覚えきれないから、必要になった機能は都度調べながら試していこう。

最後によく使う機能として、データフレームをCSVファイルに書き出す方法と、ファイルからデータを読み込む練習もやっておこう。

In [66]:
# データフレームをCSVファイルに書き出す
df_iris.to_csv('mydata.csv', index=False) # 行インデックスなし
In [67]:
# CSVファイルからデータフレームに読み込む
df_mydata = pd.read_csv('mydata.csv')
In [68]:
df_mydata.head() # 読み込んだデータの確認
Out[68]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target species
0 5.1 3.5 1.4 0.2 0 setosa
1 4.9 3.0 1.4 0.2 0 setosa
2 4.7 3.2 1.3 0.2 0 setosa
3 4.6 3.1 1.5 0.2 0 setosa
4 5.0 3.6 1.4 0.2 0 setosa

読み書きの条件指定には文字コードやヘッダー有無など、オプションのパラメータがたくさんあるので必要に応じて調べてみてね。

今回のPandasのキホン的な使い方の練習(復習)は以上で。またね!

お疲れ様でした。






スポンサーリンク

0 件のコメント :

コメントを投稿