Python 中的时间线 - 在日期线之间创建空格

问题描述 投票:0回答:1

以下是代码及其输出。

  1. 有没有办法在日期线和下面的事件名称之间留出空间,这样就不会如图所示重叠。
  2. “积极”事件名称与其水平刻度之间有一个空格。有没有办法消除空间(并将其作为刻度上的“负面”事件)?

代码: 将 matplotlib.pyplot 导入为 plt 将 numpy 导入为 np 将 pandas 导入为 pd

df = pd.DataFrame(
    {
        'event': ['Qfpgv KFJPF fpnkmkq',
                  'Ltzx cqwup xnywi bxfjzgq ol mwwqmg ukszs',
                  'MUTD ysrfzad Urmiv lqyjexdq xqkqzfx vqtwrgh',
                  'Vxdys vjxqnqojq qvqoshjmhv dmyzf fj wvtrjv',
                  'Kcxtm-Bix Nzvlqj ajmydgbxk',
                  'Nrsbo! ukguvle xavahfg tqyikwqg, UZSP tgrlqfr',
                  'Rjxetf/uzpqwhwr qtshxvlp tljybtncbq qvqybnjgq dzqj',
                  'Qwvbt-Khspqw olfypkbvh tljmyyvz ajmy zazvqfm',
                  'UHW Umkqtqm zvhq tljybtncbq',
                  'Wwscye rukqdf, vfyvqmf udzvqmcv tljybtncbq',
                  'Twljq uqtrjxwh hyvnvwbl tljmyyvz rbykqkwqjg djzv Kqkmv xnyzqmv.',
                  'Qfpgv Qnwroj rymqzqm tljybtncbq kxqj vq Kqmnjp kxqkz.',
                  'Vwkqr jvqjg fqtwp, Jvccjvj CQM Sgqhojif mblqjc',
                  'Qxltj dqg Vqsue tljmyyvz jvtsqjwuj wkhruwqlqj, ixdro xqjolvkphw',
                  'Rwkq Vqwdqlqj odujhg jvswhuh fduuleolqj',
                  'Nzvq nqfupxqj jtsqjwuj',
                  'Vqolqjyphfwhv sohwwhuqvhtg jtsqjwuj',
                  'Ulnwj ri gihw dqg rqooih OY wlg ihghovdfh orqjv',
                  'Fkxohqjdoahoo sohwwhu ydplqhuv rqh wkhhtxdo jtsqjwuj'
                 ],
        'date': ['1984', '1987', '1991', '1994', '1997', '1998', '1999', '2002', '2004',
                 '2005', '2007', '2009', '2010', '2012', '2013', '2014', '2017', '2019', '2021']
    }
)

df['date'] = pd.to_datetime(df['date'])
df['date'] = df['date'].dt.year

levels = np.tile(
    [-5, 5, -3, 3, -1, 1, -7, 7, -4, 4, -2, 2, -6, 6, -3, 3, -1, 1, -5, 5, -3, 3, -1, 1, 5],
    int(np.ceil(len(df) / 6))
)[:len(df)]

fig, ax = plt.subplots(figsize=(12.8, 4), constrained_layout=True)

ax.vlines(df['date'], 0, levels, color="tab:red")  # The vertical stems.
ax.plot(  # Baseline and markers on it.
    df['date'],
    np.zeros_like(df['date']),
    "-o",
    color="k",
    markerfacecolor="w"
)

