Vitalii
Vitalii

Reputation: 51

A clear way to create a combination chart for time series data in matplotlib

I'm a new user of matplotlib. I have already learned the basics of plotting data in matplotlib. But I still cannot understand how I can plot multiple time series data as follows:

for example

It is also important for me that the plot contained a legend.

Data for example:

import pandas as pd
from io import StringIO
from matplotlib import pyplot as plt

data_str = '''time        x1   x2  x3  x4   
01.01.1984  100 -50  50  54 
01.02.1984  200 -20  180 7 
01.03.1984  250 -100 150 442413
01.04.1984  300 -50  250 7 
01.05.1984  250 -150 100 403 
01.06.1984  300 -200 100 148 
01.07.1984  200 -100 100 1096'''

df = pd.read_csv(StringIO(data_str), delim_whitespace=True)
df['time'] = pd.to_datetime(df['time'], format='%d.%m.%Y')
df = df.set_index('time')

ax = df['x3'].plot(grid=True, figsize=(10, 6), color='tab:grey', linewidth=4)

ax.bar(height=df['x1'], x=df.index, color='tab:blue', width=0.3, zorder=2, label='x1')
ax.bar(height=df['x2'], x=df.index, color='tab:orange', width=0.3, zorder=2, label='x2')

ax2 = ax.twinx()
ax2.set_yscale('log')
ax2.invert_yaxis()
df['x4'].plot.line(ax=ax2, color='gold', label='x4 (right)', linewidth=4)

ax.axhline(linewidth=1, color='black')
ax.set_xlabel('time', fontsize=16)
plt.xticks(size = 16)
plt.yticks(size = 16)

handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
handles = handles1[1:] + handles1[:1] + handles2 # handles1 is reordered because it uses the line before the bars
labels = labels1[1:] + labels1[:1] + labels2

#order = [0,1,2,3]

ax.legend([handles[idx] for idx in order],[labels[idx] for idx in order],
          loc='upper center', bbox_to_anchor=(0.5, -0.2),
          fancybox=True, shadow=True, ncol=5,
          prop={'size': 16})



#plt.xlim(['1983-12-01', '1984-08-01'])
plt.show()

enter image description here

Upvotes: 0

Views: 1219

Answers (2)

JohanC
JohanC

Reputation: 80574

Explaining each and every step is out of the scope of a stackoverflow answer. A good starting point are the online tutorials. Also, the suggested plot is quite complicated for a beginner's plot.

Some examples of functions used:

  • pd.read_csv(filename, ...) reads in data from a csv file
  • StringIO(data_str) is a way to mimic a file with reading from a string
  • plt.subplots creates a figure (fig) and subplots (referred to as ax) to plot on
  • df['x1'].plot.bar(...) is a way to plot a bar plot with pandas
  • df['x3'].plot.line(...) is a way to plot a line plot with pandas
  • ax1.twinx() creates the y-axis at the right
  • ...
import pandas as pd
from io import StringIO
from matplotlib import pyplot as plt

data_str = '''time        x1   x2  x3  x4   
01.01.1984  100 -50  50  54 
01.02.1984  200 -20  180 7 
01.03.1984  250 -100 150 442413
01.04.1984  300 -50  250 7 
01.05.1984  250 -150 100 403 
01.06.1984  300 -200 100 148 
01.07.1984  200 -100 100 1096'''

df = pd.read_csv(StringIO(data_str), delim_whitespace=True)
fig, ax1 = plt.subplots(figsize=(10, 4))
ax1.axhline(0, color='black', lw=2) # draw a long horizontal line at y=0
df = df.set_index('time')
df['x1'].plot.bar(ax=ax1, color='skyblue', label='x1')
df['x2'].plot.bar(ax=ax1, color='darkorange', label='x2')
df['x3'].plot.line(ax=ax1, color='slategray', label='x3')
ax2 = ax1.twinx()
ax2.set_yscale('log')
df['x4'].plot.line(ax=ax2, color='gold', label='x4 (right)')

handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
handles = handles1[1:] + handles1[:1] + handles2 # handles1 is reordered because it uses the line before the bars
labels = labels1[1:] + labels1[:1] + labels2
ax1.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=len(handles))
ax1.set_xlabel('')
plt.tight_layout()
plt.show()

combined bar and line plot

Upvotes: 2

John Mommers
John Mommers

Reputation: 140

A simple way of plotting multiple series in one plot (from a Pandas Data Frame, df), using a second y-axis is as follows:

df = pd.DataFrame({'time':[1,2,3,4,5,6,7], 
                   'x1':[100, 200, 250, 300, 250, 300, 200],
                   'x2':[-50,-20,-100,-50, -150, -200, -100],
                   'x3':[50, 180, 150, 250, 100, 100, 100],
                   'x4':[54, 7, 442413, 7, 403, 148, 1096]})

plt.figure()
df.x1.plot.bar()
df.x2.plot.bar(color='red')
df.x3.plot.line(color='red')
df.x4.plot.line(color='green', secondary_y=True)

enter image description here

Upvotes: 1

Related Questions