0

I have data with 5 parameters, which I want to plot on multiple y axes and have them connected. Please see the example picture.

Currently I tried with normalizing all the values and create dictionary to do a scatter plot where on x axis would be values 1, 2, 3, 4, 5 and on y axis 5 parameter values for each data point. But this way I will need to add axis lines and values later on in Photoshop.

Is there a better way to create such graph using matplotlib and python?

Example of desired plot

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 2
    such a graph is called "parallel coordinates plot". You find a solution here: https://stackoverflow.com/questions/8230638/parallel-coordinates-plot-in-matplotlib. I also suggest to look at the plotting library plotly which can generate parallel coordinate plots in a very simple way: https://plot.ly/python/parallel-coordinates-plot/ – Pascalco Feb 24 '20 at 19:52
  • Ahh, thank you! Could not figure out the correct name of the graph. – Engineeringbridges Feb 25 '20 at 09:49
  • I just added an extra answer to the [referred question](https://stackoverflow.com/a/60401570/12046409) with example data resembling this question, and with Bézier curves between the axes. – JohanC Feb 25 '20 at 19:07

1 Answers1

0

One idea is to create 4 extra y axes, and set explicit limits for them. All the y-values can be rescaled to be compatible with the host axis.

(The code can still be optimized to make better use of numpy.)

import matplotlib.pyplot as plt
import numpy as np

fig, host = plt.subplots()

N = 20
y1 = np.random.uniform(10, 50, N)
y2 = np.sin(np.random.uniform(0, np.pi, N))
y3 = np.random.binomial(300, 0.9, N)
y4 = np.random.pareto(10, N)
y5 = np.random.uniform(0, 800, N)
ys = [y1, y2, y3, y4, y5]
y0min = ys[0].min()
dy = ys[0].max() - y0min
zs = [ys[0]] + [(y - y.min()) / (y.max() - y.min()) * dy + y0min for y in ys[1:]]
ynames = ['P1', 'P2', 'P3', 'P4', 'P5']

axes = [host] + [host.twinx() for i in range(len(ys) - 1)]
for i, (ax, y) in enumerate(zip(axes, ys)):
    ax.set_ylim(y.min(), y.max())
    ax.spines['top'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    if ax != host:
        ax.spines['left'].set_visible(False)
        ax.yaxis.set_ticks_position('right')
        ax.spines["right"].set_position(("axes", i / (len(ys) - 1)))

host.set_xlim(0, len(ys) - 1)
host.set_xticks(range(len(ys)))
host.set_xticklabels(ynames)
host.tick_params(axis='x', which='major', pad=7)
host.spines['right'].set_visible(False)

colors = plt.cm.tab20.colors
for j in range(len(ys[0])):
    host.plot(range(len(ys)), [z[j] for z in zs], c=colors[j % len(colors)])

plt.show()

example plot

JohanC
  • 71,591
  • 8
  • 33
  • 66