Pandas能够绘制日期直方图吗?

139

我已经将我的Series强制转换为dtype=datetime64[ns]的日期时间列(尽管只需要按天分辨率......不确定如何更改)。

import pandas as pd
df = pd.read_csv('somefile.csv')
column = df['date']
column = pd.to_datetime(column, coerce=True)

但绘图不起作用:

ipdb> column.plot(kind='hist')
*** TypeError: ufunc add cannot use operands with types dtype('<M8[ns]') and dtype('float64')

我想绘制一个直方图,仅显示按周、月或年计数的日期。

pandas 中肯定有方法可以做到这一点。


3
你能展示一下你所拥有的数据框(df)的样本吗? - jrjc
10个回答

225

给出这个数据框:

        date
0 2001-08-10
1 2002-08-31
2 2003-08-29
3 2006-06-21
4 2002-03-27
5 2003-07-14
6 2004-06-15
7 2003-08-14
8 2003-07-29

而且,如果还不是这种情况:

df["date"] = df["date"].astype("datetime64")

显示每个月的日期计数:

df.groupby(df["date"].dt.month).count().plot(kind="bar")

.dt 可以让您访问日期时间属性。

这将给你:

groupby date month

您可以用年、日等替换月份。

例如,如果您想区分年份和月份,只需执行:

df.groupby([df["date"].dt.year, df["date"].dt.month]).count().plot(kind="bar")

这将给出:

按日期月份年份分组


3
如果你有跨越数年的数据,所有的“一月”数据都会放在同一列中,每个月的情况也一样。 - drevicko
1
@jeanrjc 再次查看问题,我想你是对的。对于像我这样需要按年份区分的其他人,是否有一种简单的方法可以根据列数据的两个属性组合(例如:年份和日期)进行“groupby”操作? - drevicko
有没有一种方法可以准备日期,以便我可以使用seaborn.distplot()绘制日期的直方图? - panc
1
在我的PyCharm Python控制台中不起作用,有什么帮助吗? - Saurabh Yadav
1
我遇到了“重复级别名称”错误。通过添加rename()解决,例如df.groupby([df["date"].dt.year.rename('year'), df["date"].dt.month.rename('month')]).count().plot(kind="bar") - Alex
显示剩余5条评论

16

我认为重新采样(resample)可能是您正在寻找的东西。在您的情况下,可以这样做:

df.set_index('date', inplace=True)
# for '1M' for 1 month; '1W' for 1 week; check documentation on offset alias
df.resample('1M').count()

它只进行计数而不是绘图,因此您必须自己制作图表。

有关resample文档的更多详细信息,请参阅此帖子 pandas resample documentation

我遇到了与您类似的问题。希望这可以帮助您。


6
“how”已被弃用。新的语法是df.resample('1M').count() - Dan Weaver

11

所有这些答案都过于复杂,至少在使用“现代”pandas时只需要两行代码。

df.set_index('date', inplace=True)
df.resample('M').size().plot.bar()

如果你有一个带有 DatetimeIndex 的序列,那么只需要运行第二行代码。
series.resample('M').size().plot.bar() # Just counts the rows/month
or
series.resample('M').sum().plot.bar(). # Sums up the values in the series

4
似乎这只适用于 DataFrame,如果你只有一个 Series ,那么似乎不能用。你能否考虑在这种情况下添加一条注释? - David Z
在我看来,这应该是被接受的答案,它简单而且完美地工作。此外,与我尝试过的其他答案不同,它还可以正确地绘制计数为0的时间段。感谢Brian! - Jan Pisl

9

渲染的例子

输入图像描述

