通过使用 pandas 在时间序列中在先前的 NaN 之间分配值来回填值
Posted
技术标签:
【中文标题】通过使用 pandas 在时间序列中在先前的 NaN 之间分配值来回填值【英文标题】:Backfill values by distributing values across prior NaNs in a timeseries with pandas 【发布时间】:2018-10-19 15:04:56 【问题描述】:我有一个时间序列,其中每个观察代表自上次观察以来某事物的总量,如果在该时间步中没有观察,则该值报告为 NaN。格式示例:
Timestep Value
1 10
2 NaN
3 NaN
4 9
5 NaN
6 NaN
7 NaN
8 16
9 NaN
10 NaN
我想做的是将观察到的值分布在之前的 NaN 中。例如,像 [5, NaN, NaN, 6] 这样的序列将变为 [5, 2, 2, 2],最终观察值 6 分布在最后 2 个 NaN 值上。应用于所需输出上方的数据框将是:
Timestep Value
1 10
2 3
3 3
4 3
5 4
6 4
7 4
8 4
9 NaN
10 NaN
我已经尝试使用 pandas 的一些回填和插值方法来执行此操作,但没有找到完全符合我要求的方法。
【问题讨论】:
【参考方案1】:transform
df.Value.bfill().div(
df.groupby(df.Value.notna()[::-1].cumsum()).Value.transform('size')
)
0 10.0
1 3.0
2 3.0
3 3.0
4 4.0
5 4.0
6 4.0
7 4.0
8 NaN
9 NaN
Name: Value, dtype: float64
np.bincount
和 pd.factorize
a = df.Value.notna().values
f, u = pd.factorize(a[::-1].cumsum()[::-1])
df.Value.bfill().div(np.bincount(f)[f])
0 10.0
1 3.0
2 3.0
3 3.0
4 4.0
5 4.0
6 4.0
7 4.0
8 NaN
9 NaN
Name: Value, dtype: float64
替代的较短版本。这行得通,因为cumsum
自然地给了我factorize
的作用。
a = df.Value.notna().values[::-1].cumsum()[::-1]
df.Value.bfill().div(np.bincount(a)[a])
详情
在上面的两个选项中,我们需要确定空值在哪里,并在反向序列上使用cumsum
来定义组。在transform
选项中,我使用groupby
和size
来计算这些组的大小。
第二个选项使用 bin 计数和切片来获得相同的系列。
感谢@ScottBoston 提醒我提到反转元素[::-1]
【讨论】:
我很久以前从你的一篇文章中学到了 [::-1] 的把戏。我认为这是这个解决方案的关键。尤其是在考虑影响系列中“先前”值的问题时。【参考方案2】:计算累积 NA,然后我们做update
s=df.Value.notnull().cumsum().shift(1)
df.Value.update(df.Value.bfill()/s.groupby(s).transform('count'))
df
Out[885]:
Timestep Value
0 1 10.0
1 2 3.0
2 3 3.0
3 4 3.0
4 5 4.0
5 6 4.0
6 7 4.0
7 8 4.0
8 9 NaN
9 10 NaN
【讨论】:
方法也不错。我喜欢这种转变。 +1以上是关于通过使用 pandas 在时间序列中在先前的 NaN 之间分配值来回填值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 python 中在 spark notebook 上使用 pandas(dashDB 上的数据)
pandas使用read_csv函数读取文件时指定数据列的数据类型pandas使用read_csv函数读取文件时通过keep_default_na参数设置缺失值替换为空字符串