【问题标题】:Fill Consecutive NaNs in Pandas Series在 Pandas 系列中填充连续的 NaN
【发布时间】:2018-03-20 22:29:14
【问题描述】:

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

【问题讨论】:

  • 感谢您的解决方案。我只是很惊讶没有更简单的方法可以做到这一点!
  • 使用“shift”会更简单吗?即,首先用long_nan_gaps= s.index[s.shift(1).isnull() & s.shift(-1).isnull() & s.isnull()]存储所有长NAN间隙的位置,然后用0填充所有NAN,然后在事后将保存的位置恢复到NAN?我很欣赏许多解决方案;只是想知道您在下面提出的一个是否比我在这里为自己想出的更好(我认为这太丑陋/令人困惑)。

标签: pandas nan series fillna


【解决方案1】:

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

当我们使用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

【讨论】:

  • 你可以用masked = s.groupby(m.cumsum()).transform('size').gt(3); s.fillna(0).mask(masked)保存一个fillna
  • 我觉得应该是s.fillna(0).mask(sumna.ge(3))吧?
  • 您能否详细说明一下代码的作用?我看到它有效,但不明白groupby 中发生了什么
  • 刚到电脑。我会在几分钟内详细说明
  • @cᴏʟᴅsᴘᴇᴇᴅ size 将捕获第一个非空值加上随后的空值,并且通常比空值的数量多一个。请注意,我们关注的是每组的空值数量。 'count' 会做得更好,或者相反。现在正在处理这个(-:
【解决方案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】:

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

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

    然后我们可以按 na cum_count 进行分组,检查每个组中的行数并决定天气是否填充 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
    

    【讨论】:

      【解决方案4】:

      您可以通过以下方式使用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))
      

      【讨论】:

        猜你喜欢
        • 2017-09-16
        • 2018-12-20
        • 1970-01-01
        • 2017-03-11
        • 1970-01-01
        • 1970-01-01
        • 2015-02-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多