Я хочу написать этот код как 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






Вы можете просто использовать:
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))
Вы можете использовать map:
np.mean(list(map(lambda x: x[1], array)))
С чистым 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()
Если вы используете 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 -- Выражения генератора.
Если вы используете Python 2 и суммируете целые числа, у нас будет целочисленное деление, которое усекает результат, например:
>>> 25 / 4
6
>>> 25 / float(4)
6.25
Чтобы убедиться, что у нас нет целочисленного деления, мы можем установить начальное значение sum равным float значению 0.0. Однако это также означает, что мы должны сделать выражение генератора явным с круглыми скобками, иначе это синтаксическая ошибка, и это менее красиво, как отмечено в комментариях:
average = sum((value[1] for value in array), 0.0) / len(array)
Вероятно, лучше всего использовать fsum из модуля math, который вернет float:
from math import fsum
average = fsum(value[1] for value in array) / len(array)
Я понял, что есть лучшие способы сделать код Python 2. sum принимает аргумент для начального значения. Если вы передадите 0.0 ему, то числитель всегда будет с плавающей запятой, не о чем беспокоиться. Кроме того, в модуле math есть функция fsum.
Я бы сказал, что способ приведения float немного более понятен, чем передача странного аргумента значения 0.0 для sum.
@ruohola Я думаю, что использование fsum, вероятно, лучше всего подходит для Python 2.
Не можешь from __future__ import division?
@DanielSank да, это еще один вариант. Еще одно преимущество использования fsum, если вы суммируете числа с плавающей запятой, заключается в том, что он отслеживает частичные суммы, что компенсирует отсутствие точности в представлении с плавающей запятой. Итак, если мы продолжим использовать fsum, нам вообще не нужно думать о целочисленном делении, и, как правило, это лучшее решение. См. мой ответ о Суммирование Кахана в С++.
Я не понимаю: «Однако это также означает, что мы должны сделать цикл по значениям в массиве в выражение понимания» ... вы имеете в виду добавление явных круглых скобок, которые преобразуют выражение в genexp? Поскольку все предыдущие варианты вашего решения используют genexps, только без явных скобок, потому что это не нужно, если у функции нет других аргументов.
@ cs95 да, я это и имел в виду. Я попытался улучшить ответ, спасибо.
Просто найдите среднее значение, используя сумму и количество элементов списка.
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
вы можете использовать map вместо понимания списка
sum(map(lambda x:int(x[1]), array)) / len(array)
или functools.reduce (если вы используете Python2.X, просто reduce, а не functools.reduce)
import functools
functools.reduce(lambda acc, y: acc + y[1], array, 0) / len(array)
первый выдает эту ошибку: объект 'int' не вызывается
@ŞevvalKahraman, если массив определен, как показано в вашем вопросе, первый дает 8.0 (проверено и проверено на той же версии). Таким образом, либо используемый вами массив имеет другое значение, либо вы сделали опечатку.
x[1] уже является целым числом, зачем вам вызывать int()?
Использование лямбды на 30% медленнее, чем понимание генератора. Но если вы предпочитаете map, я рекомендую использовать operator.itemgetter(1) вместо лямбда.
Точно так же functools.reduce на 72% медленнее, чем понимание генератора и sum.
Если вы хотите использовать 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. В этом случае вы можете использовать float или int, это не имеет значения.
Если вы открыты для более похожих на гольф решений, вы можете транспонировать свой массив с помощью vanilla python, получить список только чисел и вычислить среднее значение с помощью
sum(zip(*array)[1])/len(array)
Поскольку это Python 3, просто используйте
statistics.mean.