Python Pandas:如何计算导数/梯度

32

假设我有以下两个向量:

In [99]: time_index
Out[99]: 
[1484942413,
 1484942712,
 1484943012,
 1484943312,
 1484943612,
 1484943912,
 1484944212,
 1484944511,
 1484944811,
 1484945110]

In [100]: bytes_in
Out[100]: 
[1293981210388,
 1293981379944,
 1293981549960,
 1293981720866,
 1293981890968,
 1293982062261,
 1293982227492,
 1293982391244,
 1293982556526,
 1293982722320]

其中bytes_in是递增计数器,time_index是unix时间戳(时代)的列表。

目标:我想要计算的是比特率。

这意味着我将构建一个类似于以下的数据框:

In [101]: timeline = pandas.to_datetime(time_index, unit="s")

In [102]: recv = pandas.Series(bytes_in, timeline).resample("300S").mean().ffill().apply(lambda i: i*8)

In [103]: recv
Out[103]: 
2017-01-20 20:00:00    10351849683104
2017-01-20 20:05:00    10351851039552
2017-01-20 20:10:00    10351852399680
2017-01-20 20:15:00    10351853766928
2017-01-20 20:20:00    10351855127744
2017-01-20 20:25:00    10351856498088
2017-01-20 20:30:00    10351857819936
2017-01-20 20:35:00    10351859129952
2017-01-20 20:40:00    10351860452208
2017-01-20 20:45:00    10351861778560
Freq: 300S, dtype: int64

问题: 现在奇怪的是,手动计算梯度给出的是:

In [104]: (bytes_in[1]-bytes_in[0])*8/300
Out[104]: 4521.493333333333

哪个是正确的值...

使用pandas计算梯度会给我

In [124]: recv.diff()
Out[124]: 
2017-01-20 20:00:00          NaN
2017-01-20 20:05:00    1356448.0
2017-01-20 20:10:00    1360128.0
2017-01-20 20:15:00    1367248.0
2017-01-20 20:20:00    1360816.0
2017-01-20 20:25:00    1370344.0
2017-01-20 20:30:00    1321848.0
2017-01-20 20:35:00    1310016.0
2017-01-20 20:40:00    1322256.0
2017-01-20 20:45:00    1326352.0
Freq: 300S, dtype: float64

这与上述不同,1356448.0与4521.493333333333不同

您能否解释一下我做错了什么?


尝试使用 recv.diff(),这是一个函数调用,其中 recv.diff 是对 diff 方法的引用;-) - MaxU - stand with Ukraine
@MaxU 谢谢 :) 你说得对!尽管我仍然看到不同的结果。我已经用新的结果编辑了原始问题。 - nskalis
2
我想你忘记了其他的步骤 - recv.diff().mul(8).div(300) - Nickil Maveli
3
@NickilMaveli 哦,谢谢。问题解决了。 - nskalis
5个回答

49

pd.Series.diff() 只计算差值,不会再除以索引的增量。

这将返回您要找的答案。

recv.diff() / recv.index.to_series().diff().dt.total_seconds()

2017-01-20 20:00:00            NaN
2017-01-20 20:05:00    4521.493333
2017-01-20 20:10:00    4533.760000
2017-01-20 20:15:00    4557.493333
2017-01-20 20:20:00    4536.053333
2017-01-20 20:25:00    4567.813333
2017-01-20 20:30:00    4406.160000
2017-01-20 20:35:00    4366.720000
2017-01-20 20:40:00    4407.520000
2017-01-20 20:45:00    4421.173333
Freq: 300S, dtype: float64
你还可以使用numpy.gradient函数,将bytes_in和你期望得到的增量传递进去。这不会减少一个长度,而是对边缘做出一些假设。
np.gradient(bytes_in, 300) * 8

array([ 4521.49333333,  4527.62666667,  4545.62666667,  4546.77333333,
        4551.93333333,  4486.98666667,  4386.44      ,  4387.12      ,
        4414.34666667,  4421.17333333])

3
你能否解释一下为什么np.gradient的结果与第一个提出的答案不同。 - Darthtrader
3
请问我正确理解了吗?numpy.gradient使用中心有限差分实现,而pandas的diff默认使用后向有限差分。换句话说,NumPy的实现使用前后两个数据点,而Pandas使用前一个数据点和当前数据点。 Numpy文档 ; Pandas文档 - Smokesick

2
一个朴素的解释是,.diff() 逐项相减,而 np.gradient() 使用中心差分方案。将其翻译为通俗易懂的表述可能是:.diff() 是一种按项相减的方法,而 np.gradient() 则使用了一种中心差分方案。

2
由于Pandas Series / DataFrame中没有内置的derivative方法,您可以使用https://github.com/scls19fr/pandas-helper-calc。它将为Pandas Series和DataFrames提供一个名为calc的新访问器,以计算数值导数和积分。因此,您只需执行以下操作:
recv.calc.derivative()

它在幕后使用diff()。最初的回答。

1

如果你想计算变化率,你可以使用df.pct_change()

作为参数,你可以输入df.pct_change(n),其中n是回溯期,假设你有一个日期时间索引的数据框。


1
为了得到正确的时间导数,需要更改你的series的索引。
def derivate(serie):
    df1 = (serie.diff() / serie.index.to_series().diff().dt.total_seconds()).dropna()
    df1.index = serie.index[0:-1]
    return df1

你能解释一下为什么np.gradient产生的结果与第一个提出的答案不同吗?np.gradient使用的是二阶方案,而.diff()使用的则是一阶方案。这意味着np.gradient产生的结果将是连续的,导数也将是连续的。.diff()产生的结果不一定具有连续的导数。基本上, np.gradient会给出“更平滑”的结果。 - Messypuddle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接