Pythonの便利技と注意点:Pandas編

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)

# DataFrameの作り方
```py:データ入力
import pandas as pd
dt = {
    'a':[30, 40], 
    'b':[20, 50]
}
dtf = pd.DataFrame(dt)
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/9b268ba8-8b40-765d-9886-427185312da6.png)

```py:カラム（列名）のつけ方
dtf.columns = ['A社', 'B社']
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/0cacd231-f21a-ec79-a328-503cfe8a5df9.png)

```py:インデックス（行名）のつけ方
dtf.index = ['4月売上', '5月売上']
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/069e303c-c839-188b-1d09-519d62ee68bc.png)

```Py:カラム名の変更方法
dtf = dtf.rename(columns = {"A社": "AAA社", "B社": "BBB社"})
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/fc2fda8c-e1e7-dd8d-275a-2c11efe85775.png)


```py:インデックスとカラム一度で設定することも可能
dt = [
    [30, 40],
    [20, 50]
]

dtf = pd.DataFrame(dt, index=['4月売上', '5月売上'], columns=['A社', 'B社'])
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/87570305-4c39-d34e-2d3b-fe290b547784.png)

# Indexが同じDataFrame同士を結合する
```Py:DataFrame_A作成
df_A = [
    [30, 40],
    [20, 50]
]

df_A = pd.DataFrame(df_A, index=['4月売上', '5月売上'], columns=['A社', 'B社'])
df_A
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/24ef1792-15f6-f7f4-695f-c076070a5794.png)

```Py:DataFrame_B作成
df_B = [
    [10, 90],
    [50, 40]
]

df_B = pd.DataFrame(df_B, index=['4月売上', '5月売上'], columns=['C社', 'D社'])
df_B
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/7fdbb4d5-e617-36e1-3491-bed723982544.png)

```Py:DataFrame_A と DataFrame_B を結合
df_AB = pd.concat([df_A, df_B], axis=1)
df_AB
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/6d276d74-a5af-3e28-da6c-4ac223c5177c.png)

# DataFrameからデータを抽出する
```py:列名を抽出する
dtf.columns
-------------------------------------
Index(['A社', 'B社'], dtype='object')
```

```py:行名を抽出する
dtf.index
--------------------------------------------
Index(['4月売上', '5月売上'], dtype='object')
```

```py:カラムのデータを抽出する　※1列のみの場合
dtf['A社']
-----------------------
4月売上    30
5月売上    20
Name: A社, dtype: int64
```

```py:カラムのデータを抽出する　※複数列の場合
dtf[['A社', 'B社']]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/f8aacb04-7302-1cb6-e0c7-149aca081b8b.png)

```py:行データを行名を指定して抽出する　※1行のみの場合
dtf.loc['4月売上']
---------------------------
A社    30
B社    40
Name: 4月売上, dtype: int64
```

```py:行データを行名を指定して抽出する　※複数行の場合
dtf.loc[['4月売上', '5月売上']]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/986d6a82-0f04-53bd-784a-e2a858bec6f4.png)

```py:行数でデータを抽出する　※1行のみの場合
dtf.iloc[0]
---------------------------
A社    30
B社    40
Name: 4月売上, dtype: int64
```

```py:行数でデータを抽出する　※複数行の場合
dtf.iloc[0:2]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/af82c97f-e6cb-2497-97ed-62af9cdb6a8b.png)

# 日付の連番を自動で生成する
```py:date_range関数で日付をperiodの分だけ連番で振れる
test_df = pd.DataFrame(list('ABCDEFGHIJ'), index=pd.date_range('20240101', periods=10))
test_df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/76f38c07-0e6b-15b7-e576-0d4f4c9ded13.png)

```Py:行を取り出す時は「-」あり・なし両方可能
# 両方とも同じ出力結果
test_df['2024-01-03':'2024-01-07']
test_df['20240103':'20240107']
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/f87188e8-4dbe-7573-3ba3-c0c7c950ba84.png)

# DataFrameの値をコピーする
参照コピーにならないので、元のDataFrameに影響を与えずに済む
```Py:copyライブラリのdeepcopy関数を使用する
import copy

df_copy = copy.deepcopy(df)
```

# DataFrameの列にある不要な文字を取り除く
```Py:数値によくある3桁ごとの「,」を取り除いて更にobject型からint64型に変換
df['money'] = df['money'].str.replace(',', '').astype('int64')
```

# 文字列の入ったDataFrameでmean()を実行すると起きるエラーを回避する
```Py:DataFrame作成
dt = [
    ['4月売上', 'X社', 40],
    ['4月売上', 'Y社', 50],
    ['5月売上', 'X社', 30],
    ['5月売上', 'Y社', 70],
]

dtf = pd.DataFrame(dt, columns=['売上月', '得意先', '売上高'])
dtf
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/7c8ddbd1-89ce-2c42-55f7-669e5826f72c.png)

```Py:objectで平均値とれないというエラー発生
dtf_group = dtf.groupby('売上月').mean()
dtf_group
==========
TypeError: agg function failed [how->mean,dtype->object]
```

