Python中的时间轴 - 在日期行之间创建间隔

1 投票
1 回答
42 浏览
提问于 2025-04-14 18:25

下面是代码和它的输出结果。

  1. 有没有办法在日期和下面的事件名称之间增加一些空隙,这样就不会像图中那样重叠了。
  2. 现在“正面”事件名称和它们的水平刻度之间有空隙。有没有办法去掉这个空隙,让它们和“负面”事件一样紧凑呢?

代码: import matplotlib.pyplot as plt import numpy as np import pandas as 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()

这里是图片

1 个回答

1

你可以通过使用“底部”或“顶部”来解决红色条的对齐问题,这取决于符号的不同:

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)

撰写回答