Polarsでのテーブルデータ操作方法解説

Polarsでのテーブルデータ操作方法解説
目次

前回の記事 ではPolarsでの文字列処理をメインに解説しました。
今回はPolarsが本領を発揮するテーブルデータに対する操作について説明していきます。

テーブルデータ操作の内容は時と場合とデータによって様々考えられますが、おおよそ次に挙げる操作を押さえておけば多くの場面で適用できると思います。

  1. 条件に応じたデータ抽出
  2. テーブル同士の結合操作
  3. 特定のカテゴリ内での集計操作(顧客ごとに売上実績を合計するなど)
  4. 計算結果を新たな列として作成する操作

初めにPolarsをインストールしてインポートしておきましょう。

!python -m pip install polars

import polars as pl

列の追加

Polarsでデータに列を追加するには with_columns() を用います。
既存の列から何かしらの計算をした結果を新たな列として追加するなど、多くの場面で用いる関数です。
以下では1つの列に1を足した新たな列を作成する例です。

# データフレームの作成
df = pl.DataFrame({
    'a': [1, 2, 3],
    'b': [4, 5, 6]
})

# 新しい列を追加する
df = df.with_columns([
    (pl.col('b') + 1).alias('c')
])

print(df)

出力結果

 a    b    c   
 ---  ---  --- 
 i64  i64  i64 
╞═════╪═════╪═════╡
 1    4    5   
 2    5    6   
 3    6    7   

条件に応じたデータの抽出

Polarsにおいてデータフレームから特定の列を抽出するには select を、条件に応じて行を抽出するには filter を用います。

select

Polarsで特定の列を抽出するために用います。

polars.DataFrame.select

以下は上記の with_columns と同様のデータを用いた例です。
select は既存の列を抽出するだけでなく、既存の列に何かしらの演算を加えた列を新たな名前で抽出することもできます。

# データフレームの作成
df = pl.DataFrame({
    'a': [1, 2, 3],
    'b': [4, 5, 6]
})

# 列を選択する
df = df.select([
    'b',
    (pl.col('b') + 1).alias('c')
])

print(df)

出力結果

 b    c   
 ---  --- 
 i64  i64 
╞═════╪═════╡
 4    5   
 5    6   
 6    7   

もしかしたら with_columnsselect の違いが分からなくなるかもしれませんが、 with_columnsデータフレームに列を追加 するのに対して selectデータフレームから列を抽出する という違いがあります。

filter

filterとは、条件に合致する行を抽出する機能です。

polars.DataFrame.filter

以下は、商品名が「A」の行だけを抽出する例です。

# データの作成
data = {'product': ['A', 'B', 'A', 'C', 'B', 'A'],
        'sales': [100, 200, 150, 300, 250, 200]}
df = pl.DataFrame(data)

# filterによる行の抽出
result = df.filter(pl.col('product') == 'A')

print(result)

出力結果:

shape: (3, 2)
┌─────────┬───────┐
 product  sales 
 ---      ---   
 str      i64   
╞═════════╪═══════╡
 "A"      100   
├─────────┼───────┤
 "A"      150   
├─────────┼───────┤
 "A"      200   
└─────────┴───────┘

テーブルの結合

Polarsにおけるテーブルの結合には join を用います。

polars.DataFrame.join

inner join

以下は商品情報と売り上げデータを結合する例です。
厳密には以下の例はinner joinと呼ばれ、両方のテーブルに存在するレコードを紐づけます。
(joinのデフォルトがinner joinです)

# 商品情報のデータ作成
product_data = {'product_id': [1, 2, 3],
                'product_name': ['A', 'B', 'C']}
product_df = pl.DataFrame(product_data)

# 売り上げデータのデータ作成
sales_data = {'product_id': [1, 2, 2, 3, 3, 4],
              'sales': [100, 200, 150, 300, 250, 200]}
sales_df = pl.DataFrame(sales_data)

# joinによるデータの結合
result = sales_df.join(product_df, on='product_id')

print(result)

出力結果:

shape: (5, 3)
 product_id  sales  product_name 
 ---         ---    ---          
 i64         i64    str          
╞════════════╪═══════╪══════════════╡
 1           100    A            
 2           200    B            
 2           150    B            
 3           300    C            
 3           250    C            

joinメソッドによって、商品情報と売り上げデータが結合されました。

テーブルの結合はその結合方法によっていくつかバリエーションがあります。
以下ではよく用いるものについて解説します。
全ての結合方法については公式ページを参照ください。

