我正在使用 Pandas 和 Matlplotlib 在 Python 中绘制聚合数据。 我的轴自定义命令失败,因为我正在调用两个类似函数中的哪一个来制作条形图。工作案例例如:
import datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
def format_x_date_month_day(ax):
days = mdates.DayLocator()
months = mdates.MonthLocator() # every month
dayFmt = mdates.DateFormatter('%D')
monthFmt = mdates.DateFormatter('%Y-%m')
ax.figure.autofmt_xdate()
ax.xaxis.set_major_locator(months)
ax.xaxis.set_major_formatter(monthFmt)
ax.xaxis.set_minor_locator(days)
span_days = 90
start = pd.to_datetime("1-1-2012")
idx = pd.date_range(start, periods=span_days).tolist()
df=pd.DataFrame(index=idx, data={'A':np.random.random(span_days), 'B':np.random.random(span_days)})
plt.close('all')
fig, ax = plt.subplots(1)
ax.bar(df.index, df.A) # loop over columns here to do stacked plot
format_x_date_month_day(ax)
plt.show()
(参见 matplotlib.org 循环创建堆叠条形图的示例。)这给了我们
另一种应该有效并且更容易的方法是使用df.plot.bar(ax=ax, stacked=True)
,但是它不接受日期使用 mdates
的轴格式:
plt.close('all')
fig, ax = plt.subplots(1)
df.plot.bar(ax=ax, stacked=True)
format_x_date_month_day(ax)
plt.show()
mdates
和 ax.figure.autofmt_xdate()
如何与 df.plot.bar
配合使用?
最佳答案
pandas 中的条形图旨在比较类别,而不是显示时间序列或其他类型的连续变量,如 in the docstring 所述:
A bar plot shows comparisons among discrete categories. One axis of the plot shows the specific categories being compared, and the other axis represents a measured value.
这就是为什么无论 x 变量的数据类型如何,pandas 条形图的 x 轴刻度都是从零开始的整数。当使用 matplotlib 创建相同的条形图时,x 轴的比例由 matplotlib 日期数字组成,因此 matplotlib.dates
的刻度定位器和格式化程序模块 (mdates) 可以按预期使用。
为了能够将 pandas 条形图与 mdates 一起使用,您需要将条形沿 x 轴移动到与 matplotlib 日期数字匹配的位置。这要归功于 mdates.date2num
功能。以下示例基于您提供的代码进行了一些修改:示例数据集包含 3 个变量,时间序列限制为 45 天,刻度格式已根据我的喜好进行了调整(并且未包装为函数)。
此示例适用于任意数量的变量(有或没有 NaN)以及传递给 pandas 绘图函数的任何条形宽度:
import numpy as np # v 1.19.2
import pandas as pd # v 1.1.3
import matplotlib.dates as mdates # v 3.3.2
# Create random dataset
rng = np.random.default_rng(seed=1) # random number generator
nperiods = 45
nvar = 3
idx = pd.date_range('2012-01-01', periods=nperiods, freq='D')
df = pd.DataFrame(rng.integers(11, size=(idx.size, nvar)),
index=idx, columns=list('ABC'))
# Draw pandas stacked bar chart
ax = df.plot(kind='bar', stacked=True, figsize=(10,5))
# Compute width of bars in matplotlib date units
pandas_width = ax.patches[0].get_width() # the default bar width is 0.5
mdates_x0 = mdates.date2num(df.index[0])
mdates_x1 = mdates.date2num(df.index[1])
mdates_width_default = (mdates_x1-mdates_x0)/2
mdates_width = pandas_width*mdates_width_default/0.5 # rule of three conversion
# Compute new x values for bars in matplotlib date units, adjusting the
# positions according to the bar width
mdates_x = mdates.date2num(df.index) - mdates_width/2
nvar = len(ax.get_legend_handles_labels()[1])
mdates_x_patches = np.ravel(nvar*[mdates_x])
# Set bars to new x positions: this loop works fine with NaN values as
# well because in bar plot NaNs are drawn with a rectangle of 0 height
# located at the foot of the bar, you can verify this with patch.get_bbox()
for patch, new_x in zip(ax.patches, mdates_x_patches):
patch.set_x(new_x)
patch.set_width(mdates_width)
# Set major and minor date tick locators
months = mdates.MonthLocator()
days = mdates.DayLocator(bymonthday=np.arange(31, step=3))
ax.xaxis.set_major_locator(months)
ax.xaxis.set_minor_locator(days)
# Set major date tick formatter
month_fmt = mdates.DateFormatter('\n%b\n%Y')
day_fmt = mdates.DateFormatter('%d')
ax.xaxis.set_major_formatter(month_fmt)
ax.xaxis.set_minor_formatter(day_fmt)
# Shift the plot frame to where the bars are now located
xmin = min(mdates_x) - mdates_width
xmax = max(mdates_x) + 2*mdates_width
ax.set_xlim(xmin, xmax)
# Adjust tick label format last, else it may produce unexpected results
ax.figure.autofmt_xdate(rotation=0, ha='center')
由您决定这是否比使用 matplotlib 从头开始绘制堆叠条更方便。
可以稍微修改此解决方案,以根据任何时间频率为时间序列显示适当的刻度标签。下面是一个使用分钟频率、自定义条形宽度和自动日期刻度定位器和格式化程序的示例。仅显示新的/修改的代码行:
import matplotlib.ticker as mtick
#...
idx = pd.date_range('2012-01-01 12', periods=nperiods, freq='T')
#...
ax = df.plot(kind='bar', stacked=True, figsize=(10,5), width=0.3)
#...
# Set adaptive tick locators and major tick formatter
maj_loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(maj_loc)
min_loc = mtick.FixedLocator(mdates_x + mdates_width/2)
ax.xaxis.set_minor_locator(min_loc) # draw minor tick under each bar
fmt = mdates.ConciseDateFormatter(maj_loc)
ax.xaxis.set_major_formatter(fmt)
#...
您可能会注意到刻度线通常与条形图没有很好地对齐。将图形元素放在一起时,matplotlib 似乎存在一些问题。我发现这通常只有在绘制比有用的细条时才会引人注意。您可以通过运行 ax.get_xticks()
并将其与 patch.get_bbox()
循环 ax.patches
.
关于python - 为什么在使用 Pandas 的内置绘图调用而不是通过 Matplotlib 绘图时我的日期轴格式被破坏了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47965026/