3 回答

TA贡献1820条经验 获得超9个赞
想法是创建布尔面具和过滤bfill用where,然后ffill和最后bfill仅如果通过启动第一系列的第一个值再次NaN:
m = df['A'].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)
df['B'] = df['A'].where(mask, df['A'].bfill()).ffill().bfill()
print (df)
A B
0 NaN 4.0
1 NaN 4.0
2 4.0 4.0
3 NaN 4.0
4 NaN 7.0
5 7.0 7.0
6 NaN 7.0
7 NaN 7.0
8 NaN 1.0
9 NaN 1.0
10 1.0 1.0
11 NaN 1.0
12 NaN 6.0
13 6.0 6.0
14 NaN 6.0
15 NaN 6.0

TA贡献1874条经验 获得超12个赞
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 之前屏蔽和安排的额外开销得到回报。
添加回答
举报