web-dev-qa-db-fra.com

Travailler avec les valeurs NaN dans matplotlib

J'ai des données horaires composées d'un certain nombre de colonnes. La première colonne est une date (date_log) et le reste des colonnes contient différents points d'échantillonnage. Le problème, c'est que les points d'échantillonnage sont enregistrés à une heure différente, même toutes les heures. Par conséquent, chaque colonne a au moins un couple de NaN. Si je trace avec le premier code, cela fonctionne bien, mais je veux des espaces où il n’ya pas de données d’enregistreur pour un jour ou deux et je ne veux pas que les points soient joints. Si j'utilise le second code, je peux voir les lacunes, mais à cause des points NaN, les points de données ne sont pas joints. Dans l'exemple ci-dessous, je ne fais que tracer les trois premières colonnes. 

Quand il y a un grand écart comme les points bleus (01/06-01/07/2015), je veux avoir un écart alors les points sont réunis. Le deuxième exemple ne joint pas les points. J'aime le premier graphique, mais je veux créer des lacunes, comme la deuxième méthode, lorsqu'il n'existe aucun échantillon de points de données pour la plage de dates de 24 heures, etc., en laissant des points de données manquants plus longtemps.

Y a-t-il un travail autour? Merci

1 méthode:

Log_1a_mask = np.isfinite(Log_1a) # Log_1a is column 2 data points
Log_1b_mask = np.isfinite(Log_1b) # Log_1b is column 3 data points

plt.plot_date(date_log[Log_1a_mask], Log_1a[Log_1a_mask], linestyle='-', marker='',color='r',)
plt.plot_date(date_log[Log_1b_mask], Log_1b[Log_1b_mask], linestyle='-', marker='', color='b')
plt.show()

2 méthode:

plt.plot_date(date_log, Log_1a, ‘-r*’, markersize=2, markeredgewidth=0, color=’r’) # Log_1a contains raw data with NaN
plt.plot_date(date_log, Log_1b, ‘-r*’, markersize=2, markeredgewidth=0, color=’r’) # Log_1a contains raw data with NaN
plt.show()

Sortie 1 méthode:  enter image description here

Sortie 2 méthodes:  enter image description here

19
Curtis

Si je vous ai bien compris, vous avez un ensemble de données avec de nombreux petits espaces (un seul NaNs) que vous souhaitez combler et des espaces plus grands que vous n’aurez pas.

Utiliser pandas pour "combler les lacunes"

Une option consiste à utiliser pandasfillna avec un nombre limité de valeurs de remplissage.

Comme exemple rapide de la façon dont cela fonctionne:

In [1]: import pandas as pd; import numpy as np

In [2]: x = pd.Series([1, np.nan, 2, np.nan, np.nan, 3, np.nan, np.nan, np.nan, 4])

In [3]: x.fillna(method='ffill', limit=1)
Out[3]:
0     1
1     1
2     2
3     2
4   NaN
5     3
6     3
7   NaN
8   NaN
9     4
dtype: float64

In [4]: x.fillna(method='ffill', limit=2)
Out[4]:
0     1
1     1
2     2
3     2
4     2
5     3
6     3
7     3
8   NaN
9     4
dtype: float64

Comme exemple d'utilisation de ceci pour quelque chose de similaire à votre cas:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)

x = np.random.normal(0, 1, 1000).cumsum()

# Set every third value to NaN
x[::3] = np.nan

# Set a few bigger gaps...
x[20:100], x[200:300], x[400:450] = np.nan, np.nan, np.nan

# Use pandas with a limited forward fill
# You may want to adjust the `limit` here. This will fill 2 nan gaps.
filled = pd.Series(x).fillna(limit=2, method='ffill')

# Let's plot the results
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot(x, color='lightblue')
axes[1].plot(filled, color='lightblue')

axes[0].set(ylabel='Original Data')
axes[1].set(ylabel='Filled Data')

plt.show()

 enter image description here

Utiliser numpy pour interpoler des espaces

Alternativement, nous pouvons le faire en utilisant uniquement numpy. Il est possible (et plus efficace) de faire un "remplissage avant" identique à la méthode des pandas ci-dessus, mais je vais vous montrer une autre méthode pour vous donner plus d'options que de simplement répéter des valeurs.

Au lieu de répéter la dernière valeur à travers le "gap", nous pouvons effectuer une interpolation linéaire des valeurs dans le gap. Cela est moins efficace du point de vue informatique (et je vais le rendre encore plus efficace en interpolant partout), mais pour la plupart des jeux de données, vous ne remarquerez pas de différence majeure.

Par exemple, définissons une fonction interpolate_gaps:

def interpolate_gaps(values, limit=None):
    """
    Fill gaps using linear interpolation, optionally only fill gaps up to a
    size of `limit`.
    """
    values = np.asarray(values)
    i = np.arange(values.size)
    valid = np.isfinite(values)
    filled = np.interp(i, i[valid], values[valid])

    if limit is not None:
        invalid = ~valid
        for n in range(1, limit+1):
            invalid[:-n] &= invalid[n:]
        filled[invalid] = np.nan

    return filled

Notez que nous obtiendrons une valeur interpolée, contrairement à la version précédente de pandas:

In [11]: values = [1, np.nan, 2, np.nan, np.nan, 3, np.nan, np.nan, np.nan, 4]

In [12]: interpolate_gaps(values, limit=1)
Out[12]:
array([ 1.        ,  1.5       ,  2.        ,         nan,  2.66666667,
        3.        ,         nan,         nan,  3.75      ,  4.        ])

Dans l'exemple de tracé, si nous remplaçons la ligne:

filled = pd.Series(x).fillna(limit=2, method='ffill')

Avec:

filled = interpolate_gaps(x, limit=2)

Nous aurons un complot visuellement identique:

 enter image description here

À titre d'exemple complet et autonome:

import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1977)

def interpolate_gaps(values, limit=None):
    """
    Fill gaps using linear interpolation, optionally only fill gaps up to a
    size of `limit`.
    """
    values = np.asarray(values)
    i = np.arange(values.size)
    valid = np.isfinite(values)
    filled = np.interp(i, i[valid], values[valid])

    if limit is not None:
        invalid = ~valid
        for n in range(1, limit+1):
            invalid[:-n] &= invalid[n:]
        filled[invalid] = np.nan

    return filled

x = np.random.normal(0, 1, 1000).cumsum()

# Set every third value to NaN
x[::3] = np.nan

# Set a few bigger gaps...
x[20:100], x[200:300], x[400:450] = np.nan, np.nan, np.nan

# Interpolate small gaps using numpy
filled = interpolate_gaps(x, limit=2)

# Let's plot the results
fig, axes = plt.subplots(nrows=2, sharex=True)
axes[0].plot(x, color='lightblue')
axes[1].plot(filled, color='lightblue')

axes[0].set(ylabel='Original Data')
axes[1].set(ylabel='Filled Data')

plt.show()

Note: Au départ, j'ai complètement mal lu la question. Voir l'historique des versions pour ma réponse originale.

20
Joe Kington

J'utilise simplement cette fonction:

import math
for i in range(1,len(data)):
  if math.isnan(data[i]):
    data[i] = data[i-1]
0
Lenar Hoyt