it-swarm.com.de

Arbeiten mit NaN-Werten in Matplotlib

Ich habe stündliche Daten, die aus mehreren Spalten bestehen. Die erste Spalte enthält ein Datum (date_log), und die restlichen Spalten enthalten verschiedene Abtastpunkte. Das Problem ist, dass die Probenpunkte auch stundenweise zu unterschiedlichen Zeiten protokolliert werden, sodass jede Spalte mindestens ein paar NaN enthält. Wenn ich mit dem ersten Code einen Plot mache, funktioniert er gut, aber ich möchte Lücken haben, wo für einen Tag keine Logger-Daten vorhanden sind, und ich möchte nicht, dass die Punkte verbunden werden. Wenn ich den zweiten Code verwende, kann ich die Lücken sehen, aber aufgrund von NaN-Punkten werden die Datenpunkte nicht verbunden. Im folgenden Beispiel zeichne ich nur die ersten drei Spalten. 

Wenn es eine große Lücke wie die blauen Punkte gibt (01/06-01/07/2015), möchte ich eine Lücke haben, dann werden die Punkte zusammengefügt. Das zweite Beispiel verbindet die Punkte nicht. Ich mag das erste Diagramm, aber ich möchte Lücken wie die zweite Methode erstellen, wenn keine Beispieldatenpunkte für den 24-Stunden-Datumsbereich usw. vorhanden sind und fehlende Datenpunkte länger als Lücke verbleiben.

Gibt es Arbeit? Vielen Dank

1-Methode:

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-Methode:

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()

1-Methoden-Ausgabe:  enter image description here

Ausgabe mit 2 Methoden:  enter image description here

19
Curtis

Wenn ich Sie richtig verstehe, haben Sie ein Dataset mit vielen kleinen Lücken (einzelne NaNs), die gefüllt werden sollen, und größere Lücken, die Sie nicht benötigen.

Verwenden von pandas, um Lücken "vorwärts zu füllen"

Eine Option ist die Verwendung von pandasfillna mit einer begrenzten Anzahl von Füllwerten.

Als schnelles Beispiel, wie das funktioniert:

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

Als ein Beispiel für die Verwendung für etwas Ähnliches zu Ihrem Fall:

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

Verwenden von numpy zum Interpolieren von Lücken

Alternativ können wir dies nur mit numpy tun. Es ist möglich (und effizienter), eine "Vorwärtsfüllung" durchzuführen, die mit der oben beschriebenen Pandas-Methode identisch ist, aber ich zeige eine andere Methode, mit der Sie mehr Optionen erhalten können, als nur Werte zu wiederholen.

Anstatt den letzten Wert durch die "Lücke" zu wiederholen, können wir die Werte in der Lücke linear interpolieren. Dies ist rechnerisch weniger effizient (und ich werde es durch Interpolation überall noch weniger effizient machen), aber bei den meisten Datensätzen werden Sie keinen großen Unterschied bemerken.

Als Beispiel definieren wir eine interpolate_gaps-Funktion:

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

Beachten Sie, dass wir im Gegensatz zur vorherigen pandas-Version einen interpolierten Wert erhalten:

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.        ])

Wenn Sie im Plot-Beispiel die Zeile ersetzen:

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

Mit:

filled = interpolate_gaps(x, limit=2)

Wir erhalten eine visuell identische Handlung:

 enter image description here

Als vollständiges, eigenständiges Beispiel:

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()

Hinweis: Ich habe die Frage ursprünglich völlig falsch gelesen. Siehe Versionsverlauf für meine ursprüngliche Antwort.

20
Joe Kington

Ich benutze einfach diese Funktion:

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