结合使用bfill()和ffill()功能

时间:2018-12-12 15:07:39

标签: python pandas

说我有以下pd.Series

0     NaN
1     NaN
2     4.0
3     NaN
4     NaN
5     7.0
6     NaN
7     NaN
8     NaN
9     NaN
10    1.0
11    NaN
12    NaN
13    6.0
14    NaN
15    NaN

我正在尝试获得一个NaNs被周围有效样本均等填充的序列。假设我在两个有效样本之间有N NaNs,我希望第一个N/2 NaNs用最后一个有效样本填充,最后一个{{1} } N/2将使用下一个有效观察值进行填充。在NaNs的情况下,多余的观察值可以由周围的两个观察值中的任何一个填充,无论哪种规则都可以。所以我想获得:

(N % 2) =! 0

3 个答案:

答案 0 :(得分:2)

想法是创建布尔掩码,并用where过滤bfill,然后过滤ffill,最后过滤bfill,如果从{{1}开始,则仅对第一个Series的第一个值}:

NaN

答案 1 :(得分:2)

我发现这是一个有趣的问题。我与这段代码非常接近(以一种更可矢量化/可移植的方式),也许这里经验更丰富的人之一可以帮助完成最后一步:

s = pd.Series([np.nan, np.nan, 4,np.nan, np.nan, 7, np.nan, np.nan, np.nan, np.nan, 1, np.nan, np.nan, 6, np.nan, np.nan])
df = pd.DataFrame(s)
df2 = df[df.isna().any(axis=1)]
grouped = df2.groupby((df2.index.to_series().diff() > 1).cumsum())
df3 = pd.DataFrame()
for group in grouped:
    test = group[1].reset_index()
    test['fill'] = pd.qcut(test.iloc[:,0], 2, labels=['ff', 'bf'])
    df3 = pd.concat([df3, test])
df3.set_index('index', inplace=True)
pd.merge(df, df3, how='left', left_index=True, right_index=True).drop(columns=['0_y'])

输出:

    0_x     fill

0   NaN     ff
1   NaN     bf
2   4.0     NaN
3   NaN     ff
4   NaN     bf
5   7.0     NaN
6   NaN     ff
7   NaN     ff
8   NaN     bf
9   NaN     bf
10  1.0     NaN
11  NaN     ff
12  NaN     bf
13  6.0     NaN
14  NaN     ff
15  NaN     bf

答案 2 :(得分:1)

import pandas as pd
while pd.isnull(my_series).sum() > 0:
    my_series = my_series.fillna(method='ffill', limit=1).fillna(method='bfill', limit=1)

要说这会很慢,可能是轻描淡写。如果您想在大型DataFrame上执行此操作,则可能会尝试使用可以在apply上使用的函数来实现它。

我从来没有真正提出过一个好主意(但是我一直在关注这个问题,因为这是一个有趣的问题)。我喜欢聪明的另一个答案,但我很好奇它对速度的影响。

def funcA(pd_series):
    m = pd_series.notna()
    c = m.cumsum()
    def f(x):
        lens = len(x.index)
        a = np.arange(lens)
        return a // (lens / 2) == 0
    mask = c[~m].groupby(c).transform(f)
    #should be removed
    #mask = mask.reindex(df.index, fill_value=False)
    return pd_series.where(mask, pd_series.bfill()).ffill().bfill()

def funcB(pd_series):
    while pd.isnull(pd_series).sum() > 0:
        pd_series = pd_series.fillna(method='ffill', limit=1).fillna(method='bfill', limit=1)
    return pd_series

ps = pd.Series(np.random.randint(0,10, size=(10000)))
ps[ps < 5] = np.nan

>>> import timeit
>>> timeit.timeit('funcA(ps)', setup='from __main__ import funcA, ps', number=100)
40.9788393480012
>>> timeit.timeit('funcB(ps)', setup='from __main__ import funcB, ps', number=100)
0.4896140840010048

嗯...那不如我期望的那么好。带有少量NaN的小序列可能不是一个很好的测试,因此也许尝试一些while循环会阻塞?

ps = pd.Series(np.random.randint(0,100, size=(1000000)))
ps[ps < 95] = np.nan

>>> timeit.timeit('funcA(ps)', setup='from __main__ import funcA, ps', number=10)
81.64654629600045
>>> timeit.timeit('funcB(ps)', setup='from __main__ import funcB, ps', number=10)
21.431495654000173

好吧,至少更接近了。我懒得再扩大规模,但看起来您可能需要10 ^ 7个条目(含95%以上的NaN),然后掩盖和安排的额外开销才能得到回报。