Pythonの表形式データ処理の高速なライブラリ「Polars」でよく用いられる機能を、間違いやすい点にも触れつつ紹介します。表題の通り、Pandasの知識がなくても大丈夫です。
目次
- はじめに
- Polarsを書く上での注意点
- Polarsで能く用いられる機能
- まとめ
はじめに
Pythonで表形式データを処理する際に、真っ先に候補に挙がるライブラリはPandasでしょう。
Pandasでは、DataFrame(データフレーム)と呼ばれる表形式のオブジェクトを用いて、列名を指定したり、条件に従って行を絞ったり、各行に一律な操作を実行したりすることができます。
ところで、インターネットの波に乗っていると、Pandasに関連して、Polarsと呼ばれるライブラリを目にしたことがあるかもしれません。
Polarsをざっくり説明するなら「Pandasより高速な表形式データ処理ライブラリ」です。
どれくらい高速かの解説はネットの海の他サイトに譲ります。
というわけで、Pythonで表形式データを処理したい際は、Polarsを使ってみましょう。以下では、Polarsで頻繁に用いられる基本的な機能を紹介します。
より高度な処理は別の機会に記事にします。
インポート
Polarsをインポートするには、まずPolarsをpipでインストールする必要があります。(Anacondaがあればcondaでも可)
なお、Polarsはplと略されることが多いです。以下でもplという略称を使用します。
!pip install polars
import polars as pl
Polarsを書く上での注意点
PolarsのDataFrameの特徴
PolarsのDataFrameは列名と各列の要素からなります。
- 列名の特徴
- 空白のDataFrameを除いて、各列には必ず列名が存在し、かつ、それらが一意(ユニーク)な列名でなければなりません。もし重複した列名を設定しようとすると、エラーが出るか、既存のDataFrameに影響を与えます。
- 各列の要素
- 各列の要素は全ての行で同じ型でなければなりません。なお、各行の要素は列ごとに異なっていても構いません。Polarsでは列の全ての行に一律にする処理がほとんどであるため、手持ちのデータのどちらを列や行とするかは事前に決めておきましょう。
- その他
- 行名や行番号はありません。ただし、これらを疑似的に付与したり利用したりすることは可能です。
Polarsで用いられるExpression(エクスプレッション)について
Polarsには、DataFrame(データフレーム、1列または複数列のオブジェクト)やSeries(シリーズ、1列だけのオブジェクト)以外に、Expression(エクスプレッション)と呼ばれる重要な独特の概念があります。
Expressionが何かを直感的に説明するのは難しいですが、条件式や計算式のようなものであると考えてください。
ここでExpressionの例を紹介します。DataFrameにおいて、"ID"と"Name"という名前の列の要素を指定するExpressionは以下のように書くことが多いです。
pl.col("ID", "Name")
# または
pl.col(["ID", "Name"])
# または
[pl.col("ID"), pl.col("Name")]
Expressionは変数として保持しておくことができます。
expr1 = pl.col("ID", "Name")
# または
expr2 = pl.col(["ID", "Name"])
# または
expr3 = [pl.col("ID"), pl.col("Name")]
これらのExpressionを引数として、dfというDataFrameの"ID"と"Name"という名前の列を選択するには以下のように書けます。
df.select(expr1)
メソッドの種類の注意点
Polarsで用いるメソッド(処理)の書き方は以下の4種類があります。
以下の例のように、"mean"は4種全てがありますが、他のメソッドはそうでないものがほとんどですので、これらを混同しないように心がけましょう。
また、返り値の違いも意識しましょう。
- polarsのメソッド。
- polars.Expressionのメソッド。
- polars.DataFrameのメソッド。
- polars.Seriesのメソッド。
# その1 polarsのメソッド。例:
mean_expr = pl.mean("Age") # -> Expr
# その2 polars.Expressionのメソッド。例:
self_mean_expr = pl.col("Age").mean() # -> self (Expr)
# その3 polars.DataFrameのメソッド。例:
df = pl.DataFrame()
mean_df = df.mean() # -> DataFrame
# その4 polars.Seriesのメソッド。例:
sr = pl.Series()
mean_fl = sr.mean() # -> Float
Polarsで能く用いられる機能
DataFrameの生成
DataFrameの生成は大きく分けて「読み込み」か「変換」の2種類あります。
読み込み
CSVファイル、JSONファイル、Excelファイルなどを読み込んで生成することができます。
input_csv = "input.csv"
df_csv = pl.read_csv(input_csv)
input_json = "input.json"
df_json = pl.read_json(input_json)
input_excel = "input.xlsx"
df_excel = pl.read_excel(input_excel)
変換
辞書型変数、Pandas.DataFrame、Numpy.ndarrayなどから変換して生成することができます。ただし、以下の例のように、それぞれの挙動が微妙に異なる点に注意しましょう。
辞書型変数から変換
import polars as pl
data = {
"店舗名": ["東京タワー店", "浅草寺店", "秋葉原店", "東京タワー店", "浅草寺店"],
"曜日": ["土曜日", "土曜日", "日曜日", "日曜日", "土曜日"],
"売上額": [500000, 300000, 450000, 600000, 350000],
"客数": [200, 150, 180, 250, 160]
}
df_dict = pl.DataFrame(data)
# または
df_dict = pl.from_dict(data)
display(df_dict)
Pandas.DataFrameから変換
Polarsにはindexが無いので、Pandasのindexはデフォルトでは引き継がれません。include_index = Trueを指定すると引き継がれます。
import pandas as pd
data = pd.DataFrame(
{
"店舗名": ["東京タワー店", "浅草寺店", "秋葉原店", "東京タワー店", "浅草寺店"],
"曜日": ["土曜日", "土曜日", "日曜日", "日曜日", "土曜日"],
"売上額": [500000, 300000, 450000, 600000, 350000],
"客数": [200, 150, 180, 250, 160]
}
)
df_pandas = pl.DataFrame(data)
# または
df_pandas = pl.from_pandas(data)
display(df_pandas)
Numpy.ndarrayから変換
ndarrayではすべての要素が同じ型になるので、Polarsで読み込んだ際にもすべての列が同じ型になります。
なお、numpyからの変換に限らず、DataFrame内のデータ型はPolars独自のものが使われます(例 : pl.Float64, pl.Int64, pl.Datetime, pl.String)が、printやdisplay(Jupyterのみ)ではstrなどと表示されることがあります。
型はschema_overridesという引数に辞書型で渡すことで変更できます。(例 : "schema_overrides={"客数":pl.Int64}")
import numpy as np
data = np.array(
[
["東京タワー店", "浅草寺店", "秋葉原店", "東京タワー店", "浅草寺店"],
["土曜日", "土曜日", "日曜日", "日曜日", "土曜日"],
[500000, 300000, 450000, 600000, 350000],
[200, 150, 180, 250, 160]
]
)
schema = ["店舗名", "曜日", "売上額", "客数"]
df_numpy = pl.DataFrame(data, schema, schema_overrides={"客数":pl.Int64})
# または
df_numpy = pl.from_numpy(data, schema, schema_overrides={"客数":pl.Int64})
display(df_numpy)
DataFrameの出力
DataFrameはCSV, JSON, Excelなどに出力できます。
output_csv = "output.csv"
df.write_csv(output_csv)
output_json = "output.json"
df.write_json(output_json)
output_excel = "output.xlsx"
df.write_excel(output_excel)
DataFrameの基本情報
行数と列数
shapeというDataFrameのメソッドで行数と列数をタプルで取得できます。
行数と列数をバラでほしい場合は、heightやwidthというメソッドを使用できます。
lenでは行数を取得できます。
data = {
"店舗名": ["東京タワー店", "浅草寺店", "秋葉原店", "東京タワー店", "浅草寺店"],
"曜日": ["土曜日", "土曜日", "日曜日", "日曜日", "土曜日"],
"売上額": [500000, 300000, 450000, 600000, 350000],
"客数": [200, 150, 180, 250, 160]
}
df_example = pl.DataFrame(data) # 以下、df_sampleを各機能の例に用います
print(df_example.shape)
print(len(df_example))
print(df_example.height)
print(df_example.width)
(5, 4)
5
5
4
列名とデータ型
schemaというDataFrameのメソッドで列名とデータ型を辞書型で取得できます。
列名とデータ型をバラでほしい場合は、columnsとdtypesというメソッドを使用できます。
print(df_example.schema)
print(df_example.columns)
print(df_example.dtypes)
OrderedDict({'店舗名': String, '曜日': String, '売上額': Int64, '客数': Int64})
['店舗名', '曜日', '売上額', '客数']
[String, String, Int64, Int64]
列の抽出
DataFrameとして抽出
selectメソッド
列を指定して抽出し、列数を減らします。selectというメソッドを用いますが、感覚的には「選択」というよりも「DataFrameを新たに生成」と考えるほうがしっくりきます。
select_columns = ["店舗名", "曜日", "客数"]
df_selected_1 = df_example.select(
select_columns,
)
display(df_selected_1)
shape: (5, 3)
店舗名 | 曜日 | 客数 |
---|---|---|
str | str | i64 |
"東京タワー店" | "土曜日" | 200 |
"浅草寺店" | "土曜日" | 150 |
"秋葉原店" | "日曜日" | 180 |
"東京タワー店" | "日曜日" | 250 |
"浅草寺店" | "土曜日" | 160 |
selectメソッドの注意点
ただし、selectには、文字列型、Expression型、およびそれらのリストなどを複数渡せますが、リストとそれ以外とを混ぜこぜに渡してしまうと、下の例のように、予期せぬ挙動を招く恐れがあるので注意しましょう。
なお、文字列を渡した場合はpl.colとして解釈されるようです。
column_1 = ["店舗名"]
column_2 = "曜日"
column_3 = pl.col("客数")
df_selected_badly = df_example.select(
column_1, column_2, column_3
)
display(df_selected_badly)
shape: (5, 3)
literal | 曜日 | 客数 |
---|---|---|
list[str] | str | i64 |
["店舗名"] | "土曜日" | 200 |
["店舗名"] | "土曜日" | 150 |
["店舗名"] | "日曜日" | 180 |
["店舗名"] | "日曜日" | 250 |
["店舗名"] | "土曜日" | 160 |
selectによる列の並べ替え
また、DataFrameの列の並べ替えは、selectに与える引数の順番を並び替えることで行います。
select_columns_order = ["曜日", "店舗名", "客数", "売上額"]
df_selected_2 = df_example.select(
select_columns_order,
)
display(df_selected_2)
shape: (5, 4)
曜日 | 店舗名 | 客数 | 売上額 |
---|---|---|---|
str | str | i64 | i64 |
"土曜日" | "東京タワー店" | 200 | 500000 |
"土曜日" | "浅草寺店" | 150 | 300000 |
"日曜日" | "秋葉原店" | 180 | 450000 |
"日曜日" | "東京タワー店" | 250 | 600000 |
"土曜日" | "浅草寺店" | 160 | 350000 |
excludeによる列の除外
excludeというExpressionで、指定した列以外を抽出することもできます。excludeの引数にも文字列やExpressionやリストを使用できます。
exclude_columns = ["客数"]
df_selected_3 = df_example.select(
pl.exclude(exclude_columns)
)
display(df_selected_3)
shape: (5, 3)
店舗名 | 曜日 | 売上額 |
---|---|---|
str | str | i64 |
"東京タワー店" | "土曜日" | 500000 |
"浅草寺店" | "土曜日" | 300000 |
"秋葉原店" | "日曜日" | 450000 |
"東京タワー店" | "日曜日" | 600000 |
"浅草寺店" | "土曜日" | 350000 |
aliasによる列名の変更
aliasというExpressionで列名を変更します。
なお、aliasはExpressionのメソッドとして書くことができますが、文字列のメソッドとしては定義されていないので注意しましょう。
また、aliasはExpressionのExpressionであり、"pl.alias"と書くことはできない点にも注意しましょう。
select_elements = [
"店舗名",
"曜日",
pl.col("売上額").alias("Sales")
]
df_selected_3 = df_example.select(select_elements)
display(df_selected_3)
shape: (5, 3)
店舗名 | 曜日 | Sales |
---|---|---|
str | str | i64 |
"東京タワー店" | "土曜日" | 500000 |
"浅草寺店" | "土曜日" | 300000 |
"秋葉原店" | "日曜日" | 450000 |
"東京タワー店" | "日曜日" | 600000 |
"浅草寺店" | "土曜日" | 350000 |
allによる全ての列の選択
allというExpressionで全ての列を指定できます。
しかし、元のDataFrameと全く同じものが返ってくるので、一見して意味のないExpressionだと考えられるかもしれません。
allの使いどころとしては、例えば、ExpressionのExpressionは"pl."の直後には書けませんが、"pl.all()."の直後には書くことができるので、そのようなケースに対応できます。
df_selected_4 = df_example.select(
pl.all()
)
display(df_selected_4)
shape: (5, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "土曜日" | 500000 | 200 |
"浅草寺店" | "土曜日" | 300000 | 150 |
"秋葉原店" | "日曜日" | 450000 | 180 |
"東京タワー店" | "日曜日" | 600000 | 250 |
"浅草寺店" | "土曜日" | 350000 | 160 |
Seriesとして抽出
get_columnというDataFrameのメソッドでは、指定した1列をSeriesとして作成します。
ただし、get_columnの引数は必ず列名(文字列型)でなければならず、pl.colなどのExpressionは使用できない点に注意しましょう。
また、複数列のSeriesを一つのget_columnで作成することはできません。
似たような名前のメソッドとしてget_columnsがありますが、これはDataFrameの全部の列をSeriesのリストに変換するものなので、混同しないように注意しましょう。
sr_sales = df_example.get_column("売上額")
display(sr_sales)
shape: (5,)
売上額 |
---|
i64 |
500000 |
300000 |
450000 |
600000 |
350000 |
計算と統計
演算子による計算
Pythonの四則演算子(+, -, *, /, //, %)とExpressionとを用いた計算が指定できます。計算結果もExpressionになります。
なお、下の例では、元のDataFrameには「一人当たりの売上」や「売上の剰余」なんて列は存在しません。
このことから、selectは「選択」というよりも「DataFrameを新たに生成」と考えるほうがしっくりくることでしょう。
また、これらを使用する際には、一つ一つの計算式を()で囲まないと、aliasで予期せぬ挙動になる恐れがあるので注意しましょう。
culc_columns = [
(
pl.col("売上額") / pl.col("客数")
).alias("一人当たり売上"),
(
pl.col("売上額") % pl.col("客数")
).alias("売上の剰余")
]
df_culc = df_example.select(
culc_columns
)
display(df_culc)
shape: (5, 2)
一人当たり売上 | 売上の剰余 |
---|---|
f64 | i64 |
2500.0 | 0 |
2000.0 | 0 |
2500.0 | 0 |
2400.0 | 0 |
2187.5 | 80 |
対数、角度、三角関数や移動平均などのExpressionも用意されていますが、本記事では割愛します。
列の統計
sum(総和)、mean(平均値)、max(最大値)など。
いくつかの計算は、DataFrameのメソッドやExpressionなどの複数通りの書き方ができます。
ただし、これらの引数は列名(文字列)のみを許容し、Expressionは許容しない点に注意しましょう。
なお、DataFrameでの計算ではDataFrameが返ることが多いですが、Seriesでの計算ではFloatが返ることが多いです。
df_sum_1 = df_example.select(
pl.sum("売上額", "客数")
)
# または
df_sum_2 = df_example.select(
pl.col("売上額", "客数").sum()
)
# または
df_sum_3 = df_example.select(
pl.col("売上額", "客数")
).sum()
# または
sr_sum = df_example.get_column(
"売上額"
).sum()
display(df_sum_1, df_sum_2, df_sum_3)
display(sr_sum)
shape: (1, 2)
売上額 | 客数 |
---|---|
i64 | i64 |
2200000 | 940 |
shape: (1, 2)
売上額 | 客数 |
---|---|
i64 | i64 |
2200000 | 940 |
shape: (1, 2)
売上額 | 客数 |
---|---|
i64 | i64 |
2200000 | 940 |
2200000
統計の一覧
describeというDataFrameのメソッドで、各列の平均値や標準偏差、四分位数などを一覧できます。
また、列の長さやnull(欠損値)の個数も見られます。
display(df_example.describe())
shape: (9, 5)
statistic | 店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|---|
str | str | str | f64 | f64 |
"count" | "5" | "5" | 5.0 | 5.0 |
"null_count" | "0" | "0" | 0.0 | 0.0 |
"mean" | null | null | 440000.0 | 188.0 |
"std" | null | null | 119373.363863 | 39.623226 |
"min" | "東京タワー店" | "土曜日" | 300000.0 | 150.0 |
"25%" | null | null | 350000.0 | 160.0 |
"50%" | null | null | 450000.0 | 180.0 |
"75%" | null | null | 500000.0 | 200.0 |
"max" | "秋葉原店" | "日曜日" | 600000.0 | 250.0 |
列の追加
with_columnsとlitによる定数や文字列の追加
with_columnsというDataFrameのメソッドで、新たな列を追加できます。
with_columnsの引数に、Expressionおよびそれらのリストなどを指定して、aliasで列名を命名することで、列が右端に追加されます。
with_columnsはExpressionではなくDataFrameのメソッドであるという点に注意しましょう。
反対に、aliasはExpressionであってDataFrameのメソッドではないという点にも注意しましょう。
ここでは、litというExpression(リテラルの意)で、全ての行に等しい値を設定することもできます。
df_with_columns_1 = df_example.with_columns(
pl.lit("東京").alias("都道府県")
)
display(df_with_columns_1)
shape: (5, 5)
店舗名 | 曜日 | 売上額 | 客数 | 都道府県 |
---|---|---|---|---|
str | str | i64 | i64 | str |
"東京タワー店" | "土曜日" | 500000 | 200 | "東京" |
"浅草寺店" | "土曜日" | 300000 | 150 | "東京" |
"秋葉原店" | "日曜日" | 450000 | 180 | "東京" |
"東京タワー店" | "日曜日" | 600000 | 250 | "東京" |
"浅草寺店" | "土曜日" | 350000 | 160 | "東京" |
場合分けによる動的な列の追加
when, then, otherwiseというExpressionで、If(-Then)-Else形式の行別の処理を書くことができます。
whenの引数には条件のExpressionが入り、thenとotherwiseの引数には計算のExpressionが入ります。
複数のwhenを繋げた場合、先に書かれたwhenの条件およびthenの処理が優先されます。
whenとthenは交互に書かれて同数でなければならない点と、otherwiseはExpressionの末尾に1つだけ必須である点に注意しましょう。
また、when-then-otherwiseのExpressionは長くなりがちなので、改行・インデントや括弧付けで読みやすくし、最後にaliasを忘れないように注意しましょう。
df_with_columns_2 = df_example.with_columns(
pl.when(
pl.col("売上額") == pl.max("売上額")
).then(
pl.lit("売上が最高")
).when(
pl.col("客数") == pl.max("客数")
).then(
pl.lit("客数が最高") # 実際には"売上が最高"が優先される
).when(
pl.col("売上額") / pl.col("客数") == (pl.col("売上額") / pl.col("客数")).max()
).then(
pl.lit("一人当たり売上が最高")
).otherwise(
pl.col("店舗名") + pl.col("曜日")
).alias("備考欄")
)
display(df_with_columns_2)
shape: (5, 5)
店舗名 | 曜日 | 売上額 | 客数 | 備考欄 |
---|---|---|---|---|
str | str | i64 | i64 | str |
"東京タワー店" | "土曜日" | 500000 | 200 | "一人当たり売上が最高" |
"浅草寺店" | "土曜日" | 300000 | 150 | "浅草寺店土曜日" |
"秋葉原店" | "日曜日" | 450000 | 180 | "一人当たり売上が最高" |
"東京タワー店" | "日曜日" | 600000 | 250 | "売上が最高" |
"浅草寺店" | "土曜日" | 350000 | 160 | "浅草寺店土曜日" |
列の上書き
aliasによる既存の列名の指定
with_columnsで既存の列名を指定した場合、その列を上書きできます。
更新前の列データは消えてしまう点に注意しましょう。
df_updated_1 = df_example.with_columns(
pl.lit(1000).alias("客数")
)
display(df_updated_1)
shape: (5, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i32 |
"東京タワー店" | "土曜日" | 500000 | 1000 |
"浅草寺店" | "土曜日" | 300000 | 1000 |
"秋葉原店" | "日曜日" | 450000 | 1000 |
"東京タワー店" | "日曜日" | 600000 | 1000 |
"浅草寺店" | "土曜日" | 350000 | 1000 |
aliasを使用しない場合
Expressionの計算に対してaliasをしない場合、デフォルトの列名は「最初に登場したExpression」に基づきます。
以下の例では、pl.col("売上額")が最初に登場するので、デフォルトの列名も"売上額"になります。
すなわち、既存の"売上額"列のデータが上書きされます。
df_updated_2 = df_example.with_columns(
(
pl.col("売上額") / pl.col("客数")
)
)
display(df_updated_2)
shape: (5, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | f64 | i64 |
"東京タワー店" | "土曜日" | 2500.0 | 200 |
"浅草寺店" | "土曜日" | 2000.0 | 150 |
"秋葉原店" | "日曜日" | 2500.0 | 180 |
"東京タワー店" | "日曜日" | 2400.0 | 250 |
"浅草寺店" | "土曜日" | 2187.5 | 160 |
with_columnsの注意点
with_columnsにはExpressionを複数渡すことができますが、その場合は各々が並列で処理されるため、互いを参照できないという点に注意しましょう。
df_updated_3 = df_example.with_columns(
pl.lit(1000).alias("客数"),
(pl.col("売上額") / pl.col("客数")).alias("一人当たり売上")
)
display(df_updated_3)
shape: (5, 5)
店舗名 | 曜日 | 売上額 | 客数 | 一人当たり売上 |
---|---|---|---|---|
str | str | i64 | i32 | f64 |
"東京タワー店" | "土曜日" | 500000 | 1000 | 2500.0 |
"浅草寺店" | "土曜日" | 300000 | 1000 | 2000.0 |
"秋葉原店" | "日曜日" | 450000 | 1000 | 2500.0 |
"東京タワー店" | "日曜日" | 600000 | 1000 | 2400.0 |
"浅草寺店" | "土曜日" | 350000 | 1000 | 2187.5 |
行の選択
sliceによる選択
行の選択にはsliceというDataFrameのメソッドを使います。
引数には開始行番号と行数の二つを渡します。
通常のlistをsliceする場合とは引数が異なるため注意しましょう。
df_sliced = df_example.slice(1, 3)
display(df_sliced)
shape: (3, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"浅草寺店" | "土曜日" | 300000 | 150 |
"秋葉原店" | "日曜日" | 450000 | 180 |
"東京タワー店" | "日曜日" | 600000 | 250 |
最初や最後の行を選択
head, tailというメソッドで、DataFrameの先頭または末尾の数行を選択します。
引数に行数を指定でき、デフォルトでは5行です。
df_head = df_example.head(1)
df_tail = df_example.tail(1)
display(df_head, df_tail)
shape: (1, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "土曜日" | 500000 | 200 |
shape: (1, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"浅草寺店" | "土曜日" | 350000 | 160 |
sampleによるランダムな選択
sampleというメソッドで、ランダムな数行を選択します。
最初の引数は行数で、それ以外の引数については割愛します。
デフォルトでは1行です。
df_sampled = df_example.sample()
display(df_sampled)
shape: (1, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "日曜日" | 600000 | 250 |
行のフィルタリング
filterによる条件付け
filterというDataFrameのメソッドを用いて、条件に従って行を絞り込みます。
条件として文字列やExpressionとPythonの条件演算子(==, !=, >=, >, <=, <)などを用いることができます。
filterにおける文字列はpl.lit(リテラル)として解釈されるようです。
df_filtered_1 = df_example.filter(
pl.col("店舗名") != "東京タワー店"
)
display(df_filtered_1)
shape: (3, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"浅草寺店" | "土曜日" | 300000 | 150 |
"秋葉原店" | "日曜日" | 450000 | 180 |
"浅草寺店" | "土曜日" | 350000 | 160 |
条件式の組み合わせ
&や|を使用して条件のExpressionを組み合わせることができます。ただし、NOTを表す演算子は!ではなく~です。
また、これらを使用する際には、一つ一つの条件を()で囲まないとエラーになるので注意しましょう。
df_filtered_2 = df_example.filter(
~(pl.col("店舗名") == "東京タワー店") &
(pl.col("売上額") >= pl.mean("売上額"))
)
display(df_filtered_2)
shape: (1, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"秋葉原店" | "日曜日" | 450000 | 180 |
uniqueによる要素の抽出
uniqueというDataFrameのメソッドで、指定した列のデータが重複した行は一つを残して削除します。
引数に列名を渡すと、その列の重複のみを考慮します。
複数の列名を渡す場合はリストで渡します。
引数がない場合は全ての列が重複する場合のみ削除されます。
unique_columns = ["店舗名", "曜日"]
df_unique = df_example.unique(unique_columns)
display(df_unique)
shape: (4, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "日曜日" | 600000 | 250 |
"東京タワー店" | "土曜日" | 500000 | 200 |
"秋葉原店" | "日曜日" | 450000 | 180 |
"浅草寺店" | "土曜日" | 300000 | 150 |
行の並べ替え
sortによる基本的な並べ替え
sortまたはsort_byを使用して行を並べ替えます。
sortはDataFrameのメソッドとExpressionのメソッド(Expressionを返す)とがありますが、"pl."で始まる書き方は出来ないので注意しましょう。
その一方でsort_byはExpressionのメソッドです。
DataFrameのメソッドのsortは、引数に指定された列を基準にDataFrameの全ての列を並び替えます。
これにはselect(pl.all().sort_by("列名"))が対応しています。
なぜなら、sort_byは引数に指定された列を基準に複数の列を並び替えるExpressionだからです。
なお、これらのメソッドの引数に渡せる列名は文字列かExpressionかそれらのリストで、それ以外の引数として降順(decending)やnull(欠損値)を末尾に表示するかどうか(nulls_last)があります。
さらに、引数には複数の列を指定できます。
複数列が指定された場合は、左の引数(列名)から優先的に並べ替えられます。
この際、decendingには基準にする列数と同じ長さのTrue/Falseのリストを渡すことによって、各列の降順/昇順を制御できます。
df_sorted_1 = df_example.sort("店舗名", "曜日")
# または
df_sorted_1 = df_example.select(pl.all().sort_by("店舗名", "曜日"))
display(df_sorted_1)
shape: (5, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "土曜日" | 500000 | 200 |
"東京タワー店" | "日曜日" | 600000 | 250 |
"浅草寺店" | "土曜日" | 300000 | 150 |
"浅草寺店" | "土曜日" | 350000 | 160 |
"秋葉原店" | "日曜日" | 450000 | 180 |
もう一つのsort
ではExpressionのメソッドであるsortは何をするかというと、直前のExpressionで選択された全ての列をバラバラに並べ替えます。
すなわち、行データを維持しないという点に注意しましょう。
sort_byやDataFrameのメソッドのsortとは対応していないので、混同しないように注意しましょう。
df_sorted_2 = df_example.select(pl.all().sort())
display(df_sorted_2)
shape: (5, 4)
店舗名 | 曜日 | 売上額 | 客数 |
---|---|---|---|
str | str | i64 | i64 |
"東京タワー店" | "土曜日" | 300000 | 150 |
"東京タワー店" | "土曜日" | 350000 | 160 |
"浅草寺店" | "土曜日" | 450000 | 180 |
"浅草寺店" | "日曜日" | 500000 | 200 |
"秋葉原店" | "日曜日" | 600000 | 250 |
グループ化
group_byというDataFrameのメソッドで、指定した列のデータに従って各行をグループ分けします。
なお、group_byには複数の列を指定できます。
その直後にaggというメソッドを付けることで、各グループごとに総和、平均、最大値などを取得できます。
なお、グループの順番は毎回ランダムなのですが、group_byの後は指定した列を基準にsortすることで一定の結果を見られます。
df_grouped = df_example.group_by("曜日").agg(
pl.max("客数").alias("最大客数"),
pl.mean("客数").alias("平均客数"),
pl.sum("客数").alias("合計客数"),
).sort("曜日")
display(df_grouped)
shape: (2, 4)
曜日 | 最大客数 | 平均客数 | 合計客数 |
---|---|---|---|
str | i64 | f64 | i64 |
"土曜日" | 200 | 170.0 | 510 |
"日曜日" | 250 | 215.0 | 430 |
ピボットテーブルの作成
pivotによる列の指定
pivotというDataFrameのメソッドで、ピボットテーブルを作成できます。
引数として、indexは表側(行名)とする列名、columnsは表頭(列名)とする列名、valuesは値を参照する列名があります。
なお、複数の列を表側や表頭とする場合は、列名のリストを渡す必要があります。
さらに、オプション引数として、aggregate_functionはvaluesに対する総和、平均、最大値などの計算を指定できます。
df_pivot = df_example.pivot(
values="売上額", index="店舗名", columns="曜日", aggregate_function="mean"
)
display(df_pivot)
shape: (3, 3)
店舗名 | 土曜日 | 日曜日 |
---|---|---|
str | f64 | f64 |
"東京タワー店" | 500000.0 | 600000.0 |
"浅草寺店" | 325000.0 | null |
"秋葉原店" | null | 450000.0 |
meltによるピボットテーブルの解除
pivotの逆のことをするメソッドとしてmeltがあります。
meltの引数のid_varsはキーとする列、value_varsはデータとする列(指定しない場合はid_vars以外の全ての列)で、さらにvariable_nameやvalue_nameという引数で、下の例で"variable", "value"になっている列名を命名できます。
df_melt = df_pivot.melt(id_vars="店舗名")
display(df_melt)
shape: (6, 3)
店舗名 | variable | value |
---|---|---|
str | str | f64 |
"東京タワー店" | "土曜日" | 500000.0 |
"浅草寺店" | "土曜日" | 325000.0 |
"秋葉原店" | "土曜日" | null |
"東京タワー店" | "日曜日" | 600000.0 |
"浅草寺店" | "日曜日" | null |
"秋葉原店" | "日曜日" | 450000.0 |
まとめ
Polarsの基本的な使い方を説明しました。
SQLの心得がある方には見覚えのあるメソッド名が並んでいたかもしれません。
RやPandasの心得がある方には反対に、細かな違いが目についたかもしれません。
いずれにせよ、これらの機能で簡単な処理はPolarsでできるはずなので、あとはExpressionの使い勝手を実践で身に着けていけば大丈夫です。
Polarsの機能は他にもまだまだあり、selectorによる列の選択、文字列や配列をデータとして持つ列の処理、複数のDataFrameを用いた結合や比較、そしてLazyFrameについての説明をしたかったところですが、長くなってしまったので次回に回します。
また、グラフのプロット(図示)の記事もそのうち作成します。
なにはともあれ、Polarsがさらによく知られ、Polars使いが一人でも多く増えることを願っています。
そうすれば、Polarsについて検索しているのに予測変換でPandasにすり替えられるというお節介も減ることでしょう。