```Py:numeric_only=Trueオプションを追加することで回避できる
dtf_group = dtf.groupby('売上月').mean(numeric_only=True)
dtf_group
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/1a5353f1-70a5-b29a-f579-78712de487dd.png)

# 並べて順位をつける
```Py:DataFrame作成
import pandas as pd

df = [
    ['ProductXX', 100],
    ['ProductYY', 120],
    ['ProductZZ', 30],
    ['ProductAA', 50],
]

df = pd.DataFrame(df, columns=['製品名', '売上高_万円'])
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/9287e61a-09ba-8e6d-dd2b-0370c57d97d0.png)

```Py:売上高を降順に並べる
df_sort = df.sort_values("売上高_万円", ascending=False)
df_sort
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/12c6600f-8ac7-eaab-81dd-c1b229c15337.png)

```Py:「順位」というカラムを追加して、そこに順位を入力
df_sort["順位"] = range(1, df_sort.shape[0]+1)
df_sort
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/dfaac7ea-2b21-1087-7f1e-07fdbba696b7.png)

```Py:カラムを並べ替える
df_sort = df_sort.reindex(columns=["順位", "製品名", "売上高_万円"])
df_sort
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/c043aa96-372c-79e9-f379-cc8e4b56331e.png)

```Py:Indexをリセットする
df_sort = df_sort.reset_index(drop=True)
df_sort
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/d1485c07-0ff7-30cb-cce4-ac143114ae69.png)


# 複数の列名を一気に変える
例として、e-StatでダウンロードしてきたCSVファイルによくある、「列と1行目の部分をドッキングして、不要部分を削除する」という操作を行う。
ここでは、列の「Unnamed 0～4」を「団体コード、都道府県名、市区町村名、性別」に変更して、index=0の行をdropする。
```Py:DataFrame再現
original_data = {
    'Unnamed: 0': ['団体コード', '-', '-', '-', '010006', '010006'],
    'Unnamed: 1': ['都道府県名', '合計', '合計', '合計', '北海道', '北海道'],
    'Unnamed: 2': ['市区町村名', '-', '-', '-', '-', '-'],
    'Unnamed: 3': ['性別', '計', '男', '女', '計', '男'],
    '総数': ['人', 123223561, 60093419, 63130142, 5148060, 2433466]
}
df = pd.DataFrame(original_data)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/fb1f2166-1ade-e495-43e6-d08ab81b855a.png)

```Py:削除するカラムをold_columns、新しい絡むをnew_columnsにリストにする
old_columns = list(df.columns[0:4])
new_columns = list(df.iloc[0, 0:4])

print(old_columns)
print(new_columns)
========================================================
# 出力結果
['Unnamed: 0', 'Unnamed: 1', 'Unnamed: 2', 'Unnamed: 3']
['団体コード', '都道府県名', '市区町村名', '性別']
```

```Py:古いカラムと新しいカラムを辞書形式にzipでドッキングする
columns_dict = {old:new for old, new in zip(old_columns, new_columns)}
print(columns_dict)
======================================================================
# 出力結果
{'Unnamed: 0': '団体コード', 'Unnamed: 1': '都道府県名', 'Unnamed: 2': '市区町村名', 'Unnamed: 3': '性別'}
```

```Py:DataFrameの列名を更新する
df.rename(columns=columns_dict, inplace=True)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/8313d935-afeb-69f1-2d99-76781b2bd390.png)

```Py:不要な0行目を削除して、Indexを振りなおす
df = df.drop(index=0).reset_index()
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/b05b0d0e-2331-9b54-6be4-9e51e0a37b4e.png)

# 列の範囲を指定して平均値等を出力する
データ指定の箇所で、df[columns[:]]を指定してあげると、最初に出した平均や2番目に出した分散などの影響を受けずに済む。
```Py:データ部分だけのカラムを指定
import numpy as np
import pandas as pd

data = [[172, 165, 186, 179, 168], [1.72, 1.65, 1.86, 1.79, 1.68]]
index = ["身長[cm]", "身長[m]"]
columns = ["A", "B", "C", "D", "E"]

df = pd.DataFrame(data=data, index=index, columns=columns)

df["平均"] = df[columns[0:5]].mean(axis=1, numeric_only=True)
df["分散"] = df[columns[0:5]].var(axis=1, numeric_only=True)
df["標準偏差"] = np.std(df[columns[0:5]], axis=1)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/bb622d01-bf3d-1620-8e57-148b8892f8bb.png)

```Py:カラムを指定しないとこうなる
import numpy as np
import pandas as pd

data = [[172, 165, 186, 179, 168], [1.72, 1.65, 1.86, 1.79, 1.68]]
index = ["身長[cm]", "身長[m]"]
columns = ["A", "B", "C", "D", "E"]

df = pd.DataFrame(data=data, index=index, columns=columns)

df["平均"] = df.mean(axis=1, numeric_only=True)
df["分散"] = df.var(axis=1, numeric_only=True)
df["標準偏差"] = np.std(df, axis=1)
df
```
平均については、A～Eまでの値を計算しているが、分散はA~平均までの値が入ってしまい、標準偏差はA～分散までの値が入ってしまう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588352/c75ab327-6354-a9c8-7d62-d36c97f5bec1.png)


[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)