【问题标题】:Matplotlib pandas Quarterly bar chart with datetime as index not WorkingMatplotlib pandas 以日期时间为索引的季度条形图不起作用
【发布时间】:2020-04-29 14:24:46
【问题描述】:

我有一个索引为datetime 的熊猫系列,我试图将其可视化, 使用条形图。我的代码如下。但我得到的图表似乎不太准确(下图)。我该如何解决?

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

np.random.seed(100)
dti = pd.date_range('2012-12-31', periods=30, freq='Q')
s2 = pd.Series(np.random.randint(100,1000,size=(30)),index=dti)
df4 = s2.to_frame(name='count')
print('\ndf4:')
print(df4)
print(type(df4))
f2 = plt.figure("Quarterly",figsize=(10,5))
ax = plt.subplot(1,1,1)
ax.bar(df4.index,df4['count'])
plt.tight_layout()
plt.show()

【问题讨论】:

  • 使条形宽度大于一天。
  • 我不确定我是否理解要进行哪些更改 @ImportanceOfBeingErnest

标签: python pandas matplotlib time-series timeserieschart


【解决方案1】:

不幸的是,matplotlib 的条形图似乎与 pandas 日期不符。

理论上,matplotlib 以天为单位表示条形宽度。但是如果你尝试ax.bar(df4.index,df4['count'], width=30) 之类的东西,你会看到带有极宽条形的情节,几乎完全填满了情节。尝试使用width,发生了一些奇怪的事情。当width 小于 2 时,它看起来像是用天表示的。但是当width 大于 2 时,它会突然跳到更宽的地方。

在我的系统(matplotlib 3.1.2、pandas 0.25.3、Windows)上,它看起来像:

一种解决方法是使用 pandas 中的条形图。这些似乎使条形图分类,每个条形图有一个刻度。 但是它们会被标记为完整的日期,包括小时、分钟和秒。您可以重新标记它们,例如:

df4.plot.bar(y='count', width=0.9, ax=ax)
plt.xticks(range(len(df4.index)),
           [t.to_pydatetime().strftime("%b '%y") for t in df4.index],
           rotation=90)

进一步调查,matplotlib 条形宽度的不一致跳跃似乎与熊猫时代的 frequency 构建有关。因此,一个解决方案可能是将日期转换为 matplotlib 日期。试试这个,是的,宽度以天为单位表示一致。

很遗憾,季度日期之间的天数并不完全相同,导致一些条形太宽,而另一些条形太窄。下一个问题的解决方案是明确计算每个柱的天数。为了在条之间获得良好的分隔,将它们的边缘绘制为白色会有所帮助。

from datetime import datetime

x = [datetime.date(t) for t in df4.index]  # convert the pandas datetime to matplotlib's
widths = [t1-t0 for t0, t1 in zip(x, x[1:])]  # time differences between dates
widths += [widths[-1]] # the very last bar didn't get a width, just repeat the last width
ax.bar(x, df4['count'], width=widths, edgecolor='white')

【讨论】:

    【解决方案2】:

    您可以通过ax.bar() 中的width 参数将条的宽度设置为大于0.8 默认值的某个值

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    np.random.seed(100)
    dti = pd.date_range('2012-12-31', periods=30, freq='Q')
    s2 = pd.Series(np.random.randint(100,1000,size=(30)),index=dti)
    df4 = s2.to_frame(name='count')
    f2 = plt.figure("Quarterly",figsize=(10,5))
    ax = plt.subplot(1,1,1)
    ax.bar(df4.index,df4['count'], width=70)
    plt.tight_layout()
    plt.show()
    

    在这种情况下,宽度被解释为以天为单位的标量。


    编辑

    出于某种原因,上述内容仅适用于旧版本的 matplotlib(已测试 2.2.3)。为了使用当前 (3.1.2) 版本,必须进行以下修改:

    # ...
    dti = pd.date_range('2012-12-31', periods=30, freq='Q')
    dti = [pd.to_datetime(t) for t in dti]
    # ...
    

    这将在设置条的宽度时产生正确的行为。

    【讨论】:

    • 当我尝试这段代码时,我得到了非常非常宽的条。首先,我运行了 matplotlib 3.1.1。升级到 3.1.2 后,同样的情况发生了。使用 Pycharm 在 Windows 上运行。
    • 我得到一个蓝色斑点方块
    • 要让它在我的系统上按预期工作,我需要在创建 dti 之后添加 dti = [pd.to_datetime(t) for t in dti]
    • @JohanC 有趣的是,这似乎仅适用于较旧的 matplotlib - 当我在 Python 2.7 和 matplotlib 2.2.3 下运行 Jupyter Notebook 时,它会在我的答案中产生输出,但当我运行时不会Python 3.6 IPython 内核与 3.1.2。
    猜你喜欢
    • 1970-01-01
    • 2020-02-08
    • 2017-08-06
    • 2020-03-24
    • 1970-01-01
    • 2018-01-24
    • 2011-04-30
    • 1970-01-01
    • 2020-02-22
    相关资源
    最近更新 更多