Я придумал решение, которое использует LineCollection. Моя неудовлетворенность этим заключается в том, что элементы LineCollection кажутся не зависящими от масштаба (они имеют одинаковую ширину независимо от масштаба по оси Y), что затрудняет манипулирование. Из-за этого недостатка, я думаю, я предпочитаю барное решение.
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as mpatches
import pandas as pd
df = pd.DataFrame(zip([0, 3, 4, 7, 9, 12], ['A', 'B', 'A', 'C', 'D', 'A']),
columns=['Time', 'State'])
df['Duration'] = df['Time'].shift(-1) - df['Time']
# Showing how we can use HTML color names
state_to_color_map = {
'A': 'LightBlue',
'B': 'gray',
'C': 'LightPink',
'D': 'MediumSeaGreen'
}
fig = plt.figure(figsize=(8, 4))
ax = fig.gca()
plot_height = 0 # Can loop this to plot multiple bars
for state, state_color in state_to_color_map.iteritems():
segments = [[(start, plot_height), (start + duration, plot_height)] for
(start, duration) in
df[df['State'] == state][['Time', 'Duration']].values]
plot_segments = mpl.collections.LineCollection(
segments=segments,
# In matplotlib 2.2.2, this code is `mcolors.to_rgba(...)`
# Use this code for matplotlib 1.5.3.
colors=[mcolors.colorConverter.to_rgba(state_color)] * len(segments),
linewidths=50)
ax.add_collection(plot_segments)
ax.set_ylim(-1, 1)
ax.set_xlim(0, 12)
# Legend
patches = []
for state, color in sorted(state_to_color_map.iteritems()):
patches.append(mpatches.Patch(color=color, label=state))
ax.legend(handles=patches, bbox_to_anchor=(1.10, 0.5), loc='center',
borderaxespad=0.)