A. データの品質管理(欠損値・異常値処理)

 観測データには欠損値異常値がしばしば含まれる。

 幸い、AMeDASのような観測体制がしっかりしているデータにこれらが含まれることはそう多くないが(一日平均のデータなので数点欠損があっても他で補える、という理由もある)欠損値・異常値の処理はデータ解析で労力を使うプロセスの一つである。

 ここでは、1978年のAMeDASデータ(欠損があるので...)を用いて、欠損値処理について説明する。

A-1. 欠損値の有無を調べる: isnull メソッド、any メソッド

  • isnull(): 各要素について、欠損値(NaN)であれば True を、なければ False を返す。
  • any(): 要素の中にTrueが一つでもあれば True を返す。
In [1]:
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
#from pandas.plotting import register_matplotlib_converters
#%register_matplotlib_converters()

file = "../../DB_2020/data/Kyoto/Kyoto_1978.csv"

df = pd.read_csv(file,encoding = "shift-jis",header=2,index_col=0,parse_dates=[0])
df = df.iloc[2:,:] 

temp = df['平均気温(℃)']
In [2]:
### isnullを適用すると...
print(temp.isnull().head())

temp_v = temp.values
print(type(temp_v))
年月日
1978-01-01    False
1978-01-02    False
1978-01-03    False
1978-01-04    False
1978-01-05    False
Name: 平均気温(℃), dtype: bool
<class 'numpy.ndarray'>
In [3]:
### 一つでもTrue(Nan)があるか?
print(temp.isnull().any())
True

あった!(あまり嬉しくないが)。ちなみにこの部分だけ抜き出してきて、何月何日のデータが欠損なのか調べてみる。

In [4]:
print(temp[temp.isnull()])
年月日
1978-06-16   NaN
1978-06-17   NaN
1978-06-18   NaN
1978-06-19   NaN
1978-06-20   NaN
1978-06-21   NaN
Name: 平均気温(℃), dtype: float64

 幸いなことにpandasでは描画・統計処理において欠損値を考慮した結果・図を示してくれる。

 ただし、スペクトル解析など、連続データを必要とする場合には穴埋めが必要である。 手法としては線形補間スプライン補間が用いられる。

A-2. 欠損値を補間する: interpolate メソッド

(オプションの例)

  • method: linear:線形補間(デフォルト)、index:インデックスの値を考慮した線形補間、 spline: スプライン補間(この場合は同時にorder(次数)を指定する)

  • axis: 補間を行う軸。デフォルトは 0

In [5]:
# 補間
temp_int = temp.interpolate(method='index')

temp_intS = temp.interpolate(method='spline',order=3) #スプライン補間


# データが埋まったか確認
print(temp_int.isnull().any())
False

これで無事に欠損データがなくなった。最後に補間する前と後のデータを図示して比べてみよう。

In [6]:
dst = "1978-05-15"; den = "1978-07-15"

# 図示
fig = plt.figure(figsize=(10,4))
ax = fig.add_subplot(111)

# 補間後
ax.plot(temp_intS[dst:den],label='Interpolated (Spline)')
ax.plot(temp_int[dst:den],label='Interpolated (Linear)')
# 補間前
ax.plot(temp[dst:den],label='Original')

ax.set_title('Temperature')
ax.set_xlabel('Date')
ax.set_ylabel('Temperature')

ax.legend()

# 図を表示
plt.show()

(余談)異常値について

 一方、異常値の除去については決まったやり方は存在せず、データの質や状況に応じて適切な手法を用いるしかない(今でも気象庁での品質管理では最後は人が目で見てチェックしているようである)。データセットによっては、品質管理情報が付与されていることもある(ここで用いているAMeDASデータでも、整数値として与えられている)。

 一から異常値を除去する場合、手法はいろいろあるが、個人的にはメディアンフィルターが気に入っている。これはデータをある期間に区切ってそれぞれでメディアン(中央値)を求め、そこから大きく外れた値(標準偏差の3倍とか)を欠損とみなすものである(この作業を収束するまで(外れた値が無くなるまで)繰り返すこともある)。ちなみに、京大のMUレーダーの品質管理でもこの手法がベースになっていた。