【问题标题】:Plotting DataFrames containing HH:MM format in a single figure matplotlib在单个图形 matplotlib 中绘制包含 HH:MM 格式的 DataFrame
【发布时间】:2019-10-08 10:06:02
【问题描述】:

我的问题(写在下面的末尾)与在不同的子图中(下面的情况 1)绘制两个 DataFrame 的直方图有关,而不是在同一个图中绘制它们(下面的情况2)。以 1 小时的间隔作为分组标准绘制直方图。两个 DataFrame 都有一个列,时间为 "HH:MM" 格式。

# Defining the two DataFrames
df_in = pd.DataFrame({'time': ['12:20', '12:06', '11:30', '11:03', '10:44', '10:50', '11:52', 
                               '12:21', '9:58', '12:43','12:56', '13:27', '12:14',]})

df_out = pd.DataFrame({'time': ['19:40', '19:44', '19:21', '20:37', '20:27', '18:46', '19:42', 
                                '18:12', '19:08', '21:09', '18:37', '20:34', '20:15']})

情况 1:在不同的子图中绘制两个 DataFrame

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import FixedFormatter

fig, axes = plt.subplots(1, 2, figsize=(9, 3))

colors = ['r', 'b']
titles = ['df-in', 'df-out']

# Looping over the dataframes and plotting them in subfigures
for df, ax, c, t in zip([df_in, df_out], axes.flatten(), colors, titles):
    df['hour'] = pd.to_datetime(df['time'], format='%H:%M')
    df.set_index('hour', drop=False, inplace=True)
    df = df['hour'].groupby(pd.Grouper(freq='60Min')).count()
    df.plot(kind='bar', color=c, ax=ax)
    ticklabels = df.index.strftime('%H:%Mh')
    ax.xaxis.set_major_formatter(FixedFormatter(ticklabels))
    ax.set_title(t, fontsize=18)
plt.show()

情况 1 的输出


情况2:在同一张图中绘制两个DataFrame

fig, axes = plt.subplots(figsize=(7, 3))

# Looping over the dataframes and plotting them in subfigures
for df, c, t in zip([df_in, df_out], colors, titles):
    df['hour'] = pd.to_datetime(df['time'], format='%H:%M')
    df.set_index('hour', drop=False, inplace=True)
    df = df['hour'].groupby(pd.Grouper(freq='60Min')).count()
    df.plot(kind='bar', color=c, ax=axes)
    ticklabels = df.index.strftime('%H:%Mh')
    axes.xaxis.set_major_formatter(FixedFormatter(ticklabels))
plt.show() 

情况2的输出

在这两种情况下,字符串格式化的代码都取自this问题。如您所见,红色和蓝色直方图分别在 12:00 和 19:00 分别绘制时具有各自的最大值。但是当我将它们绘制在同一个图中时,两个直方图重叠并且最大值不在 12:00 和 19:00 h。这个问题似乎微不足道,但我不确定出了什么问题。


我的问题是:在 情况 2 中需要修改什么以使直方图很好地分开和区分(而不是重叠),因为它们清楚地集中在 12 左右:00 和 19:00 小时?欢迎任何指示和建议。

【问题讨论】:

  • 您正在绘制一个分类条形图。就 matplotlib 轴单位而言,第一个“直方图”在位置 3 处有峰值,第二个在位置 1 处。你想要一个数字轴而不是数字代表实时而不是类别吗?
  • @ImportanceOfBeingErnest:感谢您的评论。是的,我希望 x 轴充当数字轴。基本上我希望两个子图合并在同一个图上,其中两个直方图都很好地分开,x 轴从大约 7:00 到 23:00 h。我通过使用 range(24) 作为 x 轴,绘制 条形图 而不是直方图,然后将 x 轴标签重新标记为 HH:MM 来实现这一点。但这是一个丑陋的 hack,我相信有更好的直接解决方案

标签: python pandas dataframe matplotlib


【解决方案1】:

数字条形图可能如下所示:

import pandas as pd
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()
import matplotlib.pyplot as plt
from matplotlib.dates import HourLocator, DateFormatter


# Defining the two DataFrames
df_in = pd.DataFrame({'time': ['12:20', '12:06', '11:30', '11:03', '10:44', '10:50', '11:52', 
                               '12:21', '9:58', '12:43','12:56', '13:27', '12:14',]})

df_out = pd.DataFrame({'time': ['19:40', '19:44', '19:21', '20:37', '20:27', '18:46', '19:42', 
                                '18:12', '19:08', '21:09', '18:37', '20:34', '20:15']})

colors = ['r', 'b']
titles = ['df-in', 'df-out']

fig, ax = plt.subplots(figsize=(7, 3))


for df, c, t in zip([df_in, df_out], colors, titles):
    df['hour'] = pd.to_datetime(df['time'], format='%H:%M')
    df.set_index('hour', drop=False, inplace=True)
    df = df['hour'].groupby(pd.Grouper(freq='60Min')).count()
    df.index = pd.to_datetime(df.index)
    ax.bar(df.index, df.values, width=1/24/2, color=c, label=t)

ax.xaxis.set_major_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter("%H:%Mh"))
ax.set_xlim(pd.to_datetime(["1900-01-01 07:00", "1900-01-01 23:00"]))
plt.setp(ax.get_xticklabels(), rotation=90)
plt.tight_layout()
plt.show() 

【讨论】:

  • 感谢您的回答。我接受并投了赞成票。您能否评论一下register_matplotlib_converters() 在您的回答中的用法?
  • 根据 pandas 版本,在没有明确注册或注销 pandas 日期转换器时会抛出警告。请参阅该线程中的this comment 和其他人。
【解决方案2】:

你也可以使用sns的强大色调:

# convert to time
df_in.time = pd.to_datetime(df_in.time)
df_out.time = pd.to_datetime(df_out.time)

# mark the series/dataframe and join
df_in['df'] = 'df_in'
df_out['df'] = 'df_out'
df = pd.concat((df_in,df_out))

# groupby hours:
df = df.groupby(['df',df.time.dt.hour]).size().reset_index()

# plot with sns
plt.figure(figsize=(10,6))
sns.barplot(x='time', 
            y=0,
            hue='df', 
            dodge=False,
            data=df)
plt.show()

输出:


编辑:要绘制 x 轴从 7 到 23 的条形图,我们可以在绘图前reindex

df = (df.groupby(['df', df.time.dt.hour]).size()
        .reset_index(level=0).reindex(range(7,24))
        .reset_index()
     )

sns 条形图给出:

【讨论】:

  • 在 cmets OP 中提到“基本上我希望两个子图合并在同一个图上,其中两个直方图 分离良好 并且 x 轴从周围 7:00 到 23:00 小时”。
  • 感谢广的回答。但我一直在寻找 @ImportanceOfBeingErnest 的回答。
  • 我认为这里的方法原则上是好的,它只需要考虑所有可能的类别(小时)。那么也许可以在这方面进行更新?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-08
  • 2017-01-29
  • 2015-01-09
  • 1970-01-01
相关资源
最近更新 更多