在 Pandas 系列中填充连续的 NaN
Fill Consecutive NaNs in Pandas Series
如果连续 NAN 少于 3 个,我想在我的 pandas 系列中填充缺失值。
缺失值的原始系列:
s=pd.Series(pd.np.random.randn(20))
s[[1,3,5,7,12,13,14,15, 18]]=pd.np.nan
给出:
0 0.444025
1 NaN
2 0.631753
3 NaN
4 -0.577121
5 NaN
6 1.299953
7 NaN
8 -0.252173
9 0.287641
10 0.941953
11 -1.624728
12 NaN
13 NaN
14 NaN
15 NaN
16 0.998952
17 0.195698
18 NaN
19 -0.788995
但是,使用带有限制的 pandas.fillna() 只会填充指定值的数量(不是预期的连续 NAN 数):
s.fillna(value=0, limit=3) #Fails to fill values at position 7 and forward
所需的输出将在位置 1、3、5、7 和 18 处用 0 填充 NAN。它会在位置 12-15 处留下一系列 4 个 NaN。
SO 上的文档和其他帖子尚未解决此问题(例如 )。文档似乎暗示此限制将适用于连续的 NAN,而不适用于将填充的整个数据集中的总体 #。谢谢!
我们首先通过 pd.Series.notna
找到 nan
值的位置。
当我们使用 cumsum
时,每当我们遇到 non-null 值时,我们都会增加累积和,从而为连续的 nan
值生成方便的组。
但是,除了第一组(可能是第一组),我们从 non-null 值开始。因此,我对 mask
求反并对每个组中的空值总数求和。
现在我 fillna
并使用 pd.DataFrame.where
来掩盖 nan
值之和太多的地方。
mask = s.notna()
c_na = (~mask).groupby(mask.cumsum()).transform('sum')
filled = s.fillna(0).where(c_na.le(3))
s.fillna(filled)
0 1.418895
1 0.000000
2 -0.553732
3 0.000000
4 -0.101532
5 0.000000
6 -1.334803
7 0.000000
8 1.159115
9 0.309093
10 -0.047970
11 0.051567
12 NaN
13 NaN
14 NaN
15 NaN
16 0.623673
17 -0.786857
18 0.000000
19 0.310688
dtype: float64
这是使用 np.bincount
和 pd.factorize
的奇特 Numpy/Pandas 方式
v = s.values
m = np.isnan(v)
f, u = pd.factorize((~m).cumsum())
filled = np.where(
~m, v,
np.where(np.bincount(f, weights=mask)[f] <= 3, 0, np.nan)
)
pd.Series(filled, s.index)
0 1.418895
1 0.000000
2 -0.553732
3 0.000000
4 -0.101532
5 0.000000
6 -1.334803
7 0.000000
8 1.159115
9 0.309093
10 -0.047970
11 0.051567
12 NaN
13 NaN
14 NaN
15 NaN
16 0.623673
17 -0.786857
18 0.000000
19 0.310688
dtype: float64
您可以按以下方式使用 rolling
运算符进行尝试:
1) 创建一个函数,仅当 window
中的值小于 X 时才 returns 0
fillnaiflessthan(series, count):
if series.isnull().sum() < count and series.center == pd.NaN:
return 0
2) 然后在里面使用rolling
s.rolling(window=5, center=True, min_periods=0).apply(lambda x: fillnaiflessthan(x, 4))
首先,构建一个 na cum_count 列。连续的 nas 将具有相同的 cum_count.
df = s.to_frame('value').assign(na_ct=s.notna().cumsum())
然后我们可以按na分组cum_count,检查每组中的行数并决定weather是否填充nas。
df.groupby(df.na_ct).apply(lambda x: x if len(x)>4 else x.fillna(0)).value
Out[76]:
0 0.195634
1 0.000000
2 -0.818349
3 0.000000
4 -2.347686
5 0.000000
6 -0.464040
7 0.000000
8 0.179321
9 0.356661
10 0.471832
11 -1.217082
12 NaN
13 NaN
14 NaN
15 NaN
16 -0.112744
17 -2.630191
18 0.000000
19 -0.313592
Name: value, dtype: float64
也许试试这个?
t=s[s.isnull()];
v=pd.Series(t.index,index=t.index).diff().ne(1).cumsum();
z=v[v.isin(v.value_counts()[v.value_counts().gt(3)].index.values)];
s.fillna(0).mask(s.index.isin(z.index))
Out[348]:
0 -0.781728
1 0.000000
2 -1.114552
3 0.000000
4 1.242452
5 0.000000
6 0.599486
7 0.000000
8 0.757384
9 -1.559661
10 0.527451
11 -0.426890
12 NaN
13 NaN
14 NaN
15 NaN
16 -1.264962
17 0.703790
18 0.000000
19 0.953616
dtype: float64
如果连续 NAN 少于 3 个,我想在我的 pandas 系列中填充缺失值。
缺失值的原始系列:
s=pd.Series(pd.np.random.randn(20))
s[[1,3,5,7,12,13,14,15, 18]]=pd.np.nan
给出:
0 0.444025
1 NaN
2 0.631753
3 NaN
4 -0.577121
5 NaN
6 1.299953
7 NaN
8 -0.252173
9 0.287641
10 0.941953
11 -1.624728
12 NaN
13 NaN
14 NaN
15 NaN
16 0.998952
17 0.195698
18 NaN
19 -0.788995
但是,使用带有限制的 pandas.fillna() 只会填充指定值的数量(不是预期的连续 NAN 数):
s.fillna(value=0, limit=3) #Fails to fill values at position 7 and forward
所需的输出将在位置 1、3、5、7 和 18 处用 0 填充 NAN。它会在位置 12-15 处留下一系列 4 个 NaN。
SO 上的文档和其他帖子尚未解决此问题(例如
我们首先通过 pd.Series.notna
找到 nan
值的位置。
当我们使用 cumsum
时,每当我们遇到 non-null 值时,我们都会增加累积和,从而为连续的 nan
值生成方便的组。
但是,除了第一组(可能是第一组),我们从 non-null 值开始。因此,我对 mask
求反并对每个组中的空值总数求和。
现在我 fillna
并使用 pd.DataFrame.where
来掩盖 nan
值之和太多的地方。
mask = s.notna()
c_na = (~mask).groupby(mask.cumsum()).transform('sum')
filled = s.fillna(0).where(c_na.le(3))
s.fillna(filled)
0 1.418895
1 0.000000
2 -0.553732
3 0.000000
4 -0.101532
5 0.000000
6 -1.334803
7 0.000000
8 1.159115
9 0.309093
10 -0.047970
11 0.051567
12 NaN
13 NaN
14 NaN
15 NaN
16 0.623673
17 -0.786857
18 0.000000
19 0.310688
dtype: float64
这是使用 np.bincount
和 pd.factorize
v = s.values
m = np.isnan(v)
f, u = pd.factorize((~m).cumsum())
filled = np.where(
~m, v,
np.where(np.bincount(f, weights=mask)[f] <= 3, 0, np.nan)
)
pd.Series(filled, s.index)
0 1.418895
1 0.000000
2 -0.553732
3 0.000000
4 -0.101532
5 0.000000
6 -1.334803
7 0.000000
8 1.159115
9 0.309093
10 -0.047970
11 0.051567
12 NaN
13 NaN
14 NaN
15 NaN
16 0.623673
17 -0.786857
18 0.000000
19 0.310688
dtype: float64
您可以按以下方式使用 rolling
运算符进行尝试:
1) 创建一个函数,仅当 window
中的值小于 X 时才 returns 0fillnaiflessthan(series, count):
if series.isnull().sum() < count and series.center == pd.NaN:
return 0
2) 然后在里面使用rolling
s.rolling(window=5, center=True, min_periods=0).apply(lambda x: fillnaiflessthan(x, 4))
首先,构建一个 na cum_count 列。连续的 nas 将具有相同的 cum_count.
df = s.to_frame('value').assign(na_ct=s.notna().cumsum())
然后我们可以按na分组cum_count,检查每组中的行数并决定weather是否填充nas。
df.groupby(df.na_ct).apply(lambda x: x if len(x)>4 else x.fillna(0)).value
Out[76]:
0 0.195634
1 0.000000
2 -0.818349
3 0.000000
4 -2.347686
5 0.000000
6 -0.464040
7 0.000000
8 0.179321
9 0.356661
10 0.471832
11 -1.217082
12 NaN
13 NaN
14 NaN
15 NaN
16 -0.112744
17 -2.630191
18 0.000000
19 -0.313592
Name: value, dtype: float64
也许试试这个?
t=s[s.isnull()];
v=pd.Series(t.index,index=t.index).diff().ne(1).cumsum();
z=v[v.isin(v.value_counts()[v.value_counts().gt(3)].index.values)];
s.fillna(0).mask(s.index.isin(z.index))
Out[348]:
0 -0.781728
1 0.000000
2 -1.114552
3 0.000000
4 1.242452
5 0.000000
6 0.599486
7 0.000000
8 0.757384
9 -1.559661
10 0.527451
11 -0.426890
12 NaN
13 NaN
14 NaN
15 NaN
16 -1.264962
17 0.703790
18 0.000000
19 0.953616
dtype: float64