您如何调整条形宽度以匹配辅助 x 轴?

Posted

技术标签:

【中文标题】您如何调整条形宽度以匹配辅助 x 轴?【英文标题】:How do you adjust bar width to match the secondary x-axis? 【发布时间】:2022-01-09 11:46:18 【问题描述】:

如何设置条形宽度以匹配指定的天数?

我想要 4 个条形图,每个月的每个星期都有一个条形图。根据月份的不同,一周中的天数会有所不同。以下是我计算每周天数的方法:

from calendar import monthrange

def weekly_breakdown(year=2021, month=11):
    total_days = monthrange(year, month)[1]
    
    if total_days % 4 == 0: # 28 / 4 = 7
        days_per_week = (7, 7, 7, 7)
    
    elif total_days % 4 == 2: # 30 / 4 = 7.5
        days_per_week = (8, 7, 8, 7)
    
    elif total_days % 4 == 3: # 31 / 4 = 7.75
        days_per_week = (8, 8, 8, 7)
    
    # days_per_week = 'Week 1', 'Week 2', 'Week 3', 'Week 4'
    return days_per_week

这是我的脚本中实际创建情节的部分。在此示例中,我使用的是 2021 年 11 月(30 天),因此我希望第一个条形宽度跨越 01-08(8 天),第二个条形跨越 09-15(7 天),第三个条形跨越16-23(8 天),以及跨越 24-30(7 天)的最后一个柱。

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, DayLocator


# Generate data for bars.
df1 = pd.DataFrame(
    [100, 1650, 2000, 3500],
    index=['Week 1', 'Week 2', 'Week 3', 'Week 4'],
    columns=['Amount']
)

df2 = pd.DataFrame(
    [750, 1500, 2250, 3000],
    index=['Week 1', 'Week 2', 'Week 3', 'Week 4'],
    columns=['Amount']
)

# Generate line data.
days = pd.date_range(start="2021-11-01", end="2021-11-30").to_pydatetime().tolist()
daily_df = pd.DataFrame(
    list(range(100, 3100, 100)), 
    index=days, 
    columns=['Cumulative']
)

fig, ax1 = plt.subplots(figsize=(8, 6), nrows=1, ncols=1)

# Share x-axis between bar and line plot.
ax2 = ax1.twiny()

df1['Amount'].plot(kind='bar', ax=ax1, color='red')
df2['Amount'].plot(kind='bar', ax=ax1, color='white', edgecolor='black', alpha=0.5)
daily_df['Cumulative'].plot(x='Adjusted Date', kind='line', ax=ax2, marker='o')

ax1.xaxis.tick_bottom()
ax1.tick_params(axis='x', rotation=0, length=0, pad=30)
ax1.set_ylabel('Dollars')

ax2.xaxis.tick_bottom()
ax2.tick_params(axis='x', which='major', length=3)
ax2.tick_params(axis='x', which='minor', length=3)
ax2.set_xlim([daily_df.index[0], daily_df.index[-1]])
ax2.xaxis.set_major_locator(DayLocator(interval=1))
ax2.xaxis.set_major_formatter(DateFormatter("%d"))

额外问题:我在哪里可以找到有关如何使用 df.plot(kind='') 创建绘图的文档?我搜索了 matplotlib 文档,但没有成功。

【问题讨论】:

【参考方案1】:

似乎pandas 中df.plot('kind'=bar) 函数的width 参数只接受一个值而不是宽度列表。但是,您可以直接使用 matplolib 中的 bar 函数来实现您想要的。它允许您将宽度列表传递给函数。

您可以在下面找到代码:

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, DayLocator
import numpy as np

# Generate data for bars.
df1 = pd.DataFrame(
    [100, 1650, 2000, 3500],
    index=['Week 1', 'Week 2', 'Week 3', 'Week 4'],
    columns=['Amount']
)

df2 = pd.DataFrame(
    [750, 1500, 2250, 3000],
    index=['Week 1', 'Week 2', 'Week 3', 'Week 4'],
    columns=['Amount']
)

# Generate line data.
days = pd.date_range(start="2021-11-01", end="2021-11-30").to_pydatetime().tolist()
daily_df = pd.DataFrame(
    list(range(100, 3100, 100)), 
    index=days, 
    columns=['Cumulative']
)

fig, ax1 = plt.subplots(figsize=(8, 6), nrows=1, ncols=1)

# Share x-axis between bar and line plot.
ax2 = ax1.twiny()
days_per_week=weekly_breakdown(2021,11)
N_days=np.sum(days_per_week)
widths=np.array(days_per_week)-1
index_bars=[1+sum(widths[0:i]+1) for i in range(len(widths))]
ax1.bar(index_bars,df1['Amount'],color='red', width=widths,align='edge')
ax1.bar(index_bars,df2['Amount'],color='white',edgecolor='black', alpha=0.5,width=widths,align='edge')
ax1.plot(np.arange(N_days)+1,daily_df['Cumulative'],marker='o')
ax1.set_xlim([1,N_days])
ax1.set_xticks(np.arange(N_days)+1)

ax2.xaxis.tick_bottom()
ax2.tick_params(axis='x', rotation=0, length=0, pad=30)
ax2.set_xticks([1./(len(df1.index))*i+(1./(2*len(df1.index))) for i in range(len(df1.index))])
ax2.set_xticklabels(df1.index)

输出给出:

【讨论】:

这正是我想要的。谢谢你。我很早就在使用 matplotlib 条形图时遇到了一个问题,并且在找到 pandas 条形图选项后就再也没有回去过。

以上是关于您如何调整条形宽度以匹配辅助 x 轴?的主要内容,如果未能解决你的问题,请参考以下文章

调整表单宽度以匹配 SplitContainer 大小

powerbi簇状条形图,如何去掉x轴字段

如何设置输入宽度以匹配占位符文本宽度

是否有 R 函数来调整条形图的轴比例?

堆叠条形图的 Y 比例域最小值/最小值不为零; X 轴溢出

如何防止 Nivo 刻度轴文本中的文本截断(条形图)