Построение кумулятивного графика времени 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.