说我有以下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
答案 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),然后掩盖和安排的额外开销才能得到回报。