В поисках более эффективного / питонического способа суммировать кортежи в списке и вычислять среднее - PullRequest
0 голосов
/ 03 ноября 2018

Я пытаюсь сделать некоторые основные вычисления с данными из Интернета. По этой причине я нашел некоторый код, который извлекает начальные и конечные годы для работ Рембрандта. Сохраняет его в списке

date_list =[(work['datebegin'], work['dateend']) for work in `rembrandt2_parsed['records']]`

date_list - список кортежей с начальным и конечным годами для некоторых работ Рембрандта в Гарвардском художественном музее. Для полноты картины это выглядит так:

[(0, 0), (1648, 1648), (1637, 1647), (1626, 1636), (0, 0), (1638, 1638), (1635, 1635), (1634, 1634), (0, 0), (0, 0)]

Теперь я хочу сделать некоторые базовые вычисления, Я хочу подвести итог по этому списку кортежей и вычислить среднее число лет, когда они не равны . Я придумал решение:

datebegin =0
date_end =0
count_begin =0
count_end =0

for x, y in date_list:
    if x !=0:
        datebegin +=x
        count_begin +=1
    if y != 0:
        date_end +=y
        count_end +=1

final_date_begin = datebegin/count_begin #value = year 1636
final_date_end = date_end/count_end #value = year 1639

Но я думаю, что это можно сделать гораздо эффективнее / питоничнее. Во-первых, потому что мне кажется, что для такой простой задачи мне нужно много кода, а во-вторых, потому что мне нужно инициализировать 4 (!) Глобальных переменной, если я делаю это таким образом. Может ли кто-нибудь просветить меня и показать мне более эффективный способ решения этой проблемы?

Ответы [ 4 ]

0 голосов
/ 03 ноября 2018

В чистом Python

starts = [s for s, e in date_list for if s and e]
ends = [e for s, e in date_list for if s and e]

start_avg = sum(starts) / len(starts)
end_avg = sum(ends) / len(ends)
0 голосов
/ 03 ноября 2018

Non-numpy решение:

lst = [(0, 0), (1648, 1648), (1637, 1647), (1626, 1636), (0, 0), (1638, 1638), (1635, 1635), (1634, 1634), (0, 0), (0, 0)]

print(sum(x[0] for x in lst) / sum(x[0] != 0 for x in lst))
# 1636.3333333333333
print(sum(x[1] for x in lst) / sum(x[1] != 0 for x in lst))
# 1639.6666666666667
0 голосов
/ 03 ноября 2018

Numpy и список пониманий ваш друг здесь.

import numpy as np  
date_list = [(0, 0), (1648, 1648), (1637, 1647), (1626, 1636), (0, 0), 
             (1638, 1638), (1635, 1635), (1634, 1634), (0, 0), (0, 0)]
final_date_begin = np.mean([x for x, y in date_list if not x == 0])
final_date_end = np.mean([y for x, y in date_list if not y == 0])
0 голосов
/ 03 ноября 2018

Вы можете использовать numpy, чтобы решить это:

import numpy as np

result = list(np.ma.masked_equal(date_list, 0).mean(axis=0))

Здесь мы, таким образом, сначала сохраняем date_list в массиве, затем маскируем нулевые значения, а затем вычисляем среднее значение по первой оси.

Для ваших образцов данных мы получаем:

>>> list(np.ma.masked_equal(date_list, 0).mean(axis=0))
[1636.3333333333333, 1639.6666666666667]

Производительность : для списка, содержащего 100'000 2-кортежей, сгенерированного с помощью:

from random import randint

date_list = [(randint(0, 10), randint(0, 10)) for _ in range(100000)]

мы повторили эту функцию 1000 раз и получили:

>>> timeit(f, number=1000)
51.31010195999988

локально, это работает для 100'000 & times; 2 "матрицы" за 51,3 мс за цикл.

...