【问题标题】:bar plot with 24 hours in the x-axis with date time data of only the beginning of this sectionx 轴为 24 小时的条形图,日期时间数据仅为本节的开头
【发布时间】:2026-02-13 14:55:01
【问题描述】:

我想绘制一个条形图(或直方图,但不是其他图),使 x 值对应于时间段,但 x 轴标签是从 00:00 到 23:59,间隔为半小时。

问题出在我的数据框中:["Start Time"] 列中的值是 "datetime.time" 类型,看起来像

0    00:30:00

1    06:00:00

2    07:00:00

3    09:10:00

4    15:30:00

5    18:00:00

6    19:00:00

['Main Street Green (s)'] 是对应的值。例如:

0     NaN

1    13.5

2    25.5

3    50.5

4    55.5

5    20.5

6    38.5

我希望从 00:00 到 00:30 的条形高度为 0 从 00:30 到 06:00 的条形高度为 13.5 从 06:30 到 07:00 的酒吧高度为 25.5 ...

我看过很多线图。 https://www.programcreek.com/python/example/61484/matplotlib.dates.HourLocator 是我发现的最有价值的资源

plt.rcParams['figure.figsize'] = (8.0, 6.0)
ax = plt.subplot(111)
ax.plot(Dayton_weekday["Start Time"], Dayton_weekday['Main Street Green (s)'], color = 'blue', label="main street")
#set ticks every half an hour
ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(0,24,48))) 
#set major ticks format
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
ax.set_xlim(["00:00", "23:59"])
ax.set_ylim(bottom=0)

在我的代码中,我使用 ax.plot 那是因为如果我使用 ax.bar "ValueError: microsecond must be in 0..999999" 将被返回 所以我不知道如何使用我拥有的数据绘制条形图

【问题讨论】:

  • ax.plot.bar(Dayton_weekday['Start Time'], Daton_weekday['Main...']?
  • 那行不通。将返回错误“AttributeError: 'function' object has no attribute 'bar'”

标签: python-3.x datetime matplotlib


【解决方案1】:

这里的方法应该可以满足您的需求。

import datetime as dt
import matplotlib.pyplot as plt
import numpy as np

手动创建我们想要绘制的时间。在您的数据集中,列出的时间与上一个栏的结尾相对应。为了适应柱子超过午夜的情况,时间可能更容易与柱子的开始相对应。在下面的代码中,我修改了您的原始数据集以使用这种方法。列表中的第一次将是绘图的 x 轴上列出的第一次。

times = [dt.time(0,30,0),
         dt.time(6,0,0),
         dt.time(7,0,0),
         dt.time(9,10,0),
         dt.time(15,30,0),
         dt.time(18,0,0),
         dt.time(19,0,0)]

与在 x 轴上绘制 dt.time() 相比,绘制一天中经过的秒数可能更容易。下面我们使用列表推导将times 转换为经过的秒数。

seconds = [(x.hour * 3600 + x.minute * 60 + x.second)
           for x in times]

注意:我删除了values 开头的NaN,并在末尾添加了一个虚构的5.5,以便可以显示从 19:00 到 0:30 的条形示例。

# manually add the y-axis values
values = [13.5,
          25.5,
          50.5,
          55.5,
          20.5,
          38.5,
          5.5]

现在我们创建两个列表,用于指定在 x 轴上应放置刻度的位置,以及为每个刻度分配的标签。如果经过的秒数可以被 1800(30 分钟)整除,则该数字将用作刻度之一。 start_time 将是 times 中第一个项目经过的秒数。这是 x 轴的起点。

start_time = (times[0].hour * 3600 + times[0].minute * 60 + times[0].second)
xticks = [x + start_time for x in range(60 * 60 * 24) if x % 1800 == 0]

为了显示时间而不是经过的秒数,我们必须使用dt.timedelta() 将整数转换为dt.time。如果start_time 的值大于0,则xticks 的值至少为86400。我们必须从这些值中减去86400,以防止xticklabels 显示为one day, 0:30

def label_format(seconds):
    if seconds >= 86400:
        seconds -= 86400

    return str(dt.timedelta(seconds=seconds))

xticklabels = [label_format(x) for x in xticks]

现在我们可以创建情节了。由于我们正在创建条形宽度不等的直方图样式图表,因此我们需要一种指定条形宽度的方法。 bar_traits()可用于根据x轴起点和下一个bar的起点确定bar的起点和宽度。

def bar_traits(ix, second, seconds, start_time):
    if ix < len(seconds) - 1:
        if seconds[ix + 1] < second:
            width = 86400 + seconds[ix + 1] - second
        else:
            width = seconds[ix + 1] - second
    else:
        if start_time < second:
            width = 86400 + start_time - second
        else:
            width = start_time - second
    if start_time > second:
        second += 86400

    return second, width

使用bar_traits() 创建绘图的代码来处理整个午夜的绘图。

fig, ax = plt.subplots(figsize=(16,8))

for ix, (second, value) in enumerate(zip(seconds, values)):
    second, width = bar_traits(ix, second, seconds, start_time)
    ax.bar(second, value, width=width, align='edge', color='C0')

ax.set_xticks(xticks)
ax.set_xticklabels(xticklabels, rotation=90)

plt.show()

【讨论】:

  • 感谢您的及时回复。您的回答很棒,几乎为我的问题提供了完美的解决方案。
  • 你的回答只有一点点瑕疵。如果在 19:00 到 0:30 之间应该有值怎么办?无法计算 0:00 到 0:30 之间的条形高度,因为该值直到 pd.Series 结束时才会显示。
  • 如果您想要一个覆盖 19:00 到 0:30 的条形图,则将轴跨度从 0:00 到 23:59 可能会产生误导。这种方法会导致观众将 0:00 到 0:30 和 19:00 到 23:59 解释为两个单独的段。一种可能的方法是让 x 轴从 0:30 运行到 0:29。
  • @CSY 我更新了我的回复以说明我们想要绘制经过午夜的条形图的情况。
最近更新 更多