Построение кумулятивного графика времени python

Скажем, у меня есть список дат, и мы знаем, что каждое datetime является записанным временем происходящего.

Возможно ли, что в matplotlib будет отображаться частота этого события, происходящего со временем, показывая эти данные в совокупном графике (так что каждая точка больше или равна всем пунктам, которые были до нее), без предварительной обработки этого списка? (например, передача объектов datetime непосредственно к некоторой замечательной функции matplotlib)

Или мне нужно включить этот список datetimes в список элементов словаря, например:

{"year": 1998, "month": 12, "date": 15, "events": 92}

а затем сгенерировать граф из этого списка?

Ответы

Ответ 1

Это должно сработать для вас:

counts = arange(0, len(list_of_dates))
plot(list_of_dates, counts)

Конечно, вы можете указать любой из обычных вариантов вызова plot, чтобы график выглядел так, как вы хотите. (Я укажу, что matplotlib очень разбирается в обработке дат и времени.)

Другим вариантом может быть функция hist - у нее есть опция 'cumulative = True', которая может быть полезна. Вы можете создать кумулятивную гистограмму, показывающую количество событий, которые произошли в любой заданной дате, примерно так:

from pyplot import hist
from matplotlib.dates import date2num
hist(date2num(list_of_dates), cumulative=True)

Но это создает гистограмму, которая может быть не совсем то, что вы ищете, и в любом случае правильное отображение меток даты на горизонтальной оси, вероятно, потребует некоторой подделки.

EDIT: Я понимаю, что то, что вы действительно хотите, это одно очко (или бара) за дату, при этом соответствующее значение y является числом событий, которые произошли до (и включая?) этой даты. В этом случае я предлагаю сделать что-то вроде этого:

grouped_dates = [[d, len(list(g))] for d,g in itertools.groupby(list_of_dates, lambda k: k.date())]
dates, counts = grouped_dates.transpose()
counts = counts.cumsum()
step(dates, counts)

Функция groupby из модуля itertools создаст вид данных, который вы ищете: только один экземпляр каждой даты, сопровождаемый списком (фактически итератором) всех datetime объекты, которые имеют эту дату. Как было предложено Jouni в комментариях, функция step даст график, который увеличивается в каждый день, когда произошли события, поэтому я бы предложил использовать это вместо plot.

(подсказка о шляпе для EOL для напоминания мне о cumsum)

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

from matplotlib.dates import drange, num2date
date_dict = dict((d, len(list(g))) for d,g in itertools.groupby(list_of_dates, lambda k: k.date()))
dates = num2date(drange(min(list_of_dates).date(), max(list_of_dates).date() + timedelta(1), timedelta(1)))
counts = asarray([date_dict.get(d.date(), 0) for d in dates]).cumsum()
step(dates, counts)

Я не думаю, что это действительно повлияет на график, созданный функцией step.

Ответ 2

Итак, вы начинаете с списка дат, которые вы хотите использовать для гистограммы:

from datetime import  datetime
list_of_datetime_datetime_objects = [datetime(2010, 6, 14), datetime(1974, 2, 8), datetime(1974, 2, 8)]

Matplotlib позволяет вам преобразовать объект datetime.datetime в простое число, как сказал Дэвид:

from matplotlib.dates import date2num, num2date
num_dates = [date2num(d) for d in list_of_datetime_datetime_objects]

Затем вы можете вычислить гистограмму ваших данных (посмотрите NumPy histogram docs для получения дополнительных параметров (количество ящиков и т.д.)):

import numpy
histo = numpy.histogram(num_dates)

Поскольку вы хотите получить накопленную гистограмму, вы добавляете отдельные счеты вместе:

cumulative_histo_counts = histo[0].cumsum()

Графику гистограммы потребуется размер бункера:

from matplotlib import pyplot

Затем вы можете построить совокупную гистограмму:

bin_size = histo[1][1]-histo[1][0]
pyplot.bar(histo[1][:-1], cumulative_histo_counts, width=bin_size)

В качестве альтернативы вам может понадобиться кривая вместо гистограммы:

# pyplot.plot(histo[1][1:], cumulative_histo_counts)

Если вам нужны даты на оси x вместо цифр, вы можете преобразовать числа обратно в даты и попросить matplotlib использовать строки даты как тики, а не числа:

from matplotlib import ticker

# The format for the x axis is set to the chosen string, as defined from a numerical date:
pyplot.gca().xaxis.set_major_formatter(ticker.FuncFormatter(lambda numdate, _: num2date(numdate).strftime('%Y-%d-%m')))
# The formatting proper is done:
pyplot.gcf().autofmt_xdate()
# To show the result:
pyplot.show()  # or draw(), if you don't want to block

Здесь gca() и gcf() возвращают текущую ось и рисунок соответственно.

Конечно, вы можете адаптировать способ отображения дат в вызове strftime() выше.

Чтобы выйти за пределы вашего вопроса, я хотел бы упомянуть, что Галерея Matplotlib - очень хороший источник информации: вы можете быстро найти вам нужно просто найти изображения, которые выглядят так, как вы пытаетесь сделать, и посмотреть на их исходный код.

Пример накопительной кривой с метками datetime

Ответ 3

Я просто использую директора диаграмм из передовой разработки программного обеспечения. На самом деле легко справляться особенно с датами. У них также много примеров на python.