用 Pandas 识别连续的 NaN
Identifying consecutive NaNs with Pandas
我正在读取一堆 CSV 文件(水位随时间变化的测量数据)以对它们进行各种分析和可视化。
由于各种不可控的原因,这些时间序列经常有数据缺失,所以我做了两件事:
我一共数了
Rlength = len(RainD) # Counts everything, including NaN
Rcount = RainD.count() # Counts only valid numbers
NaN_Number = Rlength - Rcount
如果缺失数据多于特定阈值,则丢弃数据集:
Percent_Data = Rlength/100
Five_Percent = Percent_Data*5
if NaN_Number > Five_Percent:
...
如果NaN的数量足够少,我想用
来填补空白
RainD.level = RainD.level.fillna(method='pad', limit=2)
现在问题来了:这是月度数据,所以如果我有超过两个连续的 NaN,我也想丢弃这些数据,因为这意味着我“猜测”了整个赛季,甚至更多。
documentation for fillna
并没有真正提到当连续的 NaN 多于我指定的 limit=2
时会发生什么,但是当我查看 [=17= 之前和之后的 RainD.describe()
时会发生什么] 并与基本CSV进行比较,很明显它填充了前两个NaN,然后将其余部分保持原样,而不是出错。
所以,长话短说:
如何使用 Pandas 识别多个连续的 NaN,而无需一些复杂且耗时的非 Pandas 循环?
您可以使用多个布尔条件来测试当前值和先前值是否为NaN
:
In [3]:
df = pd.DataFrame({'a':[1,3,np.NaN, np.NaN, 4, np.NaN, 6,7,8]})
df
Out[3]:
a
0 1
1 3
2 NaN
3 NaN
4 4
5 NaN
6 6
7 7
8 8
In [6]:
df[(df.a.isnull()) & (df.a.shift().isnull())]
Out[6]:
a
3 NaN
如果您想查找连续 NaNs
出现在您要查找超过 2 个的地方,您可以执行以下操作:
In [38]:
df = pd.DataFrame({'a':[1,2,np.NaN, np.NaN, np.NaN, 6,7,8,9,10,np.NaN,np.NaN,13,14]})
df
Out[38]:
a
0 1
1 2
2 NaN
3 NaN
4 NaN
5 6
6 7
7 8
8 9
9 10
10 NaN
11 NaN
12 13
13 14
In [41]:
df.a.isnull().astype(int).groupby(df.a.notnull().astype(int).cumsum()).sum()
Out[41]:
a
1 0
2 3
3 0
4 0
5 0
6 0
7 2
8 0
9 0
Name: a, dtype: int32
如果您希望将其映射回原始索引,或者有一个连续的 NaN 计数,请使用 Ed 的答案 cumsum
而不是 sum
。这对于可视化时间序列中的 NaN 组特别有用:
df = pd.DataFrame({'a':[
1,2,np.NaN, np.NaN, np.NaN, 6,7,8,9,10,np.NaN,np.NaN,13,14
]})
df.a.isnull().astype(int).groupby(df.a.notnull().astype(int).cumsum()).cumsum()
0 0
1 0
2 1
3 2
4 3
5 0
6 0
7 0
8 0
9 0
10 1
11 2
12 0
13 0
Name: a, dtype: int64
例如,
pd.concat([
df,
(
df.a.isnull().astype(int)
.groupby(df.a.notnull().astype(int).cumsum())
.cumsum().to_frame('consec_count')
)
],
axis=1
)
a consec_count
0 1.0 0
1 2.0 0
2 NaN 1
3 NaN 2
4 NaN 3
5 6.0 0
6 7.0 0
7 8.0 0
8 9.0 0
9 10.0 0
10 NaN 1
11 NaN 2
12 13.0 0
13 14.0 0
如果您只想求出连续 NaN 的长度...
# usual imports
import pandas as pd
import numpy as np
# fake data
data = pd.Series([np.nan,1,1,1,1,1,np.nan,np.nan,np.nan,1,1,np.nan,np.nan])
# code
na_groups = data.notna().cumsum()[data.isna()]
lengths_consecutive_na = na_groups.groupby(na_groups).agg(len)
longest_na_gap = lengths_consecutive_na.max()
我正在读取一堆 CSV 文件(水位随时间变化的测量数据)以对它们进行各种分析和可视化。
由于各种不可控的原因,这些时间序列经常有数据缺失,所以我做了两件事:
我一共数了
Rlength = len(RainD) # Counts everything, including NaN
Rcount = RainD.count() # Counts only valid numbers
NaN_Number = Rlength - Rcount
如果缺失数据多于特定阈值,则丢弃数据集:
Percent_Data = Rlength/100
Five_Percent = Percent_Data*5
if NaN_Number > Five_Percent:
...
如果NaN的数量足够少,我想用
来填补空白RainD.level = RainD.level.fillna(method='pad', limit=2)
现在问题来了:这是月度数据,所以如果我有超过两个连续的 NaN,我也想丢弃这些数据,因为这意味着我“猜测”了整个赛季,甚至更多。
documentation for fillna
并没有真正提到当连续的 NaN 多于我指定的 limit=2
时会发生什么,但是当我查看 [=17= 之前和之后的 RainD.describe()
时会发生什么] 并与基本CSV进行比较,很明显它填充了前两个NaN,然后将其余部分保持原样,而不是出错。
所以,长话短说:
如何使用 Pandas 识别多个连续的 NaN,而无需一些复杂且耗时的非 Pandas 循环?
您可以使用多个布尔条件来测试当前值和先前值是否为NaN
:
In [3]:
df = pd.DataFrame({'a':[1,3,np.NaN, np.NaN, 4, np.NaN, 6,7,8]})
df
Out[3]:
a
0 1
1 3
2 NaN
3 NaN
4 4
5 NaN
6 6
7 7
8 8
In [6]:
df[(df.a.isnull()) & (df.a.shift().isnull())]
Out[6]:
a
3 NaN
如果您想查找连续 NaNs
出现在您要查找超过 2 个的地方,您可以执行以下操作:
In [38]:
df = pd.DataFrame({'a':[1,2,np.NaN, np.NaN, np.NaN, 6,7,8,9,10,np.NaN,np.NaN,13,14]})
df
Out[38]:
a
0 1
1 2
2 NaN
3 NaN
4 NaN
5 6
6 7
7 8
8 9
9 10
10 NaN
11 NaN
12 13
13 14
In [41]:
df.a.isnull().astype(int).groupby(df.a.notnull().astype(int).cumsum()).sum()
Out[41]:
a
1 0
2 3
3 0
4 0
5 0
6 0
7 2
8 0
9 0
Name: a, dtype: int32
如果您希望将其映射回原始索引,或者有一个连续的 NaN 计数,请使用 Ed 的答案 cumsum
而不是 sum
。这对于可视化时间序列中的 NaN 组特别有用:
df = pd.DataFrame({'a':[
1,2,np.NaN, np.NaN, np.NaN, 6,7,8,9,10,np.NaN,np.NaN,13,14
]})
df.a.isnull().astype(int).groupby(df.a.notnull().astype(int).cumsum()).cumsum()
0 0
1 0
2 1
3 2
4 3
5 0
6 0
7 0
8 0
9 0
10 1
11 2
12 0
13 0
Name: a, dtype: int64
例如,
pd.concat([
df,
(
df.a.isnull().astype(int)
.groupby(df.a.notnull().astype(int).cumsum())
.cumsum().to_frame('consec_count')
)
],
axis=1
)
a consec_count
0 1.0 0
1 2.0 0
2 NaN 1
3 NaN 2
4 NaN 3
5 6.0 0
6 7.0 0
7 8.0 0
8 9.0 0
9 10.0 0
10 NaN 1
11 NaN 2
12 13.0 0
13 14.0 0
如果您只想求出连续 NaN 的长度...
# usual imports
import pandas as pd
import numpy as np
# fake data
data = pd.Series([np.nan,1,1,1,1,1,np.nan,np.nan,np.nan,1,1,np.nan,np.nan])
# code
na_groups = data.notna().cumsum()[data.isna()]
lengths_consecutive_na = na_groups.groupby(na_groups).agg(len)
longest_na_gap = lengths_consecutive_na.max()