Home > Software design >  How to plot timeline with multiple time-marker stems?
How to plot timeline with multiple time-marker stems?

Time:10-31

I'm new to matplotlib, and trying to plot something quite difficult.

I would like to plot something like (taken from the

Except, I want the timeline (x-axis) and stems to have labels in time, like:

timeline = ['0:01:00', '0:02:00', '0:03:00', '0:04:00', ...]
stems1 = ['0:01:45', '0:03:55', '0:04:22', ...]
stems2 = ['0:02:21', '0:06:54', ...

Notes:

  • Timeline ticks are evenly spaced
  • stems1 and stems2 don't necesarily have the same number of points, but are in order (like a video timeline)

It would be even better if stems1 and stems2 were different colors.

If anyone could point me in the right direction, or even code a working example, it'd be greatly appreciated! Thank you for reading.


Edit: Following @r-beginners's answer to enter image description here The issue is that the graphs are overlapping, stem color are both red, and the stems don't align with the timeline.


Edit 2: With @r-beginners code, I've tried it with 2 new stems, where stem1 isn't being plotted completely:

stem1 = ['0:08:08', '0:08:52', '0:09:42', '0:10:20', '0:10:55', '0:11:24', '0:12:31', '0:13:07', '0:13:45', '0:14:16', '0:14:49', '0:15:20', '0:15:51', '0:16:21', '0:16:53', '0:17:28', '0:19:01', '0:19:22', '0:20:19', '0:20:48', '0:21:19', '0:22:05', '0:23:06', '0:23:34', '0:24:03', '0:24:30', '0:24:51', '0:25:18', '0:25:54', '0:26:25', '0:27:07', '0:28:05', '0:29:04', '0:29:30', '0:30:34', '0:32:57', '0:33:28', '0:33:57', '0:34:35', '0:35:01', '0:35:41', '0:36:06', '0:36:30', '0:37:01', '0:37:33', '0:38:06', '0:38:40', '0:39:21', '0:40:02', '0:40:22', '0:40:42', '0:41:32', '0:41:56', '0:43:20', '0:43:39', '0:44:02', '0:44:26', '0:45:04', '0:45:32', '0:46:02', '0:47:00', '0:47:42', '0:48:05', '0:48:35', '0:49:02', '0:49:25', '0:49:56', '0:50:43', '0:51:25', '0:51:43', '0:52:18', '0:52:49', '0:53:08']

stem2 = ['0:09:49', '0:10:24', '0:14:27', '0:24:31', '0:26:03']

Code afterwards:

# Create figure
    fig, ax = plt.subplots(figsize=(100, 10), constrained_layout=True)
    ax.set(title='TEST')

    # Stem values
    names = [e for row in zip(stem1, stem2) for e in row]

    # Timeline
    timeline = [datetime.datetime.strptime(n, '%H:%M:%S') for n in names]

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

    # Stems
    ax.vlines(timeline, 0, levels, color='tab:red')

    # Plot timeline
    ax.plot(timeline, np.zeros_like(timeline), "-o", color="k", markerfacecolor="w")

    # Plot GT labels and predictions
    for time, level, name in zip(timeline, levels, names):
        ax.annotate(name, xy=(time, level),
                    xytext=(15, np.sign(level)*3),
                    textcoords='offset points',
                    horizontalalignment='right',
                    verticalalignment='bottom' if level > 0 else 'top',
                    color='green' if level > 0 else 'blue')

    # De-clutter axes
    ax.yaxis.set_visible(False)
    ax.spines[["left", "top", "right"]].set_visible(False)

    # ...
    minutes = mdates.MinuteLocator(interval=1)
    minutes_fmt = mdates.DateFormatter('%M:%S')
    ax.xaxis.set_major_locator(minutes)
    ax.xaxis.set_major_formatter(minutes_fmt)

    # Rotate x-ticks
    plt.setp(ax.get_xticklabels(), rotation=30, ha="right")

    # Save figure
    plt.savefig('test.png', bbox_inches='tight')

CodePudding user response:

The data was only partially available, so I created it appropriately. I have solved your problem by referring to the official example that you refer to. For overlapping strings, create a list of positive and negative placement heights and draw a vertical line in red. Convert the stem information to time series information and draw a timeline. In the annotation looping process, the placement height value is judged for placement and color to distinguish them. The time series representation of the x-axis is set by MinuteLocator to determine the time format.

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

fig, ax = plt.subplots(figsize=(16, 4), constrained_layout=True)
ax.set(title="Time line demo ")

stem1 = ['0:08:08', '0:08:52', '0:09:42', '0:10:20', '0:10:55', '0:11:24', '0:12:31', '0:13:07', '0:13:45', '0:14:16', '0:14:49', '0:15:20', '0:15:51', '0:16:21', '0:16:53', '0:17:28', '0:19:01', '0:19:22', '0:20:19', '0:20:48', '0:21:19', '0:22:05', '0:23:06', '0:23:34', '0:24:03', '0:24:30', '0:24:51', '0:25:18', '0:25:54', '0:26:25', '0:27:07', '0:28:05', '0:29:04', '0:29:30', '0:30:34', '0:32:57', '0:33:28', '0:33:57', '0:34:35', '0:35:01', '0:35:41', '0:36:06', '0:36:30', '0:37:01', '0:37:33', '0:38:06', '0:38:40', '0:39:21', '0:40:02', '0:40:22', '0:40:42', '0:41:32', '0:41:56', '0:43:20', '0:43:39', '0:44:02', '0:44:26', '0:45:04', '0:45:32', '0:46:02', '0:47:00', '0:47:42', '0:48:05', '0:48:35', '0:49:02', '0:49:25', '0:49:56', '0:50:43', '0:51:25', '0:51:43', '0:52:18', '0:52:49', '0:53:08']
stem2 = ['0:09:49', '0:10:24', '0:14:27', '0:24:31', '0:26:03']

stems = stem1   stem2
timelines = sorted([datetime.strptime(s, '%H:%M:%S') for s in stem])
labels = [datetime.strftime(t, '%H:%M:%S') for t in timelines]
levels = np.tile([-7, 7, -5, 5, -3, 3, -1, 1], int(np.ceil(len(timelines)/8)))[:len(timelines)]

ax.vlines(timelines, 0, levels, color='tab:red')
ax.plot(timelines, np.zeros_like(timelines), "-o", color="k", markerfacecolor="w")

for t, l, b in zip(timelines, levels, labels):
    if datetime.strftime(t, '%H:%M:%S')[1:] in stem2:
        color = 'blue'
    else:
        color = 'green'
    ax.annotate(b, xy=(t, l),
                xytext=(22, np.sign(l)*3), textcoords='offset points',
                horizontalalignment='right',
                verticalalignment='bottom' if l > 0 else 'top',
                color=color
               )
    
ax.yaxis.set_visible(False)
ax.spines[["left", "top", "right"]].set_visible(False)
ax.spines['bottom'].set_position(('data', -8))

minutes = mdates.MinuteLocator(interval=1)
minutes_fmt = mdates.DateFormatter('%M:%S')
ax.xaxis.set_major_locator(minutes)
ax.xaxis.set_major_formatter(minutes_fmt)

plt.setp(ax.get_xticklabels(), rotation=90, ha='center')

plt.show()

enter image description here

  • Related