numpyのちょっとしたテクニックの話です。僕は最近まで知らなかったのですが、numpyには np.nansum など nan + 集計関数名 という命名規則のメソッド群が用意されています。これの紹介をします。
前提として、 numpy配列の値を合計したり平均を取ったりする時、データ中にnanがあると結果もnanになります。pandasのSeriesの場合と挙動が違うのですね。例えば以下のような感じです。(Seriesと挙動違うよという話は以前どこかの記事で書いた覚えがあります)
import numpy as np
import pandas as pd
# nanを含むデータを作る
ary = np.array([1, 1, 2, 3, np.nan, 8])
print(ary)
# [ 1. 1. 2. 3. nan 8.]
# 合計するとnanになる
print(ary.sum())
# nan
# 平均も同様
print(ary.mean())
# nan
# Series はnanを無視してくれる
print(pd.Series(ary).sum())
# 15.0
print(pd.Series(ary).mean())
# 3.0
欠損値の存在に気づくきっかけになったりしてありがたいこともありますし、仕様としてどうあるべきかを考えたらnullの伝播が実装されているこの作りが正しいと思えるのですが、この挙動が不便なことが多いのも事実です。
僕はこういう時大体Seriesに変換してしまって集計していました。
ただ、実は numpyにもNanに対応したメソッドがちゃんとあり、それが冒頭に書いたnansumです。maxにはnanmax, stdにはnanstd のように多くのメソッドに対して実装されています。
dir()で探すと一覧額作成できます。
for m in dir(np):
if m.startswith("nan"): # メソッド名がnanで始まるか
if m.replace("nan", "") in dir(np): # nanの部分を除外した場合に同じ名前のメソッドがあるか
print(m)
"""
nanargmax
nanargmin
nancumprod
nancumsum
nanmax
nanmean
nanmedian
nanmin
nanpercentile
nanprod
nanquantile
nanstd
nansum
nanvar
"""
これらを使うと、エラーが起きずにnanを無視して無視して残りの要素について集計してくれます。
print(np.nansum(ary))
# 15.0
print(np.nanmean(ary))
# 3
1次元配列の場合は内包表記での対応とか色々やり方もあるのですが多次元になってくると面倒だし集計のために補完するのも面倒なのでありがたいですね。使い方がnp.nansum(ary)であって、ary.nansum() では無いので注意してください。
もう一点、 np.nan ではなく、Noneを含めてるとこれは数値の欠損値では無いので相わらずエラーになります。ここも注意です。
ary2 = np.array([1, 1, 2, 3, None, 8])
try:
np.nansum(ary2)
except Exception as e:
print(e)
# unsupported operand type(s) for +: 'int' and 'NoneType'