# annotate lines
for d, l, r in zip(df['date'], levels, df['event']):
    lines = r.split(' ')
    line1 = ' '.join(lines[:len(lines)//2])
    line2 = ' '.join(lines[len(lines)//2:])
    ax.annotate(
        line1 + '\n' + line2,
        xy=(d, l),
        xytext=(-5, np.sign(l) * 15),  # Increase the y-offset for more vertical space
        textcoords="offset points",
        horizontalalignment="center",
        verticalalignment="bottom",
        fontsize=8  # Adjust the font size to fit the annotations within the plot
    )
    
ax.text(0.5, 1.3, "PLOT PLOT PLOT", transform=ax.transAxes,  
         fontsize=16, fontweight='bold', ha='center') 
ax.get_yaxis().set_visible(False)  # Remove the y-axis
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

plt.show()

python matplotlib time-series timeline
1个回答
0
投票

您可以根据符号使用“bottom”/“top”来解决与红条的对齐问题:

verticalalignment="bottom" if l<0 else "top",

对于底部注释,您可以将所有注释收集在一个列表中,绘制图形,然后找出哪个注释的边界框位于底部以重新缩放 Y 轴:

annots = []
for d, l, r in zip(df['date'], levels, df['event']):
    lines = r.split(' ')
    line1 = ' '.join(lines[:len(lines)//2])
    line2 = ' '.join(lines[len(lines)//2:])
    annots.append(ax.annotate(
        line1 + '\n' + line2,
        xy=(d, l),
        xytext=(-5, np.sign(l) * 20),  # Increase the y-offset for more vertical space
        textcoords="offset points",
        horizontalalignment="center",
        verticalalignment="bottom" if l<0 else "top",
        fontsize=8  # Adjust the font size to fit the annotations within the plot
    ))

# ...

# draw figure
fig.canvas.draw()

# fix Y-axis limits
min_annot = min(a.get_window_extent().transformed(ax.transData.inverted()).bounds[1] for a in annots)
ax.set_ylim(bottom = min_annot-0.5)

输出:

完整代码:

df = pd.DataFrame(
    {
        'event': ['Qfpgv KFJPF fpnkmkq',
                  'Ltzx cqwup xnywi bxfjzgq ol mwwqmg ukszs',
                  'MUTD ysrfzad Urmiv lqyjexdq xqkqzfx vqtwrgh',
                  'Vxdys vjxqnqojq qvqoshjmhv dmyzf fj wvtrjv',
                  'Kcxtm-Bix Nzvlqj ajmydgbxk',
                  'Nrsbo! ukguvle xavahfg tqyikwqg, UZSP tgrlqfr',
                  'Rjxetf/uzpqwhwr qtshxvlp tljybtncbq qvqybnjgq dzqj',
                  'Qwvbt-Khspqw olfypkbvh tljmyyvz ajmy zazvqfm',
                  'UHW Umkqtqm zvhq tljybtncbq',
                  'Wwscye rukqdf, vfyvqmf udzvqmcv tljybtncbq',
                  'Twljq uqtrjxwh hyvnvwbl tljmyyvz rbykqkwqjg djzv Kqkmv xnyzqmv.',
                  'Qfpgv Qnwroj rymqzqm tljybtncbq kxqj vq Kqmnjp kxqkz.',
                  'Vwkqr jvqjg fqtwp, Jvccjvj CQM Sgqhojif mblqjc',
                  'Qxltj dqg Vqsue tljmyyvz jvtsqjwuj wkhruwqlqj, ixdro xqjolvkphw',
                  'Rwkq Vqwdqlqj odujhg jvswhuh fduuleolqj',
                  'Nzvq nqfupxqj jtsqjwuj',
                  'Vqolqjyphfwhv sohwwhuqvhtg jtsqjwuj',
                  'Ulnwj ri gihw dqg rqooih OY wlg ihghovdfh orqjv',
                  'Fkxohqjdoahoo sohwwhu ydplqhuv rqh wkhhtxdo jtsqjwuj'
                 ],
        'date': ['1984', '1987', '1991', '1994', '1997', '1998', '1999', '2002', '2004',
                 '2005', '2007', '2009', '2010', '2012', '2013', '2014', '2017', '2019', '2021']
    }
)

df['date'] = pd.to_datetime(df['date'])
df['date'] = df['date'].dt.year

levels = np.tile(
    [-5, 5, -3, 3, -1, 1, -7, 7, -4, 4, -2, 2, -6, 6, -3, 3, -1, 1, -5, 5, -3, 3, -1, 1, 5],
    int(np.ceil(len(df) / 6))
)[:len(df)]

fig, ax = plt.subplots(figsize=(12.8, 4), constrained_layout=True)

ax.vlines(df['date'], 0, levels, color="tab:red")  # The vertical stems.
ax.plot(  # Baseline and markers on it.
    df['date'],
    np.zeros_like(df['date']),
    "-o",
    color="k",
    markerfacecolor="w"
)

# annotate lines
annots = []
for d, l, r in zip(df['date'], levels, df['event']):
    lines = r.split(' ')
    line1 = ' '.join(lines[:len(lines)//2])
    line2 = ' '.join(lines[len(lines)//2:])
    annots.append(ax.annotate(
        line1 + '\n' + line2,
        xy=(d, l),
        xytext=(-5, np.sign(l) * 20),  # Increase the y-offset for more vertical space
        textcoords="offset points",
        horizontalalignment="center",
        verticalalignment="bottom" if l<0 else "top",
        fontsize=8  # Adjust the font size to fit the annotations within the plot
    ))


ax.text(0.5, 1.3, "PLOT PLOT PLOT", transform=ax.transAxes,  
         fontsize=16, fontweight='bold', ha='center') 
ax.get_yaxis().set_visible(False)  # Remove the y-axis
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.canvas.draw()

min_annot = min(a.get_window_extent().transformed(ax.transData.inverted()).bounds[1] for a in annots)
ax.set_ylim(bottom = min_annot-0.5)
© www.soinside.com 2019 - 2024. All rights reserved.