bbasaran
bbasaran

Reputation: 396

Matplotlib: How to plot labelled line graphs of dictionary pairs?

I have a dictionary of time series data in the following structure:

# {"second": [size, label]}
dict = {0.6: [100, 0],
        1.1: [120, 1],
        1.8: [220, 2]}

The dictionary above keeps track of the size and label until the corresponding second. Considering the example above, I'd like to plot lines in the same graph:

I have many data points in a dictionary for approximately 10 minutes of data. I am planning to split the x-axis e.g. for every 10 seconds, so the data above will be shown as tiny spikes in the whole graph. I cannot use scattering for this case, there should be one line graph across the time [0 to n) (composed of different graphs with different labels(colors) as in the points above).

Is this achievable by using matplotlib, or do I need to find another way? Thanks!

Upvotes: 0

Views: 500

Answers (1)

JohanC
JohanC

Reputation: 80299

plt.plot by default draws lines between the given positions. As only one point is given at each call, no lines are drawn. plt.scatter can draw one (or more) points with a given color.

You can first collect all the x and all the y values into lists, and then draw a line plot(plt.plot). If you want a step function, you can use plt.step instead. On top of those lines you can plot colored dots (plt.scatter). Or you could draw short lines via plt.plot([x_0, x_1], [y, y]).

Here is an example:

import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator

dict = {0.22: [100, 0],
        0.68: [180, 1],
        1.12: [210, 2]}
legendMap = {0: ["blue", "A"],
             1: ["red", "B"],
             2: ["green", "C"]}
x = list(dict.keys())
x.sort()  # needed in case the values wouldn't be ordered
y = [dict[xi][0] for xi in x]
# leave out the following line if you don't need the vertical lines
plt.step([0] + x, [y[0]] + y, color='black', ls=':', lw=0.5, where='pre')
used_legend_ids = set()
for xi0, xi1, yi in zip([0] + x[:-1], x, y):
    legend_id = dict[xi1][1]
    color, label = legendMap[legend_id]
    if legend_id in used_legend_ids:
        label = None
    else:
        used_legend_ids.add(legend_id)
    plt.plot([xi0, xi1], [yi] * 2, color=color, label=label)
plt.xticks(x)
plt.gca().yaxis.set_major_locator(MultipleLocator(25)) # set a tick every 25 units
plt.legend()
plt.show()

example plot

PS: To draw bars instead of horizontal lines, you could replace

plt.plot([xi0, xi1], [yi] * 2, color=color, label=label)

by

plt.bar(xi0, yi, width=xi1-xi0, align='edge', color=color, label=label, alpha=0.4)

Or even have both the bars and the lines (in that case you should leave out one of the two label=label parameters, to avoid a double legend).

To draw the plot of the comments, you could iterate through y similar as through x:

for xi0, xi1, yi0, yi1 in zip([0] + x[:-1], x, [0] + y[:-1], y):
    legend_id = dict[xi1][1]
    color, label = legendMap[legend_id]
    if legend_id in used_legend_ids:
        label = None
    else:
        used_legend_ids.add(legend_id)
    plt.plot([xi0, xi1], [yi0, yi1], color=color, label=label)

Upvotes: 1

Related Questions