Есть ли в Python эффективный способ подсчета времени, когда массив чисел находится между определенными интервалами? количество интервалов, которые я буду использовать, может стать довольно большим

Любить:

mylist = [4,4,1,18,2,15,6,14,2,16,2,17,12,3,12,4,15,5,17]

some function(mylist, startpoints):
   # startpoints = [0,10,20]
   count values in range [0,9]
   count values in range [10-19]

output = [9,10]
4
calccrypto 1 Июн 2010 в 03:46

4 ответа

Лучший ответ

Вам придется повторить список хотя бы один раз.

Приведенное ниже решение работает с любой последовательностью / интервалом, который реализует сравнение (<, > и т. Д.) И использует bisect позволяет найти правильную точку в интервале, поэтому он очень быстрый.

Он будет работать с плавающей точкой, текстом или чем-то еще. Просто передайте последовательность и список интервалов.

from collections import defaultdict
from bisect import bisect_left

def count_intervals(sequence, intervals):
    count = defaultdict(int)
    intervals.sort()
    for item in sequence:
        pos = bisect_left(intervals, item)
        if pos == len(intervals):
            count[None] += 1
        else:
            count[intervals[pos]] += 1
    return count

data = [4,4,1,18,2,15,6,14,2,16,2,17,12,3,12,4,15,5,17]
print count_intervals(data, [10, 20])

Напечатаю

defaultdict(<type 'int'>, {10: 10, 20: 9})

Это означает, что у вас есть 10 значений <10 и 9 значений <20.

6
nosklo 1 Июн 2010 в 00:05

Вы также можете использовать комбинацию value_counts () и pd.cut (), чтобы помочь вам выполнить работу.

import pandas as pd   
mylist = [4,4,1,18,2,15,6,14,2,16,2,17,12,3,12,4,15,5,17]
split_mylist = pd.cut(mylist, [0, 9, 19]).value_counts(sort = False)
print(split_mylist)

Этот кусок кода вернет это:

(0, 10] 10 (10, 20] 9 dtype: int64

Затем вы можете использовать функцию to_list (), чтобы получить то, что вы хотите

split_mylist = split_mylist.tolist()
print(split_mylist)

Выход: [10, 9]

0
Sam Bennett 15 Июл 2019 в 16:00

Если числа являются целыми числами, как в вашем примере, представление интервалов в виде Frozensets, возможно, может быть самым быстрым (стоит попробовать). Не уверен, что интервалы гарантированно являются взаимоисключающими - если нет, то

intervals = [frozenzet(range(10)), frozenset(range(10, 20))]
counts = [0] * len(intervals)

for n in mylist:
  for i, inter in enumerate(intervals):
    if n in inter:
      counts[i] += 1

Если интервалы являются взаимоисключающими, этот код можно немного ускорить, выполнив break выход из внутреннего цикла сразу после приращения. Однако для взаимоисключающих интервалов целых чисел> = 0 есть еще более привлекательный вариант: сначала подготовьте вспомогательный индекс, например учитывая вашу структуру данных startpoints, которая может быть

indices = [sum(i > x for x in startpoints) - 1 for i in range(max(startpoints))]

А потом

counts = [0] * len(intervals)
for n in mylist:
  if 0 <= n < len(indices):
    counts[indices[n]] += 1

Это можно отрегулировать, если интервалы могут быть <0 (в этом случае все должно быть смещено на -min(startpoints).

Если «числа» могут быть произвольными числами (или decimal.Decimal и т. Д.), А не просто целыми числами, возможности оптимизации более ограничены. Это тот случай ...?

1
Alex Martelli 1 Июн 2010 в 00:00

Я не знаю, насколько большим станет ваш список, но вот другой подход.

import numpy as np
mylist = [4,4,1,18,2,15,6,14,2,16,2,17,12,3,12,4,15,5,17]
np.histogram(mylist, bins=[0,9,19])
1
P Hemans 29 Июн 2012 в 00:11