matplotlib: dessiner des lignes entre des points en ignorant les données manquantes

J’ai un dataset que je veux tracer sous forme de graphique linéaire. Pour chaque série, certaines données sont manquantes (mais différentes pour chaque série). Actuellement, matplotlib ne dessine pas les lignes qui ignorent les données manquantes: par exemple

import matplotlib.pyplot as plt xs = range(8) series1 = [1, 3, 3, None, None, 5, 8, 9] series2 = [2, None, 5, None, 4, None, 3, 2] plt.plot(xs, series1, linestyle='-', marker='o') plt.plot(xs, series2, linestyle='-', marker='o') plt.show() 

résulte en un tracé avec des lacunes dans les lignes. Comment puis-je dire à matplotlib de tracer des lignes à travers les lacunes? (Je préférerais ne pas avoir à interpoler les données).

Vous pouvez masquer les valeurs NaN de cette façon:

 import numpy as np import matplotlib.pyplot as plt xs = np.arange(8) series1 = np.array([1, 3, 3, None, None, 5, 8, 9]).astype(np.double) s1mask = np.isfinite(series1) series2 = np.array([2, None, 5, None, 4, None, 3, 2]).astype(np.double) s2mask = np.isfinite(series2) plt.plot(xs[s1mask], series1[s1mask], linestyle='-', marker='o') plt.plot(xs[s2mask], series2[s2mask], linestyle='-', marker='o') plt.show() 

Cela mène à

Terrain

Qouting @Rutger Kassies ( lien ):

Matplotlib ne dessine qu’une ligne entre des points de données consécutifs (valides) et laisse un espace aux valeurs NaN.

Une solution si vous utilisez des Pandas :

 #pd.Series s.dropna().plot() #masking (as @Thorsten Kranz suggestion) #pd.DataFrame df['a_col_ffill'] = df['a_col'].ffill(method='ffill') df['b_col_ffill'] = df['b_col'].ffill(method='ffill') # changed from a to b df[['a_col_ffill','b_col_ffill']].plot() 

Sans interpolation, vous devez supprimer le paramètre None des données. Cela signifie également que vous devrez supprimer les valeurs X correspondant à Aucun dans la série. Voici une doublure (laide) pour faire ça:

  x1Clean,series1Clean = zip(* filter( lambda x: x[1] is not None , zip(xs,series1) )) 

La fonction lambda renvoie les valeurs False for None, filtrant les paires x, series de la liste, puis recalcule les données dans leur forme d’origine.

Pour ce que cela peut valoir, après quelques essais et erreurs, je voudrais append une précision à la solution de Thorsten. Espérons que cela fera gagner du temps aux utilisateurs qui ont cherché ailleurs après avoir essayé cette approche.

Je n’ai pas réussi à réussir avec un problème identique en utilisant

 from pyplot import * 

et en essayant de tracer avec

 plot(abscissa[mask],ordinate[mask]) 

Il semblait nécessaire d’utiliser import matplotlib.pyplot as plt pour obtenir le traitement correct des NaN, même si je ne peux pas dire pourquoi.

Une solution avec les pandas:

 import matplotlib.pyplot as plt import pandas as pd def splitSerToArr(ser): return [ser.index, ser.as_masortingx()] xs = range(8) series1 = [1, 3, 3, None, None, 5, 8, 9] series2 = [2, None, 5, None, 4, None, 3, 2] s1 = pd.Series(series1, index=xs) s2 = pd.Series(series2, index=xs) plt.plot( *splitSerToArr(s1.dropna()), linestyle='-', marker='o') plt.plot( *splitSerToArr(s2.dropna()), linestyle='-', marker='o') plt.show() 

La fonction splitSerToArr est très pratique lorsque vous tracez dans Pandas. Ceci est la sortie: entrer la description de l'image ici

J’ai peut-être raté le point, mais je pense que Pandas le fait maintenant automatiquement . L’exemple ci-dessous est un peu compliqué et nécessite un access à Internet, mais la ligne pour la Chine a beaucoup de lacunes dans les premières années, d’où les segments de ligne droite.

 import pandas as pd import numpy as np import matplotlib.pyplot as plt # read data from Maddison project url = 'http://www.ggdc.net/maddison/maddison-project/data/mpd_2013-01.xlsx' mpd = pd.read_excel(url, skiprows=2, index_col=0, na_values=[' ']) mpd.columns = map(str.rssortingp, mpd.columns) # select counsortinges counsortinges = ['England/GB/UK', 'USA', 'Japan', 'China', 'India', 'Argentina'] mpd = mpd[counsortinges].dropna() mpd = mpd.rename(columns={'England/GB/UK': 'UK'}) mpd = np.log(mpd)/np.log(2) # convert to log2 # plots ax = mpd.plot(lw=2) ax.set_title('GDP per person', fontsize=14, loc='left') ax.set_ylabel('GDP Per Capita (1990 USD, log2 scale)') ax.legend(loc='upper left', fontsize=10, handlelength=2, labelspacing=0.15) fig = ax.get_figure() fig.show()