有没有一种Pythonic的方法来查找数组中特定元组元素的平均值?

24

我希望将这段代码写成Pythonic风格。我的实际数组比这个例子大得多。

(5+10+20+3+2) / 5

print(np.mean(array, key=lambda x: x[1])) TypeError: mean() got an unexpected keyword argument 'key'

array = [('a', 5) , ('b', 10), ('c', 20), ('d', 3), ('e', 2)]

sum = 0
for i in range(len(array)):
    sum = sum + array[i][1]

average = sum / len(array)
print(average)

import numpy as np
print(np.mean(array,key=lambda x:x[1]))

如何避免这种情况?我想使用第二个例子。

我正在使用Python 3.7

8个回答

28
如果您使用的是Python 3.4或更高版本,则可以使用statistics模块:
from statistics import mean

average = mean(value[1] for value in array)

如果你使用的是Python 3.4以下版本:

average = sum(value[1] for value in array) / len(array)

这些解决方案都使用了Python中的一种很好的特性,即生成器表达式。循环

value[1] for value in array

以及时且内存高效的方式创建新序列。请参阅PEP 289 -- Generator Expressions

如果您正在使用Python 2并且正在对整数求和,则会进行整数除法,结果将被截断,例如:

>>> 25 / 4
6

>>> 25 / float(4)
6.25

为了确保我们不进行整数除法,我们可以将sum的起始值设置为float0.0。然而,这也意味着我们必须使用括号显式地表示生成器表达式,否则会出现语法错误,并且如评论中所指出的那样,它不太美观。
average = sum((value[1] for value in array), 0.0) / len(array)

最好使用math模块中的fsum函数,它将返回一个float类型的值。
from math import fsum

average = fsum(value[1] for value in array) / len(array)

我意识到有更好的方法来处理Python 2代码。sum函数接受一个起始值参数。如果你传递0.0给它,那么分子将始终是浮点数,不用担心。此外,在math模块中还有一个函数,fsum - Peter Wood
5
我认为使用float强制转换的方式比传递奇怪的0.0值参数更加易于理解,但两种方式的结果相同。 - ruohola
@ruohola 我认为在Python 2中使用fsum可能是最好的选择。 - Peter Wood
1
你不能使用 from __future__ import division 吗? - DanielSank
@DanielSank 是的,那是另一个选项。如果你正在对浮点数求和,使用 fsum 的另一个优点是它会跟踪部分和,这弥补了浮点表示中精度不足的问题。因此,如果我们继续使用 fsum,就根本不需要考虑整数除法,而且通常也是更好的解决方案。请参见我的答案关于 Kahan Summation 在 [tag:c++] 中。 - Peter Wood
显示剩余2条评论

3

如果你确实想使用numpy,请将其转换为numpy.array并使用numpy索引选择所需的轴:

import numpy as np

array = np.array([('a', 5) , ('b', 10), ('c', 20), ('d', 3), ('e', 2)])
print(array[:,1].astype(float).mean())
# 8.0

需要转换成数字类型,因为原始数组既包含字符串又包含数字,因此类型为object。在这种情况下,可以使用floatint,没有区别。


3

如果你希望更像高尔夫球一样的解决方案,你可以使用原生Python转置数组,获取仅包含数字的列表,并计算平均值,具体代码如下:

sum(zip(*array)[1])/len(array)

2
您可以简单地使用:

最初的回答:

print(sum(tup[1] for tup in array) / len(array))

最初的回答:
或者对于Python 2:
print(sum(tup[1] for tup in array) / float(len(array)))

对于Python 2,更简洁的说法是:

最初的回答

from math import fsum

print(fsum(tup[1] for tup in array) / len(array))

由于这是Python 3,只需使用statistics.mean - Peter Wood

2

With pure Python:

from operator import itemgetter

acc = 0
count = 0

for value in map(itemgetter(1), array):
    acc += value
    count += 1

mean = acc / count

如果你的数据太大,无法放入内存作为一个list的话,迭代方法可能更好。如果可以的话,建议采用声明式方法:

最初的回答:

如果你有很大的数据并且无法将其全部读入内存中(因为你说它很大),那么迭代方法可能更适合。如果可以的话,优先选择声明式方法。

data = [sub[1] for sub in array]
mean = sum(data) / len(data)

如果您愿意使用numpy,我认为以下代码更清晰:


a = np.array(array)

mean = a[:, 1].astype(int).mean()

2
你可以使用map代替列表推导式。"Original Answer"的意思是"最初的回答"。
sum(map(lambda x:int(x[1]), array)) / len(array)

最初的回答:使用Python2.X时,可以使用reduce而不是functools.reduce,或者使用functools.reduce
import functools
functools.reduce(lambda acc, y: acc + y[1], array, 0) / len(array)

第一个出现了错误:"int" 对象不可被调用。 - Sevval Kahraman
@ŞevvalKahraman 如果数组如你所示定义 - 第一个会给出8.0(在相同版本上测试和验证)。因此,要么您使用的数组在某个地方具有不同的值,要么您打错了。 - LinkBerest
x[1]已经是整数了,为什么还需要调用int()函数? - Barmar
使用Lambda表达式比生成器推导式慢30%。但是,如果您喜欢map,我建议使用operator.itemgetter(1)代替lambda。 - Mateen Ulhaq
同样地,functools.reduce 比生成器推导式和 sum 慢了72%。 - Mateen Ulhaq

0

只需使用列表的总和和元素数量来计算平均值。

array = [('a', 5) , ('b', 10), ('c', 20), ('d', 3), ('e', 2)]
avg = float(sum(value[1] for value in array)) / float(len(array))
print(avg)
#8.0

0
你可以使用 mapnp.mean(list(map(lambda x: x[1], array)))


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