Pandasのshift, groupby活用してテーブルを変形する方法【Python】
![](https://tech.aru-zakki.com/wp-content/uploads/2024/03/pandas-shift-groupby.001.jpeg)
行を列に並べ替えたい、例えば、行方向に時系列に並んだデータを数個づつまとめて1行にしたり、複数行に並んだ属性データを一行にまとめて並べたりしたい場合があります。
ここでは、行方向に散らばったデータを列方向に並べる、テーブルの変形方法を2つ紹介します
はじめに
テーブルデータをターゲットにしたデータ分析を行う際、行に並んだデータを1行にまとめたい場合があります。
例えば、時系列データを週ごとにまとめたい場合や、同じカテゴリに属するデータを1つの行にまとめたい場合がこれにあたります。
具体的には、「1週間単位での統計情報を集計し、これを用いて将来の動向を予測したい」というケースが挙げられます。
ここでは、pandasでテーブルデータを効果的に変形する方法について解説します。
特に、groupbyメソッドは、データを特定の基準でグループ分けし、それぞれのグループに対して操作を行うものですが、これを活用することで表を変形することができます。
![](https://tech.aru-zakki.com/wp-content/uploads/2023/06/tabbycat.png)
機械学習の特徴量エンジニアリングなどでよく使います。
私も頻繁に使うのでメモがわりに記事にしました。
時系列に並んだデータをN個づつ横に並べる
例えば、時系列に変化する値x
が行に並んでいるデータがあるとします(下図右)。
これを5つずつまとめて行にし、平均や分散、最小値と最大値といった統計量を計算し、特徴量として追加する例を考えます。
![横に並べる例](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-12-1024x435.jpg)
テーブルの準備
サンプルのテーブルを作成します。テーブルは列x
だけの簡単なものです。
import pandas as pd
import numpy as np
# サンプルのDataFrameを作成
data = {
'name': ['a', 'b', 'a', 'c', 'd', 'b', 'c', 'a'],
'val': [10.0, 20.0, 20.0, 10.0, 20.0, 4.0, 5.0, 22.0]
}
df = pd.DataFrame(data)
![テーブル1](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-18.png)
shiftを使って、行をずらして列を追加する
現在のx
の値をT
として、T-2, T-1, T0, T1, T2
という列を作成して追加します。T<N>
の形の列はそれぞれ、-2, -1, 0, +1, +2行ずれたx
の値になります。
これは、shift()
を使って行うことができます。shift
では指定された値分だけ行をシフトします。このとき、シフトにより欠損するデータにはNaNが代入されます。
![](https://tech.aru-zakki.com/wp-content/uploads/2023/06/tabbycat.png)
埋める値はfill_valueパラメータで変更することも可能です
ここでは、列名をcols
に代入もおこなっています(cols
は統計情報を計算するときに利用します)
# 前後2個選択
cols = []
for i in range (-2, 3, 1):
colname = f"T{i}"
df[colname] = df['x'].shift(-i)
cols.append(colname)
shift
によりテーブルは以下のようになります。
![テーブル2](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-13.png)
nanを前後の値で補完する
nanを別の値で埋めます。ここでは、行の前後の値で埋めることにします。これは、x0
より前の値はx0
の値で、x9
より後はx9
の値で埋めることと同じです。
これには、interpolate()
を利用ます。前後両方から埋めるのでlimit_direction
パラメータはboth
を指定します。
![](https://tech.aru-zakki.com/wp-content/uploads/2023/06/tabbycat.png)
limit_direction
には'forward'
, 'backward'
, 'both'
の3つが指定できます。
# nanを前後の値で埋める
df = df.interpolate(limit_direction = "both")
補完後の結果は以下になります。これでnanが消えました。
![テーブル3](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-14.png)
統計量を計算して追加
各行のT-2, T-1, T0, T1, T2
の平均・分散・最小・最大を計算して列に追加します。cols
に計算の対象とする列リストを保存していたので、これを利用して計算します。
# 平均、分散、最大値、最小値を追加
df['mean'] = df[cols].mean(axis=1)
df['std'] = df[cols].std(axis=1)
df['min'] = df[cols].min(axis=1)
df['max'] = df[cols].max(axis=1)
![テーブル4](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-15-1024x689.png)
以上で、時系列で並んだx
を5つずつ行にまとめて、統計情報を計算した行の作成が完了です。
![](https://tech.aru-zakki.com/wp-content/uploads/2023/06/tabbycat.png)
このように変形することで時系列データを、行単位のテーブルデータとしてlightGBMなどで使って取り扱うことが可能です。意外と活用シーンは多いです。
同じ名前に関するデータを横にに並べる
2つ目の例は、name
が同じval
を1行にまとめるものです。
name
ごとに行数が違いますが、足りないものにはnanを入れる形で行にまとめます。
![同じデータを並べる](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-12-1024x369.png)
テーブルの準備
サンプルのテーブルを作成します。テーブルはname
とval
のあるもので、同じname
の行が複数あるものです。
例えば、売上伝票で「X日にaさんが購入した合計金額」などの情報が行で並んでいるようなものを考えるとイメージしやすいかもしれません。
この表を、name
でまとめていきます。
import pandas as pd
import numpy as np
# サンプルのDataFrameを作成
data = {
'name': ['a', 'b', 'a', 'c', 'd', 'b', 'c', 'a'],
'val': [10.0, 20.0, 20.0, 10.0, 20.0, 4.0, 5.0, 22.0]
}
df = pd.DataFrame(data)
![テーブル2−1](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-19.png)
groupbyを使ってnameでまとめる
groupby
を使うと、name
でまとめることが可能です。
以下のコードでは、name
でval
をまとめて、それをlist
に変換しています。
# name列を基準にグループ化し、各グループに対してval列の値を取得
grouped = df.groupby('name')['val'].apply(list).reset_index()
結果は以下のようになります。name
ごとにval
がリスト型でまとめられています。
![](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-20.png)
![](https://tech.aru-zakki.com/wp-content/uploads/2023/06/tabbycat.png)
この状態で、使うこともあります。
groupbyの結果から新しいテーブルを作成する
上記のデータから、valを分割した表を作成します。
grouped['val'].values.tolist()
を実行すると、val
の値がリストに変換されます。具体的には、以下のようになります
[[10.0, 20.0, 22.0], [20.0, 4.0], [10.0, 5.0], [20.0]]
これを引数としてDataFrameを作成します。
# 各グループの値をDataFrameに配置
new_df = pd.DataFrame(grouped['val'].values.tolist(), index=grouped['name'])
作成されたデータフレームは以下のようになります。
![テーブル2−2](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-17.png)
列名を変更し、nameを列にする
indexがname
になっているので、これを列に変換します。また、列名が0,1,2
になっているのでこれもval0, val1, val2
に変更します。
# 列名を設定
new_df.columns = [f'val{i}' for i in range(len(new_df.columns))]
# インデックスを列に変更
new_df.reset_index(inplace=True)
これで、目的のテーブルが完成しました。
![テーブル2−3](https://tech.aru-zakki.com/wp-content/uploads/2024/03/image-16.png)
まとめ
行方向に並んだデータの一部を、列方向に移動させて新しい表を作成する方法を2つ解説しました。個人的には、この加工は結構行います。