7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Polars 0.20 恒例の破壊的変更の一足先のご紹介

Posted at

はじめに

全国のPolarsファンのみなさまこんにちは。カジュアルに破壊的変更を行うPolarsのversion 0.20 のリリースが近づいてきました。そこで、今回の破壊的変更を一足先にご紹介いたします。ちなみに map_dict()replace() に rename されていたのは最近知りました

Disclamers

この内容は本家release noteの pr#12872 について、重要度が大きいと思われる変更点だけを2023年12月16日時点で日本語で紹介しているだけです。おそらく未来の時点で読んでも(また破壊的変更が加わっていて)参考にならない可能性があります。翻訳上のミスや解釈の誤りなどありましたら、コメント欄でご指摘いただけますと助かります。

version 0.20 破壊的変更内容抜粋

join 時の null の扱い

  • 0.19まで: join のkeyにnullが存在する場合、null自体もjoinのkeyの対象としていた
  • 0.20から: null はデフォルトでは join key の対象としない。 join_nulls=True で0.19までの挙動と同じになる。

一般のSQLエンジンの挙動に近くなると思う。なお実行速度アップにも貢献するらしい。

Example

Before:

>>> df1 = pl.DataFrame({"a": [1, 2, None], "b": [4, 4, 4]})
>>> df2 = pl.DataFrame({"a": [None, 2, 3], "c": [5, 5, 5]})
>>> df1.join(df2, on="a", how="inner")
shape: (2, 3)
┌──────┬─────┬─────┐
│ a    ┆ b   ┆ c   │
│ ---  ┆ --- ┆ --- │
│ i64  ┆ i64 ┆ i64 │
╞══════╪═════╪═════╡
│ null ┆ 4   ┆ 5   │
│ 2    ┆ 4   ┆ 5   │
└──────┴─────┴─────┘

After:

>>> df1.join(df2, on="a", how="inner")
shape: (1, 3)
┌─────┬─────┬─────┐
│ a   ┆ b   ┆ c   │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 │
╞═════╪═════╪═════╡
│ 2   ┆ 4   ┆ 5   │
└─────┴─────┴─────┘
>>> df1.join(df2, on="a", how="inner", join_nulls=True)  # Keeps previous behavior
shape: (2, 3)
┌──────┬─────┬─────┐
│ a    ┆ b   ┆ c   │
│ ---  ┆ --- ┆ --- │
│ i64  ┆ i64 ┆ i64 │
╞══════╪═════╪═════╡
│ null ┆ 4   ┆ 5   │
│ 2    ┆ 4   ┆ 5   │
└──────┴─────┴─────┘

個人的感想

既存のSQLエンジンの挙動と整合的になるので好ましい変更。

outer join での 左右のkeyの列をキープするようにする

言葉で説明しづらいので、次のexampleを見てください。

Example

Before:

>>> df1 = pl.DataFrame({"L1": ["a", "b", "c"], "L2": [1, 2, 3]})
>>> df2 = pl.DataFrame({"L1": ["a", "c", "d"], "R2": [7, 8, 9]})
>>> df1.join(df2, on="L1", how="outer")
shape: (4, 3)
┌─────┬──────┬──────┐
│ L1  ┆ L2   ┆ R2   │
│ --- ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  │
╞═════╪══════╪══════╡
│ a   ┆ 1    ┆ 7    │
│ c   ┆ 3    ┆ 8    │
│ d   ┆ null ┆ 9    │
│ b   ┆ 2    ┆ null │
└─────┴──────┴──────┘

After:

>>> df1.join(df2, on="L1", how="outer")
shape: (4, 4)
┌──────┬──────┬──────────┬──────┐
│ L1   ┆ L2   ┆ L1_right ┆ R2   │
│ ---  ┆ ---  ┆ ---      ┆ ---  │
│ str  ┆ i64  ┆ str      ┆ i64  │
╞══════╪══════╪══════════╪══════╡
│ a    ┆ 1    ┆ a        ┆ 7    │
│ b    ┆ 2    ┆ null     ┆ null │
│ c    ┆ 3    ┆ c        ┆ 8    │
│ null ┆ null ┆ d        ┆ 9    │
└──────┴──────┴──────────┴──────┘
>>> df1.join(df2, on="a", how="outer_coalesce")  # Keeps previous behavior
shape: (4, 3)
┌─────┬──────┬──────┐
│ L1  ┆ L2   ┆ R2   │
│ --- ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  │
╞═════╪══════╪══════╡
│ a   ┆ 1    ┆ 7    │
│ c   ┆ 3    ┆ 8    │
│ d   ┆ null ┆ 9    │
│ b   ┆ 2    ┆ null │
└─────┴──────┴──────┘

個人的感想

pandasの merge(..., how="outer") 的な結果が変更となるので要注意。
SQLエンジンでの挙動としてはPostgreSQL系の扱いと同じになる。というかSQLエンジンによって full outer join がkeyを潰す(1列)のか残す(2列)か挙動が違うことを初めて知った。列数が変わるので既存コードが動かなくなるレベルの破壊的変更。ぞくぞくしますね。

count にて null values を無視するようになる。

Expr or Series のメソッドの挙動の変更です。

after: count() -> null含まない個数。 len() null含む個数。

Example

Before:

>>> df = pl.DataFrame({"a": [1, 2, None]})
>>> df.select(pl.col("a").count())
shape: (1, 1)
┌─────┐
│ a   │
│ --- │
│ u32 │
╞═════╡
│ 3   │
└─────┘

After:

>>> df.select(pl.col("a").count())
shape: (1, 1)
┌─────┐
│ a   │
│ --- │
│ u32 │
╞═════╡
│ 2   │
└─────┘
>>> df.select(pl.col("a").len())  # Mirrors previous behavior
shape: (1, 1)
┌─────┐
│ a   │
│ --- │
│ u32 │
╞═════╡
│ 3   │
└─────┘

個人的感想

挙動としてはflavorな変更。既存コードで値が結果が変わった場合はこれを疑うべき。

NaN values が equal あつかいされる

Floating NaN は、一般的(IEEE754的)には NaN 同士の比較演算子を適用した結果は全て False となる。その挙動に従わなり equal での比較が True となる。

Example

Before:

>>> s = pl.Series([1.0, float("nan"), float("inf")])
>>> s == s
shape: (3,)
Series: '' [bool]
[
        true
        false
        true
]

After:

>>> s == s
shape: (3,)
Series: '' [bool]
[
        true
        true
        true
]

個人的感想。

IEEE 754のルールを逸脱する危険な変更。個人的に全く賛同できない。

その他変更

  • replace メソッドの修正(マッチしない場合はnullとなるなど)
  • datetime系の要素のIntが効率化(例: dt.month や dt.hour が u32型 から i8型へ)(なぜ u8 でないのであろう?)
  • value_counts() のカラム名が counts から count に変更となる
  • cumk系関数がcum* から cum_* に renameされる

以上です。破壊的変更を楽しむくらいPolars愛に溢れる皆様はゾクゾクできましたでしょうか。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?