【问题标题】:pandas - cumulative median熊猫 - 累积中位数
【发布时间】:2017-12-27 10:46:12
【问题描述】:

我想知道是否有任何熊猫相当于 cumsum()cummax() 等中位数:例如cummedian().

如果我有,例如这个数据框:

   a
1  5
2  7
3  6
4  4

我想要的是这样的:

df['a'].cummedian()

应该输出:

5
6  
6 
5.5

【问题讨论】:

  • 如果您正在寻找一个快速的解决方案,我打赌 divakar 会是它。
  • @cᴏʟᴅsᴘᴇᴇᴅ 不,我的看起来更慢! np.nanmedian 没有多大帮助。
  • @OP,请在一个包含 100000 个元素的大型数据集上测试我们所有的解决方案。最适合小数据的解决方案可能并不总是可扩展的。
  • @Binayamin Even,请看我的回答。我为大数据添加了计时,expanding.median 显然是赢家。
  • 如果您必须对多列执行扩展中位数,使用expanding 更容易泛化。另一方面,缺乏任何矢量化优势的纯 python 函数将线性扩展,导致更大的减速。

标签: python pandas math accumulate


【解决方案1】:

你可以使用expanding.median -

df.a.expanding().median()

1    5.0
2    6.0
3    6.0
4    5.5
Name: a, dtype: float64

时间安排

df = pd.DataFrame({'a' : np.arange(1000000)})

%timeit df['a'].apply(cummedian())
1 loop, best of 3: 1.69 s per loop

%timeit df.a.expanding().median()
1 loop, best of 3: 838 ms per loop

优胜者是expanding.median,差距很大。 Divakar 的方法是内存密集型的,并且在这种输入大小下会出现内存井喷。

【讨论】:

  • 在此处发布了您正在寻找的解决方案 :)
  • 使用strides 很难从您的问题中获得预期的输出。
【解决方案2】:

我们可以使用基于 strides 的函数将 nan 填充子数组创建为行,就像这样 -

def nan_concat_sliding_windows(x):
    n = len(x)
    add_arr = np.full(n-1, np.nan)
    x_ext = np.concatenate((add_arr, x))
    strided = np.lib.stride_tricks.as_strided
    nrows = len(x_ext)-n+1
    s = x_ext.strides[0]
    return strided(x_ext, shape=(nrows,n), strides=(s,s))

示例运行 -

In [56]: x
Out[56]: array([5, 6, 7, 4])

In [57]: nan_concat_sliding_windows(x)
Out[57]: 
array([[ nan,  nan,  nan,   5.],
       [ nan,  nan,   5.,   6.],
       [ nan,   5.,   6.,   7.],
       [  5.,   6.,   7.,   4.]])

因此,要获得数组x 的滑动中值,我们将有一个矢量化解决方案,就像这样-

np.nanmedian(nan_concat_sliding_windows(x), axis=1)

因此,最终的解决方案是 -

In [54]: df
Out[54]: 
a
1  5
2  7
3  6
4  4

In [55]: pd.Series(np.nanmedian(nan_concat_sliding_windows(df.a.values), axis=1))
Out[55]: 
0    5.0
1    6.0
2    6.0
3    5.5
dtype: float64

【讨论】:

  • 嗯,我知道有办法大步前进。你明白了。
  • 好的,我计时了这个答案,结果我的测试输入失败了。我不认为有任何方法可以减少内存密集度,对吧? :(
【解决方案3】:

特定累积中位数的更快解决方案

In [1]: import timeit

In [2]: setup = """import bisect
   ...: import pandas as pd
   ...: def cummedian():
   ...:     l = []
   ...:     info = [0, True]
   ...:     def inner(n):
   ...:         bisect.insort(l, n)
   ...:         info[0] += 1
   ...:         info[1] = not info[1]
   ...:         median = info[0] // 2
   ...:         if info[1]:
   ...:             return (l[median] + l[median - 1]) / 2
   ...:         else:
   ...:             return l[median]
   ...:     return inner
   ...: df = pd.DataFrame({'a': range(20)})"""

In [3]: timeit.timeit("df['cummedian'] = df['a'].apply(cummedian())",setup=setup,number=100000)
Out[3]: 27.11604686321956

In [4]: timeit.timeit("df['expanding'] = df['a'].expanding().median()",setup=setup,number=100000)
Out[4]: 48.457676260100335

In [5]: 48.4576/27.116
Out[5]: 1.7870482372031273

【讨论】:

  • 我对你的回答投了反对票,因为你的帖子有误导性的时间安排。
  • 以后,请在 large 数据上测试您的函数,然后再声称您的函数更快。如果您在多列上执行扩展中位数,这会变得更慢,因为您的函数不会以任何方式矢量化,您必须线性应用它们。
猜你喜欢
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-09
  • 1970-01-01
相关资源
最近更新 更多