在Pandas系列中填写连续的NaN

时间:2018-03-20 22:29:15

标签: pandas nan series fillna

如果少于3个连续的NAN,我想填写我的熊猫系列中的缺失值。

缺少值的原始系列:

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()只会填充指定的值数(不是符合预期的符号数量):

s.fillna(value=0, limit=3) #Fails to fill values at position 7 and forward

所需的输出将在1,3,5,7和18位填充NANs为0.这将在12-15位置留下一系列4个NaN。

SO上的文档和其他帖子尚未解决此问题(例如here)。文档似乎暗示此限制将适用于连续的NAN,而不是将填充的整个数据集中的整体#。谢谢!

4 个答案:

答案 0 :(得分:4)

我们首先通过nan查找pd.Series.notna值的位置。

当我们使用cumsum时,每当遇到非空值时,我们都会递增累积和,从而为连续的nan值生成方便的组。

但是,除了第一组(也许是第一组)之外的所有组,我们都以非空值开头。因此,我取消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.bincountpd.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

答案 1 :(得分:2)

首先,构建一个na cum_count列。连续的nas将具有相同的cum_count。

df = s.to_frame('value').assign(na_ct=s.notna().cumsum())

然后我们可以通过na cum_count进行分组,检查每组中的行数,并确定天气是否填充。

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

答案 2 :(得分:2)

也许试试这个?

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

答案 3 :(得分:1)

您可以按以下方式使用rolling运算符进行尝试:

1)创建一个仅当窗口中的值小于X时才返回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))