pandas メモ

2022年11月6日

はじめに

pandas についてのメモ。Jupyter Notebook での利用を想定。

インストール

$ pip install pandas

準備

%matplotlib inline
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd

データフレーム

基本

pandas の基本となるデータ構造はデータフレームである。

df = pd.DataFrame()

Excel で作る表みたいなイメージである。列に名前をつけてデータを割り当てられる。

df["身長"] = [147.9, 163.5, 159.8, 155.1, 163.3, 158.7, 172.0, 161.2, 153.9, 161.6]
df["体重"] = [41.7, 60.2, 47.0, 53.2, 48.3, 59.7, 58.5, 49.0, 46.7, 44.5]

df の出力結果は以下のようになる。

      身長     体重
0  147.9   41.7
1  163.5   60.2
2  159.8   47.0
3  155.1   53.2
4  163.3   48.3
5  158.7   59.7
6  172.0   58.5
7  161.2   49.0
8  153.9   46.7
9  161.6   44.5

ラベルを得たい場合は、次のようにする。

>>> df.columns
Index(['身長', '体重'], dtype='object')

データの個数が欲しい場合は、ふつうに len() でよい。

>>> len(df)
10

データにアクセスするには、次のようにする。

>>> df["身長"]
0    147.9
1    163.5
2    159.8
3    155.1
4    163.3
5    158.7
6    172.0
7    161.2
8    153.9
9    161.6
Name: 身長, dtype: int64
>>> df["身長"][0]
147.9

次のようにしてもよい。

>>> df.values[:, 0]
array([147.9, 163.5, 159.8, 155.1, 163.3, 158.7, 172. , 161.2, 153.9,
       161.6])

NumPy のデータとして得られる。ラベルのインデックスは次のように得られる。

>>> df.columns.get_loc("身長")
0

データを抽出するには次のようにする。

>>> df[(df["身長"] > 160) & (df["体重"] < 50)]
	身長	体重
4	163.3	48.3
7	161.2	49.0
9	161.6	44.5

計算

総和や平均値を得ることができる。

>>> df["身長"].sum()
1597.0
>>> df["身長"].mean()
159.7

列ごとの平均をとるには次のようにできる。

>>> df.apply(lambda x: x.mean())
身長    159.70
体重     50.88
dtype: float64

行ごとに計算したい場合は、次のようにできる。

>>> df["BMI"] = df.apply(lambda x: round(x["体重"]/(x["身長"]/100.)**2, 1), axis=1)
>>> df
      身長     体重	BMI
0	147.9	41.7	19.1
1	163.5	60.2	22.5
2	159.8	47.0	18.4
3	155.1	53.2	22.1
4	163.3	48.3	18.1
5	158.7	59.7	23.7
6	172.0	58.5	19.8
7	161.2	49.0	18.9
8	153.9	46.7	19.7
9	161.6	44.5	17.0

要素ごとに処理を行いたい場合は、次のようにする。

>>> df["身長"].map(lambda x: x if x > 150 else 150)
0    150.0
1    163.5
2    159.8
3    155.1
4    163.3
5    158.7
6    172.0
7    161.2
8    153.9
9    161.6
Name: 身長, dtype: float64

データフレーム全体について要素ごとに処理したい場合は、df.applymap() を用いる。

>>> df.applymap(lambda x: round(x))
      身長     体重	BMI
0	148	42	19
1	164	60	22
2	160	47	18
3	155	53	22
4	163	48	18
5	159	60	24
6	172	58	20
7	161	49	19
8	154	47	20
9	162	44	17

結合

データフレームを結合する場合を考える。以下の 2 つのデータフレーム df, df2 があったとする。

>>> df = pd.DataFrame()
>>> df["身長"] = [147.9, 163.5, 159.8, 155.1, 163.3]
>>> df["体重"] = [41.7, 60.2, 47.0, 53.2, 48.3]
>>> df
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
>>> df2 = pd.DataFrame()
>>> df2["身長"] = [158.7, 172.0, 161.2, 153.9, 161.6]
>>> df2["体重"] = [59.7, 58.5, 49.0, 46.7, 44.5]
>>> df2
      身長    体重
0  158.7  59.7
1  172.0  58.5
2  161.2  49.0
3  153.9  46.7
4  161.6  44.5

これを結合するには、pd.concat() を用いる。

>>> df3 = pd.concat([df, df2])
>>> df3
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
0  158.7  59.7
1  172.0  58.5
2  161.2  49.0
3  153.9  46.7
4  161.6  44.5

これだと元のインデックスが保持される。改めてインデックスを振り直す場合は、オプション "ignore_index=True" を指定して結合する。

>>> df3 = pd.concat([df, df2], ignore_index=True)
>>> df3
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
5  158.7  59.7
6  172.0  58.5
7  161.2  49.0
8  153.9  46.7
9  161.6  44.5

重複削除

重複したデータを削除するには、drop_duplicates() を用いる。上の結合の例で示したデータで、次のようにあえて重複したデータを作る。

>>> df3 = pd.concat([df, df, df2], ignore_index=True)
>>> df3
       身長    体重
0   147.9  41.7
1   163.5  60.2
2   159.8  47.0
3   155.1  53.2
4   163.3  48.3
5   147.9  41.7
6   163.5  60.2
7   159.8  47.0
8   155.1  53.2
9   163.3  48.3
10  158.7  59.7
11  172.0  58.5
12  161.2  49.0
13  153.9  46.7
14  161.6  44.5

重複を削除すると次のようになる。

