Pythonのwarningsで警告(Warning)を非表示、例外化

Modified: | Tags: Python, エラー処理

Pythonのライブラリで廃止予定の関数を使った場合などに警告(Warning)が出力されることがある。警告を非表示にしたり、例外として扱ったりするには、標準ライブラリのwarningsモジュールを使う。

warningsモジュールには警告を発するための関数warn()などもあるが、ここでは触れない。自作の関数などの中で警告を発したい場合は上記の公式ドキュメントを参照されたい。

本記事のサンプルコードでは以下のモジュールをインポートしている。pandas.DataFrameはあくまでも警告の例として使うだけなので中身は気にしなくてよい。

import warnings
import pandas as pd

df = pd.DataFrame([[0, 1, 2], [3, 4, 5]])

警告(Warning)の例

リテラルのis比較によるSyntaxWarning

文字列リテラルや数値リテラルをisで比較するとSyntaxWarningが発せられる。

print(100 is 100)
# True
# 
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_60077/3973932639.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
#   print(100 is 100)

pandasのSettingWithCopyWarning

pandasのchained assignmentに対してはSettingWithCopyWarningが発せられる。

df.iloc[:1][0] = 0
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_60077/1345802814.py:1: SettingWithCopyWarning: 
# A value is trying to be set on a copy of a slice from a DataFrame.
# Try using .loc[row_indexer,col_indexer] = value instead
# 
# See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
#   df.iloc[:1][0] = 0

警告を非表示

警告の扱いを変更するにはwarnings.simplefilter()、変更した設定をデフォルトに戻す(リセットする)にはwarnings.resetwarnings()を使う。

なお、最初からすべての警告を非表示にして無視するのはおすすめしない。あくまでも警告が発生することを認識し原因を理解した上で、例えばJupyter Notebook(IPython Notebook)の出力をスッキリさせたいというような状況でのみ非表示にするべき。

すべての警告を表示させない

warnings.simplefilter()の第一引数action'ignore'とするとすべての警告が非表示になる。

warnings.simplefilter('ignore')

print(100 is 100)
# True

df.iloc[:1][0] = 0

第一引数actionには、警告を例外として扱う'error'や警告を一度だけ発する'once'なども指定できる。'error'については後述。

非表示にするカテゴリを指定

warnings.simplefilter()の第二引数categoryに対象とする警告カテゴリを指定できる。FutureWarningDeprecationWarning, SyntaxWarning, RuntimeWarningなどがある。

第二引数categoryのデフォルトは、すべての警告カテゴリクラスの基底クラスWarning。上の例のように、デフォルトではすべての警告が対象となる。

警告カテゴリは警告メッセージに記述されている。上述のようにリテラルのisによる比較はSyntaxWarningでpandasのchained assignmentはSettingWithCopyWarning

例えばcategory=SyntaxWarningとすると、リテラルのis比較の警告は非表示になるがchained assignmentによる警告は出力される。

warnings.resetwarnings()

warnings.simplefilter('ignore', SyntaxWarning)

print(100 is 100)
# True

df.iloc[:1][0] = 0
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_60077/1345802814.py:1: SettingWithCopyWarning: 
# A value is trying to be set on a copy of a slice from a DataFrame.
# Try using .loc[row_indexer,col_indexer] = value instead
# 
# See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
#   df.iloc[:1][0] = 0

chained assignmentによる警告のカテゴリSettingWithCopyWarningはpandasで独自に定義されたものなので、そのまま指定するとエラーとなる。ライブラリ内で定義されたカテゴリクラスを読み込んで指定しなければならない。

warnings.resetwarnings()

# warnings.simplefilter('ignore', SettingWithCopyWarning)
# NameError: name 'SettingWithCopyWarning' is not defined

warnings.simplefilter('ignore', pd.errors.SettingWithCopyWarning)

print(100 is 100)
# True
# 
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# <>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_60077/3973932639.py:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
#   print(100 is 100)

df.iloc[:1][0] = 0

警告を例外として扱う

警告が発生しても処理は止まることなく実行される。厳格に、警告でも例外のように処理を止めたいという場合は、warnings.simplefilter()の第一引数action'error'とする。

warnings.resetwarnings()

warnings.simplefilter('error')

# print(100 is 100)
# SyntaxError: "is" with a literal. Did you mean "=="?

ここでは便宜上コメントアウトしているが、実際にコードを実行すると警告が発生した時点でプログラムが途中終了する。

'ignore'で非表示にする場合と同じく、第二引数categoryで対象とする警告カテゴリを指定できる。カテゴリごとに別々のアクションを指定することも可能。

warnings.resetwarnings()

warnings.simplefilter('ignore', SyntaxWarning)
warnings.simplefilter('error', pd.errors.SettingWithCopyWarning)

print(100 is 100)
# True

# df.iloc[:1][0] = 0
# SettingWithCopyWarning: ...

一時的に警告を制御

一時的に警告を制御したい場合は、withブロックとwarnings.catch_warnings()を使う。

withブロック内でのみwarnings.simplefilter()による変更が有効になる。ブロックの外ではそのまま。

warnings.resetwarnings()

with warnings.catch_warnings():
    warnings.simplefilter('ignore')
    df.iloc[:1][0] = 0

df.iloc[:1][0] = 0
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_60077/1345802814.py:1: SettingWithCopyWarning: 
# A value is trying to be set on a copy of a slice from a DataFrame.
# Try using .loc[row_indexer,col_indexer] = value instead
# 
# See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
#   df.iloc[:1][0] = 0

Python3.11から、warnings.catch_warnings()に引数actioncategoryが追加された。warnings.simplefilter()の引数action, categoryに渡されたものとして扱われる。

with warnings.catch_warnings(action='ignore', category=pd.errors.SettingWithCopyWarning):
    df.iloc[:1][0] = 0

関連カテゴリー

関連記事