2015-05-29 2 views
2

У меня есть файл данных pandas DataFrame со временем как индекс (1 мин. Freq) и несколько столбцов данных. Иногда данные содержат NaN. Если это так, я хочу интерполировать только в том случае, если разрыв не превышает 5 минут. В этом случае это будет максимум 5 последовательных NaN. Данные могут выглядеть следующим образом (случаи несколько тестов, которые показывают проблемы):Интерполировать (или экстраполировать) только небольшие пробелы в pandas dataframe

import numpy as np 
import pandas as pd 
from datetime import datetime 

start = datetime(2014,2,21,14,50) 
data = pd.DataFrame(index=[start + timedelta(minutes=1*x) for x in range(0, 8)], 
         data={'a': [123.5, np.NaN, 136.3, 164.3, 213.0, 164.3, 213.0, 221.1], 
           'b': [433.5, 523.2, 536.3, 464.3, 413.0, 164.3, 213.0, 221.1], 
           'c': [123.5, 132.3, 136.3, 164.3] + [np.NaN]*4, 
           'd': [np.NaN]*8, 
           'e': [np.NaN]*7 + [2330.3], 
           'f': [np.NaN]*4 + [2763.0, 2142.3, 2127.3, 2330.3], 
           'g': [2330.3] + [np.NaN]*7, 
           'h': [2330.3] + [np.NaN]*6 + [2777.7]}) 

Он звучит так:

In [147]: data 
Out[147]: 
         a  b  c d  e  f  g  h 
2014-02-21 14:50:00 123.5 433.5 123.5 NaN  NaN  NaN 2330.3 2330.3 
2014-02-21 14:51:00 NaN 523.2 132.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:52:00 136.3 536.3 136.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:53:00 164.3 464.3 164.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:54:00 213.0 413.0 NaN NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:55:00 164.3 164.3 NaN NaN  NaN 2142.3  NaN  NaN 
2014-02-21 14:56:00 213.0 213.0 NaN NaN  NaN 2127.3  NaN  NaN 
2014-02-21 14:57:00 221.1 221.1 NaN NaN 2330.3 2330.3  NaN 2777.7 

Я знаю data.interpolate(), но он имеет ряд недостатков, так как она производит этот результат, который хорош для колонн аи, но и для столбцов Fh он не по разным причинам ::

      a  b  c d  e  f  g \ 
2014-02-21 14:50:00 123.5 433.5 123.5 NaN  NaN  NaN 2330.3 
2014-02-21 14:51:00 129.9 523.2 132.3 NaN  NaN  NaN 2330.3 
2014-02-21 14:52:00 136.3 536.3 136.3 NaN  NaN  NaN 2330.3 
2014-02-21 14:53:00 164.3 464.3 164.3 NaN  NaN  NaN 2330.3 
2014-02-21 14:54:00 213.0 413.0 164.3 NaN  NaN 2763.0 2330.3 
2014-02-21 14:55:00 164.3 164.3 164.3 NaN  NaN 2142.3 2330.3 
2014-02-21 14:56:00 213.0 213.0 164.3 NaN  NaN 2127.3 2330.3 
2014-02-21 14:57:00 221.1 221.1 164.3 NaN 2330.3 2330.3 2330.3 

           h 
2014-02-21 14:50:00 2330.300000 
2014-02-21 14:51:00 2394.214286 
2014-02-21 14:52:00 2458.128571 
2014-02-21 14:53:00 2522.042857 
2014-02-21 14:54:00 2585.957143 
2014-02-21 14:55:00 2649.871429 
2014-02-21 14:56:00 2713.785714 
2014-02-21 14:57:00 2777.700000 

F) зазор состоит из 4-х минут стоят NaNs в начале, тыс ey следует заменить на это значение 2763,0 (т.е. экстраполяция назад во время)

г) Разрыв больше чем за 5 минут, но все же он получает экстраполирован

ч) Разрыв больше чем за 5 минут, но все еще зазор интерполирован.

Я понимаю эти причины, конечно, я нигде не указал, что он не должен интерполировать более длинные промежутки времени, чем 5 минут. Я понимаю, что interpolate только экстраполирует вперед во времени, но я хочу, чтобы он также экстраполировал назад во времени. Есть ли какие-либо известные методы, которые я могу использовать для своей проблемы, не изобретая колесо?

Редактировать: Метод data.interpolate принимает входной параметр limit, который определяет максимальное количество последовательных NaNs быть замещен с помощью интерполяции. Но это все еще интерполирует до предела, но я хочу продолжить все NaN в этом случае.

+1

После интерполяции можно использовать метод bfill() 'backward-fill? – Zero

+0

@JohnGalt '' bfill() '' отлично подходит для интерполяции. К сожалению, она сталкивается с той же проблемой для более длинных промежутков времени, чем 5 минут, так как она просто заполняет их. – Nras

+0

Что вы ожидаете? Если он имеет только одно значение, он не может интерполировать значения. – Zero

ответ

4

Итак, вот маска, которая должна решить проблему. Просто interpolate, а затем примените маску для сброса соответствующих значений в NaN. Честно говоря, это было немного больше работы, чем я понял, это было бы потому, что мне пришлось перебирать каждый столбец, но затем группа не работала без меня, предоставляя некоторые фиктивные столбцы, такие как «те».

В любом случае, я могу объяснить, если что-то неясно, но на самом деле только несколько строк несколько трудно понять. См. here для более подробного объяснения трюка на линии df['new'] или просто распечатайте отдельные строки, чтобы лучше видеть, что происходит.

mask = data.copy() 
for i in list('abcdefgh'): 
    df = pd.DataFrame(data[i]) 
    df['new'] = ((df.notnull() != df.shift().notnull()).cumsum()) 
    df['ones'] = 1 
    mask[i] = (df.groupby('new')['ones'].transform('count') < 5) | data[i].notnull() 

In [7]: data 
Out[7]: 
         a  b  c d  e  f  g  h 
2014-02-21 14:50:00 123.5 433.5 123.5 NaN  NaN  NaN 2330.3 2330.3 
2014-02-21 14:51:00 NaN 523.2 132.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:52:00 136.3 536.3 136.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:53:00 164.3 464.3 164.3 NaN  NaN  NaN  NaN  NaN 
2014-02-21 14:54:00 213.0 413.0 NaN NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:55:00 164.3 164.3 NaN NaN  NaN 2142.3  NaN  NaN 
2014-02-21 14:56:00 213.0 213.0 NaN NaN  NaN 2127.3  NaN  NaN 
2014-02-21 14:57:00 221.1 221.1 NaN NaN 2330.3 2330.3  NaN 2777.7 

In [8]: mask 
Out[8]: 
         a  b  c  d  e  f  g  h 
2014-02-21 14:50:00 True True True False False True True True 
2014-02-21 14:51:00 True True True False False True False False 
2014-02-21 14:52:00 True True True False False True False False 
2014-02-21 14:53:00 True True True False False True False False 
2014-02-21 14:54:00 True True True False False True False False 
2014-02-21 14:55:00 True True True False False True False False 
2014-02-21 14:56:00 True True True False False True False False 
2014-02-21 14:57:00 True True True False True True False True 

Это легко оттуда, если вы что-нибудь любитель не делать в отношении экстраполяции:

In [9]: data.interpolate().bfill()[mask] 
Out[9]: 
         a  b  c d  e  f  g  h 
2014-02-21 14:50:00 123.5 433.5 123.5 NaN  NaN 2763.0 2330.3 2330.3 
2014-02-21 14:51:00 129.9 523.2 132.3 NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:52:00 136.3 536.3 136.3 NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:53:00 164.3 464.3 164.3 NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:54:00 213.0 413.0 164.3 NaN  NaN 2763.0  NaN  NaN 
2014-02-21 14:55:00 164.3 164.3 164.3 NaN  NaN 2142.3  NaN  NaN 
2014-02-21 14:56:00 213.0 213.0 164.3 NaN  NaN 2127.3  NaN  NaN 
2014-02-21 14:57:00 221.1 221.1 164.3 NaN 2330.3 2330.3  NaN 2777.7 

Изменить, чтобы добавить: Вот быстрее (около 2х на этом образце данных) и слегка более простым способом, перемещая некоторый материал за пределы цикла:

mask = data.copy() 
grp = ((mask.notnull() != mask.shift().notnull()).cumsum()) 
grp['ones'] = 1 
for i in list('abcdefgh'): 
    mask[i] = (grp.groupby(i)['ones'].transform('count') < 5) | data[i].notnull() 
+0

Эй, JohnE, очень хорошая идея интерполировать/bfill для всех значений и просто просто использовать результаты в ранее определенных позициях. Runtime - проблема, есть случаи с приблизительно 1000 столбцами. Я проверю время выполнения и посмотрю, смогу ли я создать маску более эффективно с некоторым усердием, если это необходимо. – Nras

+0

FYI: Я принял вашу идею, добавив случаи, поскольку случай некоторых NaN, который должен быть интерполирован, встречается довольно редко: '' if df.isnull(). All() '' - маска - это False, '' elif df.isnull(). any() '' делает материал groubpy, который вы предоставили, и для '' else' 'устанавливает маску для всех True. Кроме того, он несколько быстрее, когда не запускается цикл, но помещает создание маски в метод и кормит '' data.apply() '' им. – Nras

+0

ОК, честно говоря, я был просто счастлив, когда получил код для работы вообще! Я не тратил времени на его оптимизацию, поэтому я уверен, что вы сможете ее улучшить. Я просто добавил редактирование, которое помогает просто перемещать некоторый код за пределы цикла, но похоже, что у вас уже есть хорошие идеи для повышения производительности. FWIW, я подозреваю, что groupby/transform сейчас занимает большую часть времени, но не вижу очевидного способа обойти это. – JohnE

Смежные вопросы