示例代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Create random datetime object."""

# core modules
from datetime import datetime
import random

# 3rd party modules
import pandas as pd
import matplotlib.pyplot as plt


def visualize(df, column_name='start_date', color='#494949', title=''):
    """
    Visualize a dataframe with a date column.

    Parameters
    ----------
    df : Pandas dataframe
    column_name : str
        Column to visualize
    color : str
    title : str
    """
    plt.figure(figsize=(20, 10))
    ax = (df[column_name].groupby(df[column_name].dt.hour)
                         .count()).plot(kind="bar", color=color)
    ax.set_facecolor('#eeeeee')
    ax.set_xlabel("hour of the day")
    ax.set_ylabel("count")
    ax.set_title(title)
    plt.show()


def create_random_datetime(from_date, to_date, rand_type='uniform'):
    """
    Create random date within timeframe.

    Parameters
    ----------
    from_date : datetime object
    to_date : datetime object
    rand_type : {'uniform'}

    Examples
    --------
    >>> random.seed(28041990)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(1998, 12, 13, 23, 38, 0, 121628)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(2000, 3, 19, 19, 24, 31, 193940)
    """
    delta = to_date - from_date
    if rand_type == 'uniform':
        rand = random.random()
    else:
        raise NotImplementedError('Unknown random mode \'{}\''
                                  .format(rand_type))
    return from_date + rand * delta


def create_df(n=1000):
    """Create a Pandas dataframe with datetime objects."""
    from_date = datetime(1990, 4, 28)
    to_date = datetime(2000, 12, 31)
    sales = [create_random_datetime(from_date, to_date) for _ in range(n)]
    df = pd.DataFrame({'start_date': sales})
    return df


if __name__ == '__main__':
    import doctest
    doctest.testmod()
    df = create_df()
    visualize(df)

9

以下是一个解决方案,当您只想得到预期的直方图时可以使用。这不使用groupby,而是将日期时间值转换为整数并更改绘图标签。可以通过改进使刻度标签移动到均匀位置。此外,使用此方法还可以制作核密度估计图(以及任何其他图形)。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.DataFrame({"datetime": pd.to_datetime(np.random.randint(1582800000000000000, 1583500000000000000, 100, dtype=np.int64))})
fig, ax = plt.subplots()
df["datetime"].astype(np.int64).plot.hist(ax=ax)
labels = ax.get_xticks().tolist()
labels = pd.to_datetime(labels)
ax.set_xticklabels(labels, rotation=90)
plt.show()

Datetime histogram


3
labels = pd.to_datetime(labels).strftime('%-m/%-d') 可以帮助清理这些刻度标签。 - Wassadamo
如何使用不同颜色绘制多个数据框? - Osmel Bordiés López
在 plot.hist 行上使用 for 循环,并使用参数“alpha”来减少不透明度,以便所有直方图都可见。可能需要使用“color”参数自己定义颜色。请查看 matplotlib 教程或文档以获取更多信息。 - JulianWgs

5

我能够通过以下方式解决此问题:(1)使用matplotlib进行绘图,而不是直接使用数据框,(2)使用values属性。请参见以下示例:

import matplotlib.pyplot as plt

ax = plt.gca()
ax.hist(column.values)

如果不使用values,这段代码就无法运行,但我不知道为什么它可以正常工作。


1

我也遇到了这个问题。我想,因为你正在处理日期,你希望保留时间顺序(就像我一样)。

解决方法是

import matplotlib.pyplot as plt    
counts = df['date'].value_counts(sort=False)
plt.bar(counts.index,counts)
plt.show()

请,如果有更好的方法,请说出来。
编辑: 对于上面的jean,这是一个数据样本[我从完整的数据集中随机抽样,因此是微不足道的直方图数据。]
print dates
type(dates),type(dates[0])
dates.hist()
plt.show()

输出:

0    2001-07-10
1    2002-05-31
2    2003-08-29
3    2006-06-21
4    2002-03-27
5    2003-07-14
6    2004-06-15
7    2002-01-17
Name: Date, dtype: object
<class 'pandas.core.series.Series'> <type 'datetime.date'>

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-f39e334eece0> in <module>()
      2 print dates
      3 print type(dates),type(dates[0])
----> 4 dates.hist()
      5 plt.show()

/anaconda/lib/python2.7/site-packages/pandas/tools/plotting.pyc in hist_series(self, by, ax, grid, xlabelsize, xrot, ylabelsize, yrot, figsize, bins, **kwds)
   2570         values = self.dropna().values
   2571 
-> 2572         ax.hist(values, bins=bins, **kwds)
   2573         ax.grid(grid)
   2574         axes = np.array([ax])

/anaconda/lib/python2.7/site-packages/matplotlib/axes/_axes.pyc in hist(self, x, bins, range, normed, weights, cumulative, bottom, histtype, align, orientation, rwidth, log, color, label, stacked, **kwargs)
   5620             for xi in x:
   5621                 if len(xi) > 0:
-> 5622                     xmin = min(xmin, xi.min())
   5623                     xmax = max(xmax, xi.max())
   5624             bin_range = (xmin, xmax)

TypeError: can't compare datetime.date to float

1

我认为要解决这个问题,你可以使用以下代码,它将日期类型转换为整数类型:

df['date'] = df['date'].astype(int)
df['date'] = pd.to_datetime(df['date'], unit='s')

要仅获取日期,您可以添加此代码:

pd.DatetimeIndex(df.date).normalize()
df['date'] = pd.DatetimeIndex(df.date).normalize()

2
这并没有回答如何绘制一个有序的日期时间直方图的问题。 - lollercoaster
我认为你的问题在于日期时间类型,你需要在绘图之前进行归一化处理。 - user4329078
您也可以查看此链接 - user4329078

0

随着最新版本的matplotlib,这个限制似乎已经被解除了。 现在可以使用Axes.bar来绘制时间序列图。

默认情况下,柱状图是以给定的日期为横坐标中心,宽度为0.8天。柱状图位置可以通过“align”参数进行偏移,并且宽度可以分配为标量或与横坐标列表具有相同维度的列表。

只需添加以下一行代码即可获得漂亮的日期标签,无论缩放因子如何:

plt.rcParams['date.converter'] = 'concise'

0
我曾经卡了很长时间,试图用“bar”绘制时间序列。当尝试绘制两个具有不同索引的时间序列时(例如每日和每月数据),情况变得非常奇怪。然后我重新阅读了文档,matplotlib文档明确指出bar适用于分类数据。 应该使用的绘图函数是step。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接