绘制 python 日期时间的累积图

Posted

技术标签:

【中文标题】绘制 python 日期时间的累积图【英文标题】:Plotting a cumulative graph of python datetimes 【发布时间】:2011-03-03 07:50:34 【问题描述】:

假设我有一个日期时间列表,我们知道每个日期时间都是事件发生的记录时间。

是否可以在 matplotlib 中绘制此事件随时间发生的频率,在累积图中显示此数据(以便每个点大于或等于之前的所有点),而无需预处理此列表? (例如,将 datetime 对象直接传递给一些精彩的 matplotlib 函数)

或者我需要把这个日期时间列表变成一个字典项目列表,比如:

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

然后从这个列表中生成一个图表?

【问题讨论】:

【参考方案1】:

我只是使用高级软件工程的图表导向器。真的很容易处理,尤其是日期。他们在 python 中也有很多例子。

【讨论】:

不过,它有点贵,而且我无法想象它比 Python 容易得多。 (嗯,我想容易是主观的,所以这只是我的看法)【参考方案2】:

这应该适合你:

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

您当然可以为plot 调用提供任何常用选项,以使图表看起来像您想要的那样。 (我会指出 matplotlib 非常擅长处理日期和时间。)

另一个选项是hist function - 它有一个可能有用的选项“cumulative=True”。您可以创建一个累积直方图,显示截至任何给定日期已发生的事件数量,如下所示:

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

但这会生成一个条形图,这可能不是您要查找的内容,并且无论如何要正确显示水平轴上的日期标签可能需要一些捏造。

编辑:我感觉您真正想要的是每个日期一个点(或小节),相应的 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)

itertools 模块中的groupby 函数将生成您要查找的数据类型:每个日期只有一个实例,并附有所有datetime 的列表(实际上是一个迭代器)具有该日期的对象。正如 Jouni 在 cmets 中所建议的那样,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 函数产生的情节产生真正的影响。

【讨论】:

这个 arange() 方法似乎并没有考虑一天发生的事件的次数,不过.. 我似乎只是得到了一系列升序数字。例如pastebin.ca/1882575也许我没有以最清楚的方式问我最初的问题.. @ventolin: arange() 与 Python 的内置函数 range() 相同,只是它返回的是 NumPy 数组而不是 Python 列表。它不应该考虑有关您的事件的任何内容。您对问题的措辞暗示该列表包含一个 datetime 对象,用于每个事件的发生,我推断您希望每个事件在图表上都有一个点。如果不是这样,请澄清一下,我可以相应地调整我的答案。 +1 表示 matplotlib 的 date2numhist 中的 cumulative 选项。 @David:啊哈,抱歉我不清楚。我提到的字典表示可能会让事情变得清晰:我需要的是 Y 轴上的事件数量和 X 轴上的时间(以固定的每日间隔)的图表。一天可能发生 50 个事件,第二天可能发生 2 个,依此类推,我需要这些事件随时间推移的累积图。现在阅读 EOL 的回复... 试试“step”而不是“plot”?【参考方案3】:

因此,您从要绘制直方图的日期列表开始:

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

Matplotlib 允许您将 datetime.datetime 对象转换为简单的数字,正如 David 所提到的:

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

然后您可以计算数据的直方图(查看NumPy histogram docs for more options (number of bins, etc.)):

import numpy
histo = numpy.histogram(num_dates)

由于您需要累积直方图,因此您可以将各个计数相加:

cumulative_histo_counts = histo[0].cumsum()

直方图需要 bin 大小:

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's gallery 是一个非常好的信息来源:您通常可以通过查找看起来像您正在尝试做的图像来快速找到您需要的内容,并且查看他们的源代码。

【讨论】:

试试这个,我得到paste.pocoo.org/show/225396 ...这是因为处理后,X轴上的点数与Y轴上的点数不一样吗?还是我离题了? 你是对的。我更新了答案中的代码,它可以在我的机器上运行。

以上是关于绘制 python 日期时间的累积图的主要内容,如果未能解决你的问题,请参考以下文章

[星期维度]日志数据提取事件关键词,解析对应日期的星期计数,matplotlib绘制统计图,python

[星期维度]日志数据提取事件关键词,解析对应日期的星期计数,matplotlib绘制统计图,python

如何使用 Python Pandas 绘制堆叠事件持续时间(甘特图)

根据星期时间统计日期总量,绘制matplotlib,pandas,Python

根据星期时间统计日期总量,绘制matplotlib,pandas,Python

绘制水平堆积条形图不适用于日期中的 x 轴