Сколько данных в интервале? - PullRequest
1 голос
/ 09 июля 2019

У меня есть объект списка, я хочу знать, сколько чисел в определенном интервале? Код выглядит следующим образом:

a = [1, 7, 4, 7, 4, 8, 5, 2, 17, 8, 3, 12, 9, 6, 28]
interval = 3
a = list(map(lambda x:int(x/interval),a))
for i in range(min(a),max(a)+1):
    print(i*interval,(i+1)*interval,':',a.count(i))

выход

0 3 : 2
3 6 : 4
6 9 : 5
9 12 : 1
12 15 : 1
15 18 : 1
18 21 : 0
21 24 : 0
24 27 : 0
27 30 : 1

Есть ли простой способ получить эту информацию? Чем проще, тем лучше

Ответы [ 3 ]

3 голосов
/ 09 июля 2019

Теперь, когда мы говорим о производительности, я хотел бы предложить свое решение numpy с использованием bincount :

import numpy as np

interval = 3
a = [1, 7, 4, 7, 4, 8, 5, 2, 17, 8, 3, 12, 9, 6, 28]
l = max(a) // interval + 1
b = np.bincount(a, minlength=l*interval).reshape((l,interval)).sum(axis=1)

(minlength isнеобходимо просто иметь возможность изменить форму, если max(a) не кратен интервалу)

С метками, взятыми из ответа Эрфана, мы получаем:

rnge = range(0, max(a) + interval + 1, interval)
lables = [f'[{i}-{j})' for i, j in zip(rnge[:-1], rnge[1:])]

for l,b in zip(lables,b):
    print(l,b)

[0-3) 2
[3-6) 4
[6-9) 5
[9-12) 1
[12-15) 1
[15-18) 1
[18-21) 0
[21-24) 0
[24-27) 0
[27-30) 1

Это намного быстрее, чемPandas Solution.

Сравнение производительности и масштабирования

Чтобы оценить возможности масштабирования, я просто заменил a = [1, ..., 28] * n и рассчитал время выполнения (без импорта и печати) для n = 1, 10, 100, 1K, 10K и 100K:

enter image description here

(python 3.7.3 onwin32 / pandas 0.24.2 / numpy 1.16.2)

2 голосов
/ 09 июля 2019

Решение Pandas с pd.cut и groupby

s = pd.Series(a)
bins = pd.cut(s, range(0, s.max() + interval, interval), right=False)
s.groupby(bins).count()
[0, 3)      2
[3, 6)      4
[6, 9)      5
[9, 12)     1
[12, 15)    1
[15, 18)    1
[18, 21)    0
[21, 24)    0
[24, 27)    0
[27, 30)    1
dtype: int64

Чтобы получить более чистые результаты по бункерам, мы можем использовать этот метод из связанного ответа:

s = pd.Series(a)
rnge = range(0, s.max() + interval, interval)
labels = [f'{i}-{j}' for i, j in zip(rnge[:-1], rnge[1:])]
bins = pd.cut(s, range(0, s.max() + interval, interval), right=False, labels=labels)
s.groupby(bins).count()
0-3      2
3-6      4
6-9      5
9-12     1
12-15    1
15-18    1
18-21    0
21-24    0
24-27    0
27-30    1
dtype: int64
1 голос
/ 09 июля 2019

Вы можете сделать это в одной строке, используя словарь:

a = [1, 7, 4, 7, 4, 8, 5, 2, 17, 8, 3, 12, 9, 6, 28]

{"[{};{}[".format(x, x+3) : len( [y  for y in a if y >= x and y < x+3] ) 
 for x in range(min(a), max(a), 3)}

Вывод:

{'[1;4[': 3,
 '[4;7[': 4,
 '[7;10[': 5,
 '[10;13[': 1,
 '[13;16[': 0,
 '[16;19[': 1,
 '[19;22[': 0,
 '[22;25[': 0,
 '[25;28[': 0}

Сравнение производительности:

Решение Pandas с pd.cut и groupby: 8,51 мс ± 32 мкс на цикл (среднее ± стандартное отклонение из 7 циклов, по 100 циклов каждый)

Понимание словаря: 19,7мкс ± 37,1 нс на цикл (среднее ± стандартное отклонение из 7 циклов, по 100000 циклов каждый)

Использование np.bincount: 22,4 мкс ± 263 нс на цикл (среднее± стандартное отклонение от 7 прогонов, 10000 петель каждый)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...