polars.DataFrame.join

left join

left joinは、左側のテーブルにある全てのレコードと、右側のテーブルにある該当するレコードを結合します。
右側に該当するレコードがない場合は、NULL値が入ります。

以下は、売り上げデータと商品情報をleft joinする例です。

# 商品情報のデータ作成
product_data = {'product_id': [1, 2, 3],
                'product_name': ['A', 'B', 'C']}
product_df = pl.DataFrame(product_data)

# 売り上げデータのデータ作成
sales_data = {'product_id': [1, 2, 2, 3, 3, 4],
              'sales': [100, 200, 150, 300, 250, 100]}
sales_df = pl.DataFrame(sales_data)

# left joinによるデータの結合
result = sales_df.join(product_df, on='product_id', how='left')

print(result)

出力結果

 product_id  sales  product_name 
 ---         ---    ---          
 i64         i64    str          
╞════════════╪═══════╪══════════════╡
 1           100    A            
 2           200    B            
 2           150    B            
 3           300    C            
 3           250    C            
 4           100    null         

cross join

cross joinは、左側のテーブルの全てのレコードに、右側のテーブルの全てのレコードを結合する機能です。
個人的には、何らかのマスタデータのようなものを作成したいときに使うことが多いでしょうか。
以下は、商品情報と売り上げデータをcross joinする例です。

# 行が省略されないようにする
pl.Config.set_tbl_rows(-1)

# 商品情報のデータ作成
product_data = {'product_id': [1, 2, 3],
                'product_name': ['A', 'B', 'C']}
product_df = pl.DataFrame(product_data)

# 売り上げデータのデータ作成
sales_data = {'product_id': [1, 2, 2, 3, 3],
              'sales': [100, 200, 150, 300, 250]}
sales_df = pl.DataFrame(sales_data)

# cross joinによるデータの結合
result = product_df.join(sales_df, how="cross")

print(result)

出力結果:

shape: (15, 4)
 product_id  product_name  product_id_right  sales 
 ---         ---           ---               ---   
 i64         str           i64               i64   
╞════════════╪══════════════╪══════════════════╪═══════╡
 1           A             1                 100   
 1           A             2                 200   
 1           A             2                 150   
 1           A             3                 300   
 1           A             3                 250   
 2           B             1                 100   
 2           B             2                 200   
 2           B             2                 150   
 2           B             3                 300   
 2           B             3                 250   
 3           C             1                 100   
 3           C             2                 200   
 3           C             2                 150   
 3           C             3                 300   
 3           C             3                 250   

cross_joinメソッドによって、商品情報と売り上げデータの全てのレコードが結合されました。

特定カテゴリ内での集計操作

特定のカテゴリでデータをグループ化して集計するには groupby を用います。

polars.DataFrame.groupby

以下は、商品情報と売り上げデータを作成し、商品IDごとに売り上げを集計する例です。

# 商品情報のデータ作成
product_data = {'product_id': [1, 2, 3, 4, 5],
                'product_name': ['A', 'B', 'C', 'D', 'E']}
product_df = pl.DataFrame(product_data)

# 売り上げデータのデータ作成
sales_data = {'product_id': [1, 2, 2, 3, 3, 3, 4, 5],
              'sales': [100, 200, 150, 300, 250, 200, 50, 100]}
sales_df = pl.DataFrame(sales_data)

# 商品IDごとの売り上げを集計する
result = sales_df.groupby('product_id').agg(pl.sum("sales"))

print(result)

出力結果

shape: (5, 2)
 product_id  sales 
 ---         ---   
 i64         i64   
╞════════════╪═══════╡
 3           750   
 1           100   
 4           50    
 2           350   
 5           100   

上記は「product_idごとに、salesの値の和を取る」という操作を実現しています。
pl.sum("sales")pl.col("sales").sum() と書いても同様です。

sum() の部分を他の計算に変えることで様々な計算を実現できます。
よく使うのは以下でしょうか。

  • mean() : 平均を計算
  • var() : 分散を計算
  • std() : 標準偏差を計算
  • max(), min(), quantile(0.25) : 最大値、最小値、第一分位点を計算

agg(...) にリストとして複数の集計操作を渡すことで複数の計算を行うこともできます。

まとめ

よく使うテーブルデータ操作をPolarsで行う例を紹介しました。
少しでも皆様のお役に立てば幸いです。