pythonで利用するpolarsで何が良いの? 既にpandasがあるのに?

推薦理由が、紹介サイトの最後にありました。

「まとめ」にも書いているとおり、Polarsは慣れると可読性がとても高いと感じています。しかも処理速度が早いので、多少雑に書いても速度が全く気になりません。

そのため、僕個人の話ですが、今までPandasを使用していたところをPolarsに変えたら、生産性がだいぶ上がりました。ほんとすごい。

イメージのために、よくありそうな簡単な利用例のご紹介
※理解しやすいように一部変えているところがあります。ミスも修正。

 

本当に最低限だけ覚えて一瞬で使い始めるPolars入門

https://qiita.com/yknoguchi/items/be49932d7f9061333954

Polarsの基本的な使用フォーマット

Polarsでは基本的にselectとfilterメソッドを使用していきます。
そしてそれぞれが、列のデータ選択(+処理)、行のデータ選択を担っています。

また、それぞれのメソッドの中で操作を行いたいときは、pl.Exprクラスを使用します。
特にpl.colは列の操作を行うときにたくさん使用します。

特にselectでは、特定の列に対して処理を加えて新しい列として追加することができます。なので、基本的な処理手順は以下のようになります。

import polars as pl       #polars 利用するぞ pl とする
df = pl.read_csv(“data.csv”) #data.csv読み込みdfとする(DataFile)
print(df)

読み込んだCSVファイルの内容をpolarsでprintしたもの
shape: (3, 2)   #データが3行2列。先頭3行は列名と列の定義部
┌──────┬───────┐
│ name ┆ age │
│ — ┆ — │
│ str ┆ i64 │
╞══════╪═══╡
│ one ┆ 10 │
│ one ┆ 20 │
│ two ┆ 30 │
└──────┴───────┘

列名のリストは
print(df.columns)
#[‘name’,  ‘age’]   #列名コピペでき便利

プログラムの流れ

df.select(
  # ここで使用する列の選択
  # または特定の列に対して処理を行う
  # 場合に応じては処理後、別のカラムとしてデータを追加
).filter(
  # ここでデータの中から使用する行を抽出
).select(
).filter… # これを繰り返す

列の選択・操作 select

selectで特定の列を取り出すことができます。具体的な使い方としては以下をご覧ください。

df.select(
  ”name”,
  ”age”,
)

# 出力
# shape: (3, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ — ┆ — │
# │ str ┆ i64 │
# ╞══════╪═════╡
# │ one ┆ 10 │
# │ one ┆ 20 │
# │ two ┆ 30 │
# └──────┴─────┘

上のサンプルでは”name”と”age”という名前の列を取り出しています。直感的で簡単ですね。
しかも、Polarsでは取り出す列の名前を配列にする必要がありません。(配列にする必要がないだけで、配列で指定しても同様の結果が返ってきます。)

# 配列で列名を指定してもOK
df.select(
  [“name”, “age”] )

# 出力
# shape: (3, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ —  ┆ — │
# │ str  ┆ i64 │
# ╞══════╪═════╡
# │ one  ┆ 10  │
# │ one  ┆ 20  │
# │ two  ┆ 30  │
# └──────┴─────┘

列の操作 pl.col()
selectで列を指定することができたら、次は列の操作をしたくなります。その時はselectの中にpl.col()を使用しましょう。
 
df.select(
  ”name”,
  pl.col(“age”)*2
)
 
# 出力
# shape: (3, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ —  ┆ — │
# │ str  ┆ i64 │
# ╞══════╪═════╡
# │ one  ┆ 20  │
# │ one  ┆ 40  │
# │ two  ┆ 60  │
# └──────┴─────┘
こうすることで、ageの列が2倍になって結果が返されます。
もしもこの処理結果を別のカラムにしたいときは、.aliasを使用しましょう。
 
df.select(
  ”name”,
  ”age”,
  (pl.col(“age”)*2).alias(“*2”)
  # ↑コードが `*2` で終わってるとメソッドチェーンができないので括弧で囲っている
)
 
# 出力
# shape: (3, 3)
# ┌──────┬─────┬─────┐
# │ name ┆ age ┆ *2  │
# │ —  ┆ — ┆ — │
# │ str  ┆ i64 ┆ i64 │
# ╞══════╪═════╪═════╡
# │ one  ┆ 10  ┆ 20  │
# │ one  ┆ 20  ┆ 40  │
# │ two  ┆ 30  ┆ 60  │
# └──────┴─────┴─────┘
 
場合わけを使用した列の操作 pl.when
少し細かい処理を書こうとしたら、場合分けが必要になります。その時はpl.whenを使用しましょう。
pl.whenはwhen、then、otherwiseを組み合わせて使用します。
 
