確認した環境
- Pandas 3.0.0
- Python 3.13.1
dtype の比較で起きた違和感
"bool"と"boolean"の比較
pandasのdtypeには、numpyのdtypeと、それを拡張したnullableなdtypeがあります。
たとえば、"bool"はnumpyのdtypeで、"boolean"はnullableなdtypeです。
In [35]: s_bool = pd.Series([True, False, None], dtype="bool")
In [37]: s_bool
Out[37]:
0 True
1 False
2 False
dtype: bool
In [34]: s_boolean = pd.Series([True, False, None], dtype="boolean")
In [36]: s_boolean
Out[36]:
0 True
1 False
2 <NA>
dtype: boolean
"bool"と"boolean"は異なるdtypeなので、==でdtypeを比較すると以下のような結果になります。期待通りの結果だと思います。
In [38]: s_bool.dtype == s_boolean.dtype
Out[38]: False
In [48]: s_bool.dtype == "bool"
Out[48]: True
In [49]: s_bool.dtype == "boolean"
Out[49]: False
In [50]: s_boolean.dtype == "bool"
Out[50]: False
In [51]: s_boolean.dtype == "boolean"
Out[51]: True
"str"と"string"の比較
pandas 3.0からは、新しいdtypeである"str"が利用できるようになりました。このdtypeは、欠損値をNaN(np.nan)で表します。
dtypeが"string"の場合は、欠損値をpd.NAで表します。
In [40]: s_str = pd.Series(["a", "b", None], dtype="str")
In [42]: s_str
Out[42]:
0 a
1 b
2 NaN
dtype: str
In [39]: s_string = pd.Series(["a", "b", None], dtype="string")
In [41]: s_string
Out[41]:
0 a
1 b
2 <NA>
dtype: string
"str"と"string"は異なるdtypeですが、==でdtypeを比較すると、直観と異なる挙動がありました。
この挙動は、どういうことでしょうか?
In [43]: s_str.dtype == s_string.dtype
Out[43]: False
In [55]: s_str.dtype == "str"
Out[55]: True
# なぜFalseでなくTrue?
In [56]: s_str.dtype == "string"
Out[56]: True
In [57]: s_string.dtype == "str"
Out[57]: False
In [58]: s_string.dtype == "string"
Out[58]: True
調査した結果
まず、"str"と"string"のdtypeはどちらもpandas.StringDtypeクラスのインスタンスで、na_valueプロパティが異なります。
In [60]: s_str.dtype
Out[60]: <StringDtype(na_value=nan)>
In [61]: s_string.dtype
Out[61]: <StringDtype(na_value=<NA>)>
In [64]: s_str.dtype.__class__
Out[64]: pandas.StringDtype
以下は、StringDtype.__eq__関数の中身です。
if other == "string" or other == self.name:という判定を行っているため、s_str.dtype == "string"の結果はFalseでなくTrueになることが分かりました。
なお、コメント文には # TODO should dtype == "string" work for the NaN variant?と書いてあります。
"TODO"なので、もしかしたら今後挙動が変わるかもしれません。
"str"と"string"を区別して判定する方法
以下のようにna_valueプロパティも確認すれば、"str"と"string"を区別して判定することができます。
In [68]: pd.api.types.is_string_dtype(s_str.dtype) and s_str.dtype.na_value is np.nan
Out[68]: True
まとめ
- pandas 3.0から利用可能になった
"str"dtypeと、今まで利用できた"string"dtypeは、どちらもpandas.StringDtypeクラスのインスタンスで、na_valueプロパティが異なる -
StringDtype(na_value=np.nan) == "string"の結果はTrueで、期待した結果と異なる
参考にしたサイト