高效计算平均值和中位数

6

什么是在Python列表中顺序查找行的平均数和中位数的最有效方法?

例如,我的列表:

input_list = [1,2,4,6,7,8]

我希望能够生成一个输出列表,其中包含以下内容:
output_list_mean = [1,1.5,2.3,3.25,4,4.7]
output_list_median = [1,1.5,2.0,3.0,4.0,5.0]

其中均值计算如下:

  • 1 = mean(1)
  • 1.5 = mean(1,2) (即输入列表的前两个值的均值)
  • 2.3 = mean(1,2,4) (即输入列表的前三个值的均值)
  • 3.25 = mean(1,2,4,6) (即输入列表的前四个值的均值) 等等。

而中位数的计算方法如下:

  • 1 = median(1)
  • 1.5 = median(1,2) (即输入列表的前两个值的中位数)
  • 2.0 = median(1,2,4) (即输入列表的前三个值的中位数)
  • 3.0 = median(1,2,4,6) (即输入列表的前四个值的中位数) 等等。

我尝试使用以下循环来实现,但它似乎非常低效。

import numpy

input_list = [1,2,4,6,7,8]

for item in range(1,len(input_list)+1):
    print(numpy.mean(input_list[:item]))
    print(numpy.median(input_list[:item]))

相比于什么,看起来非常低效?你计时了吗?我怀疑 import numpy 占用了大部分运行时间(而且你不需要它)。 - msw
3个回答

8

如果你自己完成某些任务,特别是在中位数方面,要么需要大量的工作,要么效率非常低,但是Pandas内置了高效的函数实现,例如扩展均值的时间复杂度为O(n),扩展中位数使用跳表的时间复杂度为O(n*log(n)):

import pandas as pd
import numpy as np

input_list = [1, 2, 4, 6, 7, 8]

>>> pd.expanding_mean(np.array(input_list))
array([ 1.     ,  1.5    ,  2.33333,  3.25   ,  4.     ,  4.66667])

>>> pd.expanding_median(np.array(input_list))
array([ 1. ,  1.5,  2. ,  3. ,  4. ,  5. ])

你好!我一直在寻找像这样的解决方案来解决同样的问题。但不幸的是,由于时间的推移,我一直在苦苦挣扎。我不确定这是否有效地产生了差异,但我认为pandas模块中的实现发生了一些变化。请问您能否告诉我您的答案是否与我在此链接中找到的相匹配?我认为这是针对新版本的pandas。这是否类似于reduce被移动到itertools模块的情况? - NickS1

4
您可以使用itertools.islice来切片数组,并使用np.fromiternp.mean函数:

>>> arr=np.array([1,2,4,6,7,8])
>>> l=arr.size
>>> from itertools import islice
>>> [np.fromiter(islice(arr,0,i+1),float).mean(dtype=np.float32) for i in xrange(l)]
[1.0, 1.5, 2.3333333, 3.25, 4.0, 4.6666665]

作为替代方法,如果您想要平均值,可以使用np.cumsum来获得元素的累积和,并使用np.true_divide除以主数组:
>>> np.true_divide(np.cumsum(arr),arr)
array([ 1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5])

@hoof_hearted 欢迎!抱歉,您所说的“sequential median”是什么意思? - Mazdak
我需要找到第一个值(1),前两个值(1,2),前三个值(1,2,3)等的中位数。在这个例子中,输出将是相同的(即output_list = [1,1.5,2,2.5,3,3.5,4,4.5]),但我正在计算所有值的中位数,而不是平均值。希望这很清楚。 - hoof_hearted
@hoof_hearted 是的,这正是我的答案所做的! - Mazdak
是的,但这是因为对于这个例子,中位数和平均数是相同的。如果输入的数字不是连续的,那么中位数将会不同。我可能需要单独发布这个问题。 - hoof_hearted
1
那个 cumsum 部分很聪明! - Divakar
显示剩余3条评论

0
import numpy as np
a = np.array([1,2,4,6,7,8])

使用`numpy.meshgrid`(还有其他可行的公式)和`numpy.triu`来创建一个包含你感兴趣的值的数组。
x, y = np.meshgrid(a,a)
# y = a.repeat(len(a)).reshape(len(a), len(a))
c = np.triu(y)

>>> y
array([[1, 1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2, 2],
       [4, 4, 4, 4, 4, 4],
       [6, 6, 6, 6, 6, 6],
       [7, 7, 7, 7, 7, 7],
       [8, 8, 8, 8, 8, 8]])
>>> c
array([[1, 1, 1, 1, 1, 1],
       [0, 2, 2, 2, 2, 2],
       [0, 0, 4, 4, 4, 4],
       [0, 0, 0, 6, 6, 6],
       [0, 0, 0, 0, 7, 7],
       [0, 0, 0, 0, 0, 8]])

定义一个函数,返回所有非零值的中位数,并将其应用于您的有趣数组。
def foo(a):
    '''return the the median of the non-zero elements of a 1d array
    '''
    return np.median(a[a.nonzero()])
d = np.apply_along_axis(foo, 0, c)

>>> d
array([ 1. ,  1.5,  2. ,  3. ,  4. ,  5. ])
>>>

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