Работа с значениями NaN в matplotlib

У меня есть ежечасные данные, состоящие из нескольких столбцов. Первый столбец - это дата (date_log), а остальные столбцы содержат разные точки выборки. Проблема заключается в том, что выборочные точки регистрируются с использованием разного времени даже ежечасно, поэтому каждый столбец имеет как минимум пару NaN. Если я использую первый код, он работает хорошо, но я хочу иметь пробелы, где нет данных регистратора за день или около того, и не хочу, чтобы точки были соединены. Если я использую второй код, я вижу пробелы, но из-за точек NaN точки данных не соединяются. В приведенном ниже примере Im просто рисует первые три столбца.

Когда есть большой разрыв, например, синие точки (01/06-01/07/2015), я хочу иметь промежуток, после чего соединяются точки. Второй пример не соединяет точки. Мне нравится первый график, но я хочу создать пробелы, такие как второй метод, когда нет точек данных образца для 24-часового диапазона дат и т.д., Оставляя недостающие точки данных в течение более длительного времени в качестве пробела.

Есть ли какая-нибудь работа? Благодаря

1-метод:

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-метод:

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 метода: введите описание изображения здесь

Выход из 2-х методов: введите описание изображения здесь

Ответ 1

Если я правильно понимаю вас, у вас есть набор данных с большим количеством небольших пробелов (один NaN s), который вы хотите заполнить и увеличить пробелы, которые у вас нет.

Использование pandas для "переполнения" пробелов

Один из вариантов заключается в использовании pandas fillna с ограниченным количеством значений заполнения.

Как быстрый пример того, как это работает:

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

В качестве примера использования этого для чего-то похожего на ваш случай:

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

введите описание изображения здесь

Использование numpy для интерполяции пробелов

В качестве альтернативы мы можем сделать это, используя только numpy. Возможно (и более эффективно) выполнить "форвардную заливку", идентичную методу pandas выше, но я покажу еще один способ, чтобы дать вам больше опций, чем просто повторять значения.

Вместо повторения последнего значения через "пробел" мы можем выполнить линейную интерполяцию значений в пробеле. Это менее эффективно вычислительно (и я сделаю его еще менее эффективным, интерполируя везде), но для большинства наборов данных вы не заметите существенной разницы.

В качестве примера определим функцию 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

Обратите внимание, что мы получим интерполированное значение, в отличие от предыдущей версии 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.        ])

В примере построения графика, если мы заменим строку:

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

С

filled = interpolate_gaps(x, limit=2)

Мы получим визуально идентичный сюжет:

введите описание изображения здесь

Как полный, автономный пример:

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

Примечание. Я изначально полностью неправильно прочитал вопрос. См. Историю версий для моего первоначального ответа.

Ответ 2

Я просто использую эту функцию:

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