df.select(
  ”name”,
  ”age”,
  pl.when(pl.col(“age”)>18)
    .then(“成人”)
    .otherwise(“未成年”)
    .alias(“成人済みか?”) # whenにもaliasが使用可能
)
 
# 出力
# shape: (3, 3)
# ┌──────┬─────┬──────────────┐
# │ name ┆ age ┆ 成人済みか?  │
# │ —  ┆ — ┆ —      │
# │ str  ┆ i64 ┆ bool     │
# ╞══════╪═════╪══════════════╡
# │ one  ┆ 10  ┆ false    │
# │ one  ┆ 20  ┆ true     │
# │ two  ┆ 30  ┆ true     │
# └──────┴─────┴──────────────┘
 
行の選択 filter
filterで行の絞り込みが行えます。基本的には行ごとに絞り込みを行うので、
上にも登場したpl.colを使います。
条件指定 大   >    >=    ==   =<    <    小
     不一致              != 

df.filter(
  pl.col(“age”) > 10
)
 
# 出力
# shape: (2, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ —  ┆ — │
# │ str  ┆ i64 │
# ╞══════╪═════╡
# │ one  ┆ 20  │
# │ two  ┆ 30  │
# └──────┴─────┘
 
複数条件も指定できます。
 
df.filter(
  (pl.col(“age”) > 10)
  & (pl.col(“age”) < 30)
  # pythonでは&や|の演算優先順位が高いため、括弧で囲う
)
 
# 出力
# shape: (2, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ —  ┆ — │
# │ str  ┆ i64 │
# ╞══════╪═════╡
# │ one  ┆ 20  │
# └──────┴─────┘
もちろん、違う列で条件指定したり、selectで追加した新しい列に対しても操作可能です。
 
まとめ
さて、これまでselectとfilterを使用したPolarsの使用方法を紹介しました。
 
では、それを踏まえて以下のコードを読んでみましょう。(※参考のためのコードなので、冗長な部分があります。)
 
df.select(
  ”name”,
  ”age”,
  pl.when(pl.col(“age”)>18)
    .then(True)
    .otherwise(False)
    .alias(“成人済みか?”)
).filter(
  pl.col(“成人済みか?”)
).select(
  ”name”,
  ”age”
)
 
はい。これを上から読んでいくと、このように解釈できます。
 
使用する列を選択する
nameとageをそのまま使用
ageが18以上かをboolで表す成人済みか?の列を追加
使用する行を選択する
成人済みか?の列がTrueであるもののみを表示
使用する列を選択する
nameとageをそのまま使用(成人済みか?の列は表示しない)
 
よってプログラムcodeは以下のようになります。
 
df.select(
  ”name”,
  ”age”,
  pl.when(pl.col(“age”)>18)
    .then(True)
    .otherwise(False)
    .alias(“成人済みか?”)
).filter(
  pl.col(“成人済みか?”)
).select(
  ”name”,
  ”age”
)
 
# 出力
# shape: (2, 2)
# ┌──────┬─────┐
# │ name ┆ age │
# │ —  ┆ — │
# │ str  ┆ i64 │
# ╞══════╪═════╡
# │ one  ┆ 20  │
# │ two  ┆ 30  │
# └──────┴─────┘
 
と、このようにselectとfilterを交互に使用していくだけで、簡単に、わかりやすくデータフレームの操作ができました。
 
個人的に、自分の頭の中で思ったことをそのままその順番で書き連ねられるところが、Polarsの良さだなぁと思っています。
 
覚えていたらより便利なメソッドたち
列をすべて選択 pl.all()
 selectするときに、すべての列を選択できます。
 
df.select(
  pl.all()
)
 
指定した列以外を選択 pl.exclude()
 selectするときに、指定した列以外を選択します。
 指定したい列が多いときに便利。複数指定も可能。
 
df.select(
  pl.exclude(“name”)
)
 
値がnullかを判別 .is_null()
 Pandasでいうisna()のようなもの。
 欠損値がある行を使用しないときなどに使う。
 
df.select(
  pl.col(“name”).is_null() # nameが欠損している行のみ表示
)
 
値がnullではないことを判別 .is_not_null()
 .is_null()の逆。
 データが入っていたらTrue。
 
df.select(
  pl.col(“name”).is_not_null() # nameのデータが入っている行のみ表示
)
 
終わりに
ここまでで、Polarsの基本の使い方を紹介してきました。
 
「まとめ」にも書いているとおり、Polarsは慣れると可読性がとても高いと感じています。しかも処理速度が早いので、多少雑に書いても速度が全く気になりません。
 
そのため、僕個人の話ですが、今までPandasを使用していたところをPolarsに変えたら、生産性がだいぶ上がりました。ほんとすごい。