環境
- pandas v1.5.1
- Python v3.10.2
概要
pandas.Sereisで、値の型が揃っていないときの動きにハマりました。
後学のため、値の型が揃っていないときの動きをまとめます。
値の型が揃っていないときの挙動
dtypeがstringのpandas.Seriesに、文字列以外を代入
dtypeがstringのpandas.Seriesに対して数値またはbool値を代入しようとすると、ValueErrorが発生します。
In [167]: s1 = pandas.Series(["a", "b"], dtype="string")
In [168]: s1
Out[168]:
0 a
1 b
dtype: string
In [169]: s1[0] = 1
---------------------------------------------------------------------------
ValueError: Cannot set non-string value '1' into a StringArray.
In [170]: s1[0] = True
---------------------------------------------------------------------------
ValueError: Cannot set non-string value 'True' into a StringArray.
数値と文字列のlistからpandas.Series(dtype="string")を生成
数値と文字列のlistからpandas.Seriesを生成する際、dtype="string"を指定すると、数値は文字列に変換されます。
In [198]: s2 = pandas.Series([1, "b"], dtype="string")
In [199]: s2
Out[199]:
0 1
1 b
dtype: string
In [200]: type(s2[0])
Out[200]: str
dtypeがint64のpandas.Seriesに、文字列を代入
dtypeがint64のpandas.Seriesに対して文字列を代入すると、dtypeはobjectに変わります。エラーは発生しません。
In [202]: s3 = pandas.Series([1, 2])
In [203]: s3
Out[203]:
0 1
1 2
dtype: int64
In [204]: s3[0] = "a"
In [205]: s3
Out[205]:
0 a
1 2
dtype: object
dtypeがInt64のpandas.Seriesに、文字列を代入
dtypeがInt64のpandas.Seriesに対して文字列、bool値、小数を代入すると、TypeErrorが発生します。
小数を代入してもエラーになるところが、nullableな型の特徴です。
In [211]: s4 = pandas.Series([1, 2], dtype="Int64")
In [212]: s4
Out[212]:
0 1
1 2
dtype: Int64
In [213]: s4[0] = "a"
---------------------------------------------------------------------------
TypeError: Invalid value 'a' for dtype Int64
In [214]: s4[0] = True
---------------------------------------------------------------------------
TypeError: Invalid value 'True' for dtype Int64
In [279]: s4[0] = 1.2
---------------------------------------------------------------------------
TypeError: Invalid value '1.2' for dtype Int64
数値と文字列のlistからpandas.Series(dtype="int64")を生成
数値と文字列のlistからpandas.Seriesを生成する際、dtype="int64"を指定すると、数値に変換できるならエラーは発生しません。ただし警告メッセージが表示されます。
In [260]: s5 = pandas.Series([1, "b"], dtype="int64")
---------------------------------------------------------------------------
ValueError: invalid literal for int() with base 10: 'b'
n [272]: s5 = pandas.Series([1, "2"], dtype="int64")
/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/numpy/core/numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
return bool(asarray(a1 == a2).all())
<ipython-input-272-ddb554c61a9c>:1: FutureWarning: Values are too large to be losslessly cast to int64. In a future version this will raise OverflowError. To retain the old behavior, use pd.Series(values).astype(int64)
s5 = pandas.Series([1, "2"], dtype="int64")
[274]: s5
Out[274]:
0 1
1 2
dtype: int64
まとめ
各挙動を確認した結果、以下であることが分かりました。
-
Int64,stringなどnullableな型に対して異なる値を代入しようとしたら、エラーが発生しました。ただし発生するエラーはTypeErrorとValueErrorで、それぞれ異なりました。 -
int64などnullableでない型に対して、異なる値を代入すると、より広い範囲を表す型に変わります。 -
pandas.Series生成時には、dtype引数で指定した型に変換を試みます。変換できない場合はエラーが発生します。
補足
pandas.Series生成時にdtype="string"を指定しない場合
pandas.Seriesに文字列のlistを渡した場合、dtypeを指定しなければdtypeはobjectになります。
objectは最も汎用的な型なので、この場合は異なる値を代入してもエラーは発生しません。
[206]: s5 = pandas.Series(["a", "b"])
In [207]: s5
Out[207]:
0 a
1 b
dtype: object
In [208]: s5[0] = 1
In [209]: s5
Out[209]:
0 1
1 b
dtype: object
In [210]: type(s5[0])
Out[210]: int