環境
- Python 3.12.4
- pandas 2.2.3
- numpy 1.26.4
はじめに
pandasで予期しないZeroDivisionErrorにハマったので、ゼロ除算の挙動を整理しました。
ゼロ除算の前提
プリミティブな数値型の場合
ゼロで割るとZeroDivisionError
が発生します。
In [652]: 1 / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero
In [653]: 0 / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero
numpyの数値型の場合
ゼロで割ってもエラーは発生しませんが、RuntimeWarning
は表示されます。
In [659]: np.int64(1) / 0
<ipython-input-659-f1425159271a>:1: RuntimeWarning: divide by zero encountered in scalar divide
np.int64(1) / 0
Out[659]: inf
In [661]: np.int64(0) / 0
<ipython-input-661-a8c114fc651e>:1: RuntimeWarning: invalid value encountered in scalar divide
np.int64(0) / 0
Out[661]: nan
numpy.ndarray
も同様の挙動です。
In [654]: arr = np.array([0, 1])
In [655]: arr
Out[655]: array([0, 1])
In [656]: arr / 0
<ipython-input-656-db39a1c38901>:1: RuntimeWarning: divide by zero encountered in divide
arr / 0
<ipython-input-656-db39a1c38901>:1: RuntimeWarning: invalid value encountered in divide
arr / 0
Out[656]: array([nan, inf])
本題
pandas.Series
に対してゼロで割ってもエラーは発生しません。またwarningも表示されません。
In [595]: a = pd.Series([0, 1])
In [596]: a
Out[596]:
0 0
1 1
dtype: int64
# 要素の型は、numpyの数値型
In [597]: type(a[0])
Out[597]: numpy.int64
In [598]: a / 0
Out[598]:
0 NaN
1 inf
dtype: float64
しかし、astype("object")
でdtypeをobject
にするとZeroDivisionError
が発生します。これは、pandas.Sereis
の各要素がnumpyの数値型ではなくプリミティブな数値型であるためです。astype("object")
により、数値の型がnumpy.int64
からint
に変換されました。
In [605]: b = a.astype("object")
In [606]: b
Out[606]:
0 0
1 1
dtype: object
# 要素の型は、プリミティブな数値型
In [607]: type(b[0])
Out[607]: int
In [608]: b / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero
dtypeがobject
のSeriesに、numpyの数値型である値が格納されるケース
上の例では、dtypeがobject
のSeriesにプリミティブな数値型の値が格納されていますが、numpyの数値型である値を含める方法もあります。
pandas.Series()
にnumpyの数値型の値を格納する
In [648]: c = pd.Series([np.int64(0), np.int64(1)], dtype="object")
In [649]: c
Out[649]:
0 0
1 1
dtype: object
In [650]: type(c[0])
Out[650]: numpy.int64
In [651]: c / 0
Out[651]:
0 NaN
1 inf
dtype: object
pandas.DataFrmae.iloc
でpandas.Series
を取得する
In [677]: df = pd.DataFrame({"name":["Alice","Bob"], "count":[0,1]})
In [698]: df
Out[698]:
name count
0 Alice 0
1 Bob 1
In [699]: row = df.iloc[0]
In [700]: row
Out[700]:
name Alice
count 0
Name: 0, dtype: object
In [701]: type(row["count"])
Out[701]: numpy.int64
In [706]: row[1:] / 0
Out[706]:
count NaN
Name: 0, dtype: object
dtypeがobject
のSeriesに、プリミティブな数値型の値が格納されるケース
以下のケースでは、dtypeがobject
のSeriesに、プリミティブな数値型の値が格納されます。ゼロで割るとZeroDivisionError
が発生するので注意してください。
astype("object")
でpandas.Series
を取得する
すでに例を示したので、省略します。
pandas.DataFrmae.iterrows()
でpandas.Series
を取得する
In [708]: for i, row in df.iterrows():
...: print(row)
...: print(type(row["count"]))
...: row[1:] / 0
...:
name Alice
count 0
Name: 0, dtype: object
<class 'int'>
---------------------------------------------------------------------------
ZeroDivisionError
pandas.DataFrmae.apply
で関数を行に対して適用する
In [331]: df = pd.DataFrame({"name":["Alice","Bob"], "count":[0,1]})
In [342]: def foo(row):
...: print(f"{type(row["count"])=}")
...: return row
...:
In [343]: df.apply(foo, axis=1)
type(row["count"])=<class 'int'>
type(row["count"])=<class 'int'>
Out[343]:
name count
0 Alice 0
1 Bob 1
pandas.Series()
にnumpy.array
を渡す
In [736]: d = pd.Series(np.array([1,0]), dtype="object")
In [737]: d
Out[737]:
0 1
1 0
dtype: object
In [738]: type(d[0])
Out[738]: int
In [739]: d / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero
まとめ
-
pandas.Series
のdtypeが数値型の場合は、ゼロで割ってもZeroDivisionError
が発生しません。これは、各要素の型がnumpyの数値型であるためです。numpyの数値型の値に対しては、ゼロで割ってもZeroDivisionError
が発生しません。 -
pandas.Series
のdtypeがobject
の場合は、ゼロで割るとZeroDivisionError
が発生するときがあります。astype()
やiterrows()
でdtypeがobject
であるSeriesを取得したときは、ゼロで割るとZeroDivisionError
が発生します。各要素の値がプリミティブな数値型であるためです。
補足
numpy.array
のdtypeがobject
の場合
numpy.array
のdtypeがobject
の場合は、pandas.Series
同様ZeroDivisionError
が発生するときがあります。
numpy.ndarray.astype("object")
を実行する
In [784]: a = np.array([1,0])
In [785]: a
Out[785]: array([1, 0])
In [788]: type(a[0])
Out[788]: numpy.int64
In [786]: b = a.astype("object")
In [787]: b
Out[787]: array([1, 0], dtype=object)
In [789]: type(b[0])
Out[789]: int
In [790]: b / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero
numpy.array()
のdtype
引数を指定する
In [802]: c = np.array([1,0], dtype="object")
In [803]: c
Out[803]: array([1, 0], dtype=object)
In [804]: type(c[0])
Out[804]: int
In [805]: c / 0
---------------------------------------------------------------------------
ZeroDivisionError: division by zero