>>> df3.drop_duplicates()
       身長    体重
0   147.9  41.7
1   163.5  60.2
2   159.8  47.0
3   155.1  53.2
4   163.3  48.3
10  158.7  59.7
11  172.0  58.5
12  161.2  49.0
13  153.9  46.7
14  161.6  44.5

上の場合、重複した分の最初に出てきたデータを採用している。最後に出てきたデータを採用したい場合は、次のようにする。

>>> df3.drop_duplicates(keep="last")
       身長    体重
5   147.9  41.7
6   163.5  60.2
7   159.8  47.0
8   155.1  53.2
9   163.3  48.3
10  158.7  59.7
11  172.0  58.5
12  161.2  49.0
13  153.9  46.7
14  161.6  44.5

ある列についてだけ重複削除を行うには、オプション subset で列を指定する。

>>> df3.drop_duplicates(subset="身長", keep="last")

インデックスを振り直すには、reset_index() を用いる。

>>> df3.drop_duplicates().reset_index(drop=True)
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
5  158.7  59.7
6  172.0  58.5
7  161.2  49.0
8  153.9  46.7
9  161.6  44.5

並び替え

>>> df.sort_values(by="身長").reset_index(drop=True)
      身長    体重
0  147.9  41.7
1  153.9  46.7
2  155.1  53.2
3  158.7  59.7
4  159.8  47.0
5  161.2  49.0
6  161.6  44.5
7  163.3  48.3
8  163.5  60.2
9  172.0  58.5

書き出す

CSV として書き出すには次のようにする。

df.to_csv("data.csv", index=False)

index オプションに False を指定しておかないと、インデックスも出力される。

CSV を読み込む

df = pd.read_csv("data.csv")

デフォルトではヘッダーが想定される。ヘッダーが存在しないデータを読み込む場合は、次のようにする。

df = pd.read_csv("data.csv", header=None)

この場合、ラベルとして数字が割り当てられる。ラベルを指定したい場合は、次のようにする。

df = pd.read_csv("data.csv", names=("A", "B", "C"))

ヘッダーが存在するデータのラベルを書き換えるには、次のようにする。

df = pd.read_csv("data.csv", names=("A", "B", "C"), header=0)

あとからラベルを変えることもできる。

df2 = df.rename({"A": "a", "B": "b", "C": "c"})

列の 1 つをインデックスに指定したい場合は、index_col で指定する。

df = pd.read_csv("data.csv", index_col=0)

Excel を読み込む

df = pd.read_excel("data.xlsx", sheet_name="Sheet1", header=None)

欠損値の処理

>>> df = pd.DataFrame()
>>> df["身長"] = [147.9, 163.5, 159.8, 155.1, 163.3, 158.7, 172.0, 161.2, 153.9, 161.6]
>>> df["体重"] = [41.7, 60.2, 47.0, 53.2, 48.3, 59.7, 58.5, 49.0, np.nan, 44.5]
>>> df
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
5  158.7  59.7
6  172.0  58.5
7  161.2  49.0
8  153.9   NaN
9  161.6  44.5

単純に削除する

>>> df.dropna()
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
5  158.7  59.7
6  172.0  58.5
7  161.2  49.0
9  161.6  44.5

ある値で埋める

>>> df.fillna(0)
      身長    体重
0  147.9  41.7
1  163.5  60.2
2  159.8  47.0
3  155.1  53.2
4  163.3  48.3
5  158.7  59.7
6  172.0  58.5
7  161.2  49.0
8  153.9   0.0
9  161.6  44.5

補間する

たとえば、身長順に並べてから補間する。

>>> df.sort_values(by="身長")
      身長    体重
0  147.9  41.7
8  153.9   NaN
3  155.1  53.2
5  158.7  59.7
2  159.8  47.0
7  161.2  49.0
9  161.6  44.5
4  163.3  48.3
1  163.5  60.2
6  172.0  58.5

線形補間

>>> df.sort_values(by="身長").interpolate()
      身長     体重
0  147.9  41.70
8  153.9  47.45
3  155.1  53.20
5  158.7  59.70
2  159.8  47.00
7  161.2  49.00
9  161.6  44.50
4  163.3  48.30
1  163.5  60.20
6  172.0  58.50

前の値で埋める

>>> df.sort_values(by="身長").interpolate(method="ffill")
      身長    体重
0  147.9  41.7
8  153.9  41.7
3  155.1  53.2
5  158.7  59.7
2  159.8  47.0
7  161.2  49.0
9  161.6  44.5
4  163.3  48.3
1  163.5  60.2
6  172.0  58.5

後ろの値で埋める

>>> df.sort_values(by="身長").interpolate(method="bfill")
      身長    体重
0  147.9  41.7
8  153.9  53.2
3  155.1  53.2
5  158.7  59.7
2  159.8  47.0
7  161.2  49.0
9  161.6  44.5
4  163.3  48.3
1  163.5  60.2
6  172.0  58.5

日付の datetime への変換

df = pd.DataFrame()
df["時間"] = ["2021-1-1 01:00:00", "2021-2-2 02:00:00", "2021-3-3 03:00:00"]
dt = pd.to_datetime(df["時間"])

時間による抽出

from datetime import datetime
from dateutil.relativedelta import relativedelta

dt = pd.to_datetime(df["時間"])
dt0 = datetime.strptime("202201", "%Y%m")
dt1 = datetime.strptime("202207", "%Y%m")
dt1 += relativedelta(months=1)

df1 = df[(dt0 < dt) & (dt < dt1)]