経緯
このwarningはたまにしか見かけないけれども、発生すると毎回解決に時間がかかってしまっている。
もっと理解を深め(なるべく即座に解決す)るため、事例を記録してみた。
概要
タイトルの通り。
FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
が、
- どんな時に発生したか
- どうやって解決したか
を記録したもの。
※ただし、pandasがメインターゲットです。
numpyで発生している場合の対策は特に記載しません。
(numpyはそんなに使わないので...。numpyで同じ問題にぶつかったら追記するかもしれません。)
Environment
version | |
---|---|
Python | 3.8.1 |
事例
1. 'string' in np.array
ソースコードはstackoverflowより拝借いたしました...
import numpy as np
'x' in np.arange(5)
# 結果
<stdin>:1: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
2. np.array同士の比較だが、一方が数値型でもう一方が文字列型
ソースコードはstackoverflowより拝借いたしました...
import numpy as np
np.arange(5) == np.arange(5).astype(str)
# 結果
<stdin>:1: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
3. series == 'string'
私が実際に見かけた事例です。
import numpy as np
import pandas as pd
df = pd.DataFrame({
'number': [1, np.nan, 3, np.nan, np.nan, 11],
'memo': ['sign', '', np.nan, 'sign2', np.nan, 'sign3']
})
df
number memo
0 1.0 sign
1 NaN
2 3.0 NaN
3 NaN sign2
4 NaN NaN
5 11.0 sign3
df['number'] == 'sign'
# 結果
/usr/local/pyenv/versions/20200222/lib/python3.8/site-packages/pandas/core/ops/array_ops.py:253: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
ただし、いろいろ試してみてわかったが、「3.」の事例をほんのちょっと変更するだけで、warningは発生しなくなった。
変更箇所はどこかというと、問題になっているseriesの中に、比較対象として合致する'sign'
を入れておくのである。
import pandas as pd
import numpy as np
df = pd.DataFrame({
'number': [1, 'sign', 3, np.nan, np.nan, 11], # <= 2個目の値を 'sign' にした
'memo': ['sign', '', np.nan, 'sign2', np.nan, 'sign3']
})
df['number'] == 'sign'
# 結果
0 False
1 True
2 False
3 False
4 False
5 False
考察
上記1, 2, 3の結果と、stackoverflowのこの記事を見る限り、「numpy配列やseries」と「文字列」とを比較することで発生することは間違いなさそう。
ただ、上記「3.」の二つ目のソースコードを見るとわかるのだが、検出したい値が実際にseriesの中に入っていれば、このwarningは発生しない。
この事実には、今回の記事を書いて初めて気が付いた。
対策については、やはりstackoverflowのこの記事頼りになってしまうが、以下のようになる。
対策
seriesと文字列の比較を行う際に、==
やin
ではなく、.isin(['string'])
メソッドや.contains('string')
メソッドを用いることで回避できる。
# Bad: warningが発生してしまう、ダメな書き方
df['number'] == 'sign'
# Good: warningが発生しない
df['number'].isin(['sign'])
# Error: contains を使おうとしたら、series(df['number'])内のデータが文字列型ではないのでエラーになった
df['number'].str.contains('sign')
--- error (略) ---
AttributeError: Can only use .str accessor with string values!
# Good: warningが発生しない
df.astype({'number': str})['number'].str.contains('sign')
考察の続き
『基本的にはこのwarningは普段出ないのだけど、ときどき出る』、ということが私には何度かありました。
調べてみると、その原因は『データの中に、期待している文字列が1つも含まれていないこと』であることがわかりました。
実際に大量のデータを扱う際、期待していた文字列が1つも含まれていないということは通常あり得るので、そんなときにこのwarningが発生してしまうのだと思います。
これで次からもっとスムーズにwarningが消せる気がする。
というより、この書き方さえ避けて.isin()
を使えば、そもそも発生させないことができそう(^ワ^*)