Home > Enterprise >  How to improve the readablity of this graph of multiple series with matplotlib?
How to improve the readablity of this graph of multiple series with matplotlib?

Time:12-08

If I have several curves on either side of the x-axis (like the green and orange curve in my case) what would be the best way to improve the display of this graph, for a better reading?

I was thinking for example by integrating a zoomed part on the curves between 0 and 0.15s on the x-axis.

Also each value of the curves correspond to a number, represented by a different marker (square, triangle, circle..) on the curves. Is there a better way to represent these curves and display these markers? In a slightly cleaner and more scientific way.

import matplotlib.pyplot as plt

A = [0.807, 0.633, 0.416, 0.274, 0.188]
time_A = [0.0990, 0.1021, 0.1097, 0.1109, 0.1321]

B = [0.764, 0.753, 0.716, 0.576, 0.516]
time_B = [0.1727, 0.1742, 0.1772, 0.1869, 0.1765]

C = [0.729, 0.719, 0.674, 0.631, 0.616]
time_C = [0.5295, 0.5368, 0.5431, 0.5391, 0.5443]

E = [0.709, 0.605, 0.390, 0.259, 0.155]
time_E = [0.0829, 0.0929, 0.0910, 0.0950, 0.0972]

D = [0.703, 0.541, 0.174, 0.062, 0.020]
time_D = [0.0740, 0.0792, 0.0819,  0.0837, 0.0858]

F = [0.748, 0.566, 0.366, 0.198, 0.168]
time_F = [0.0885, 0.0936, 0.09621, 0.0974, 0.0999]

markers = ["s", "^", "o", 'p', '*']

plt.plot(time_A, A, c='tab:blue', 
         label='A')

plt.plot(time_B, B, c='tab:red', 
         label='B')

plt.plot(time_C, C, c='tab:orange',
         label='C')

plt.plot(time_D, D, c='tab:green',
         label='D')

plt.plot(time_E, E, c='yellow',
         label='E')
 
plt.plot(time_F, F, c='tab:cyan',
         label='F')

for i in range(5):

    plt.plot(time_A[i], A[i], c='tab:blue',
             marker=markers[i], markersize=7)

    plt.plot(time_B[i], B[i], c='tab:red',
            marker=markers[i], markersize=7)

    plt.plot(time_C[i], C[i], c='tab:orange',
             marker=markers[i], markersize=7)

    plt.plot(time_D[i], D[i], c='tab:green',
             marker=markers[i], markersize=7)

    plt.plot(time_E[i], E[i], c='yellow',
             marker=markers[i], markersize=7)

    plt.plot(time_F[i], F[i],  c='tab:cyan',
             marker=markers[i], markersize=7)

textstr = '\n'.join((
    f'\u25A0 1',
    f'\u25B2 2',
    f'\u25CF 3',
    f'\u2B1F 4',
    f'\u2605 5'))

plt.text(0.4, 0.5, textstr,
         verticalalignment='top', fontsize = 'small') 

plt.legend(fontsize = 'small')

plt.xlabel('time (s)')
plt.ylabel('score')
plt.show()

enter image description here


Below is the result with the broken axis between 0.2 and 0.5 according to the comments. What is the correct way to integrate markers into curves with matplotlib?

enter image description here

CodePudding user response:

Here are some ideas:

  • use a dummy line to add labels for the markers; use two columns for the legend
  • set a log scale on the x-axis, but with regular tick labels
  • connect the markers of the same style with a fine line (order the points left to right for the line not to cross itself)
  • use the color 'gold' instead of 'yellow' to make it better visible
  • write everything as much as possible using loops
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter, ScalarFormatter, FixedLocator
import numpy as np

A = [0.807, 0.633, 0.416, 0.274, 0.188]
time_A = [0.0990, 0.1021, 0.1097, 0.1109, 0.1321]
B = [0.764, 0.753, 0.716, 0.576, 0.516]
time_B = [0.1727, 0.1742, 0.1772, 0.1869, 0.1765]
C = [0.729, 0.719, 0.674, 0.631, 0.616]
time_C = [0.5295, 0.5368, 0.5431, 0.5391, 0.5443]
E = [0.709, 0.605, 0.390, 0.259, 0.155]
time_E = [0.0829, 0.0929, 0.0910, 0.0950, 0.0972]
D = [0.703, 0.541, 0.174, 0.062, 0.020]
time_D = [0.0740, 0.0792, 0.0819, 0.0837, 0.0858]
F = [0.748, 0.566, 0.366, 0.198, 0.168]
time_F = [0.0885, 0.0936, 0.09621, 0.0974, 0.0999]

names = ['A', 'B', 'C', 'D', 'E', 'F']
times = [time_A, time_B, time_C, time_D, time_E, time_F]
scores = [A, B, C, D, E, F]

markers = ["s", "^", "o", 'p', '*']
colors = ['tab:blue', 'tab:red', 'tab:orange', 'tab:green', 'gold', 'tab:cyan']

fig, ax = plt.subplots(figsize=(12, 5))
for time, score, name, color in zip(times, scores, names, colors):
    ax.plot(time, score, c=color, label=name)

for i in range(len(scores[0])):
    ax.plot([], [], color='black', ls='', marker=markers[i], markersize=7, label=i   1)
    for time, score, name, color in zip(times, scores, names, colors):
        ax.plot(time[i], score[i], color=color, marker=markers[i], markersize=7)
    time_i = np.array([time[i] for time in times])
    score_i = np.array([score[i] for score in scores])
    order = np.argsort(time_i)
    ax.plot(time_i[order], score_i[order], color='grey', linestyle=':', linewidth=0.5, zorder=0)

ax.legend(fontsize='small', ncol=2)
ax.set_xscale('log')
xmin, xmax = ax.get_xlim()
ax.set_xticks(np.arange(0.1, round(xmax, 1), 0.1))
ax.set_xticks(np.arange(round(xmin, 2), round(xmax, 1), 0.01), minor=True)
ax.xaxis.set_major_formatter(ScalarFormatter())
ax.xaxis.set_minor_formatter(NullFormatter())
ax.set_xlabel('time (s)')
ax.set_ylabel('score')
plt.show()

line plot with markers and updated legend

If the values of the x-ticks are very important, the minor ticks could also get labels, for example:

minor_formatter = lambda x, pos: f'{x:.2f}' if (x < .1) or (x < .2 and round(100 * x) % 2 == 0) or (
        x > .2 and round(100 * x) % 10 == 5) else ''
ax.xaxis.set_minor_formatter(minor_formatter)
ax.tick_params(axis='x', which='minor', size=6, labelcolor='grey')
ax.tick_params(axis='x', which='major', size=12)

labeling some of the minor x ticks

  • Related