Я отметил этот вопрос с помощью poisson
, так как не уверен, будет ли он полезен в этом случае.
Мне нужно создать дистрибутив (возможно, отформатированный как изображение в конце) из списка данных.
Например:
data = [1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 10, 10, 10, 22, 30, 30, 35, 46, 58, 59, 59]
чтобы данные могли использоваться для создания визуального распределения. Я мог бы, например, в этом случае сказать, что диапазоны находятся в 10, и в каждом диапазоне должно быть не менее 3 элементов.
В этом примере данные, я ожидаю, что результат будет аналогичен
ditribution = [1, 2, 4, 6]
так как у меня есть > 3 элемента в диапазонах 0-9, 10-19, 30-39 и 50-59. Используя этот результат, я мог бы создать изображение, в котором сегменты сегментированы (более темный цвет), которые существуют в моем конечном распределении. Пример типа изображения, который я пытаюсь создать, можно увидеть ниже и был бы создан с гораздо большим количеством данных. Игнорируйте синюю линию на данный момент.
Я знаю, как сделать это методом грубой силы, итерации по каждому элементу списка и выполнению моих вычислений. Но мой набор данных может иметь сотни тысяч или даже миллионы чисел. Мой диапазон (10) и мое необходимое количество предметов (3), вероятно, будут намного больше в реальном мире.
Спасибо за любую помощь.
Если data
всегда сортируется, можно использовать компактный подход:
import itertools as it
d = [k+1 for k, L in
((k, len(list(g))) for k, g in it.groupby(data,key=lambda x:x//10))
if L>=3]
Если data
не сортируется или если вы не знаете, используйте sorted(data)
в качестве первого аргумента для itertools.groupby
, а не только data
.
Если вы предпочитаете менее плотный/компактный подход, вы можете, конечно, расширить это, например. в:
def divby10(x): return x//10
distribution = []
for k, g in it.groupby(data, key=divby10):
L = len(list(g))
if L < 3: continue
distribution.append(k+1)
В любом случае механизм состоит в том, что groupby
сначала применяет вызываемый, передаваемый как key=
к каждому элементу в итерабельном переданном в качестве первого аргумента, для получения "ключа" каждого элемента; для каждой последовательной группы элементов, имеющих один и тот же "ключ", groupby
дает кортеж с двумя элементами: значением ключа и итерированием по всем элементам в указанной группе.
Здесь ключ получается путем деления элемента на 10 (с усечением); len(list(g))
- количество последовательных элементов с этим "ключом". Поскольку элементы должны быть последовательными, вам нужны данные для сортировки (и проще их сортировать, чем сортировать по значению, деленное на 10 с усечением "; -).
Так как data
может быть очень длинным, вам может понадобиться использовать numpy. Он предоставляет множество полезных функций для численной работы, для хранения data
в массиве numpy требуется меньше памяти, чем список Python [*], и поскольку многие из функций numpy вызывают функции C под капотом, вы можете получить некоторую прирост скорости:
import numpy as np
data = np.array([1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 10, 10, 10, 22, 30, 30, 35, 46, 58, 59, 59])
hist,bins=np.histogram(data,bins=np.linspace(0,60,7))
print(hist)
# [11 3 1 3 1 3]
distribution=np.where(hist>=3)[0]+1
print(distribution)
# [1 2 4 6]
[*] - Примечание: В приведенном выше коде был сформирован список Python в процессе определения data
. Таким образом, максимальная потребность в памяти здесь больше, чем если бы вы использовали список Python. Однако память должна быть освобождена, если нет других ссылок на список Python. В качестве альтернативы, если данные хранятся на диске, numpy.loadtxt
можно использовать для чтения непосредственно в массив numpy.
Это звучит как работа для некоторой формы гистограммы. Для этого не требуется предварительный сбор. Я обсуждаю использование варианта сортировки ведра для группировки близлежащих элементов здесь, хотя вам необходимо настроить этот алгоритм в соответствии с вашими потребностями. Обратите внимание, что вам не нужно хранить номера в ведрах, чтобы сформировать гистограмму
numpy
. Вы, вероятно, знаете лучше, хотя :)