機械学習
記事内に商品プロモーションを含む場合があります

Pandasのshift, groupby活用してテーブルを変形する方法【Python】

tadanori

行を列に並べ替えたい、例えば、行方向に時系列に並んだデータを数個づつまとめて1行にしたり、複数行に並んだ属性データを一行にまとめて並べたりしたい場合があります。

ここでは、行方向に散らばったデータを列方向に並べる、テーブルの変形方法を2つ紹介します

はじめに


テーブルデータをターゲットにしたデータ分析を行う際、行に並んだデータを1行にまとめたい場合があります。

例えば、時系列データを週ごとにまとめたい場合や、同じカテゴリに属するデータを1つの行にまとめたい場合がこれにあたります。

具体的には、「1週間単位での統計情報を集計し、これを用いて将来の動向を予測したい」というケースが挙げられます。

ここでは、pandasでテーブルデータを効果的に変形する方法について解説します。

特に、groupbyメソッドは、データを特定の基準でグループ分けし、それぞれのグループに対して操作を行うものですが、これを活用することで表を変形することができます。

機械学習の特徴量エンジニアリングなどでよく使います。

私も頻繁に使うのでメモがわりに記事にしました。

時系列に並んだデータをN個づつ横に並べる

例えば、時系列に変化する値xが行に並んでいるデータがあるとします(下図右)。

これを5つずつまとめて行にし、平均や分散、最小値と最大値といった統計量を計算し、特徴量として追加する例を考えます。

横に並べる例

テーブルの準備

サンプルのテーブルを作成します。テーブルは列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

shiftを使って、行をずらして列を追加する

現在のxの値をTとして、T-2, T-1, T0, T1, T2という列を作成して追加します。T<N>の形の列はそれぞれ、-2, -1, 0, +1, +2行ずれたxの値になります。

これは、shift()を使って行うことができます。shiftでは指定された値分だけ行をシフトします。このとき、シフトにより欠損するデータにはNaNが代入されます。

埋める値は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

nanを前後の値で補完する

nanを別の値で埋めます。ここでは、行の前後の値で埋めることにします。これは、x0より前の値はx0の値で、x9より後はx9の値で埋めることと同じです。

これには、interpolate()を利用ます。前後両方から埋めるのでlimit_directionパラメータはbothを指定します。

limit_directionには'forward''backward''both'の3つが指定できます。

# nanを前後の値で埋める
df = df.interpolate(limit_direction = "both")

補完後の結果は以下になります。これでnanが消えました。

テーブル3

統計量を計算して追加

各行の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

以上で、時系列で並んだxを5つずつ行にまとめて、統計情報を計算した行の作成が完了です。

このように変形することで時系列データを、行単位のテーブルデータとしてlightGBMなどで使って取り扱うことが可能です。意外と活用シーンは多いです。

同じ名前に関するデータを横にに並べる

2つ目の例は、nameが同じvalを1行にまとめるものです。

nameごとに行数が違いますが、足りないものにはnanを入れる形で行にまとめます。

同じデータを並べる

テーブルの準備

サンプルのテーブルを作成します。テーブルはnamevalのあるもので、同じ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

groupbyを使ってnameでまとめる

groupbyを使うと、nameでまとめることが可能です。

以下のコードでは、namevalをまとめて、それをlistに変換しています。

# name列を基準にグループ化し、各グループに対してval列の値を取得
grouped = df.groupby('name')['val'].apply(list).reset_index()

結果は以下のようになります。nameごとにvalがリスト型でまとめられています。

この状態で、使うこともあります。

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

列名を変更し、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

まとめ

行方向に並んだデータの一部を、列方向に移動させて新しい表を作成する方法を2つ解説しました。個人的には、この加工は結構行います。

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました