pythonmatplotlibtime-seriestimeline

Changing ranges in timeline


Following this question I would like to change the X range (date), so that it will be from 1970, 1980,... till 2020 and the events will be a..b, etc... (I have a predefined list of events). I use the following code which dates starts with 1969 and ends with 1970 (instead of 1970 and 2020 respectively). The tried 2 options: 'event': ['a', 'b','c','d','e','f'], first option:

'date':pd.date_range(start='1970', periods=6)

second option:

'date': ['1970', '1980','1990','2000','2010','2020']   

Here is the complete code:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from datetime import datetime

df = pd.DataFrame(
    {
        'event': ['a', 'b','c','d','e','f'],
       'date': ['1970','1980','1990','2000','2010','2020']
     # ->   'date':pd.date_range(start='1970', periods=6)
             
    }
)

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

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

fig, ax = plt.subplots(figsize=(12.8, 4), constrained_layout=True);
ax.set(title="A series of events")

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']):
    ax.annotate(
        r,
        xy=(d, l),
        xytext=(-3, np.sign(l)*3),
        textcoords="offset points",
        horizontalalignment="right",
        verticalalignment="bottom" if l > 0 else "top"
    );

# format xaxis with 4 month intervals
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4));
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"));
plt.setp(ax.get_xticklabels(), rotation=30, ha="right");

# remove y axis and spines
ax.yaxis.set_visible(False);
ax.yaxis.set_visible(False);
ax.spines["left"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)    
ax.margins(y=0.1);
plt.show();

As explained above I would like to see on Xais only years 1970 to 2020 (with no month and day) and respectively their events (a to f).


Solution

  • Answer based on comments and from what I've understood, see full code below (notice I have just added one line for year and commented out the xaxis part):

    
    import matplotlib.pyplot as plt
    import numpy as np
    import matplotlib.dates as mdates
    from datetime import datetime
    import pandas as pd
    
    df = pd.DataFrame(
        {
            'event': ['a', 'b','c','d','e','f'],
           'date': ['1970','1980','1990','2000','2010','2020']
         # ->   'date':pd.date_range(start='1970', periods=6)
                 
        }
    )
    
    df['date'] = pd.to_datetime(df['date'])
    df['date'] = df['date'].dt.year # ADDED THIS!
    
    levels = np.tile(
        [-5, 5, -3, 3, -1, 1],
        int(np.ceil(len(df)/6))
    )[:len(df)]
    
    fig, ax = plt.subplots(figsize=(12.8, 4), constrained_layout=True);
    ax.set(title="A series of events")
    
    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']):
        ax.annotate(
            r,
            xy=(d, l),
            xytext=(-3, np.sign(l)*3),
            textcoords="offset points",
            horizontalalignment="right",
            verticalalignment="bottom" if l > 0 else "top"
        );
    
    # COMMENTED OUT THIS!
    # format xaxis with 4 month intervals
    # ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4));
    # ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"));
    plt.setp(ax.get_xticklabels(), rotation=30, ha="right");
    
    # remove y axis and spines
    ax.yaxis.set_visible(False);
    ax.yaxis.set_visible(False);
    ax.spines["left"].set_visible(False)
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)    
    ax.margins(y=0.1);
    plt.show();
    

    OUTPUT: enter image description here