Заполнение строк NaN в индексированном фрейме данных big pandas datetime с использованием других значений строк, отличных от NaN

#python #pandas #time-series #nan #fillna

Вопрос:

У меня есть большой фрейм данных csv погоды, содержащий несколько сотен тысяч строк, а также множество столбцов. Строки представляют собой временные ряды, которые отбираются каждые 10 минут в течение многих лет. Столбец данных индекса, представляющий дату и время, состоит из года, месяца, дня, часа, минуты и секунды. К сожалению, было несколько тысяч отсутствующих строк, содержащих только NAN. Цель состоит в том, чтобы заполнить эти строки, используя значения других строк, собранных в то же время, но за другие годы, если они не являются NAN.

Я написал python для кода цикла, но это кажется очень трудоемким решением. Мне нужна ваша помощь для более эффективного и быстрого решения.

Необработанный кадр данных выглядит следующим образом:

 print(df)
 
                     p (mbar)  T (degC)  Tpot (K)  Tdew (degC)  rh (%)
datetime                                                              
2004-01-01 00:10:00    996.52     -8.02    265.40        -8.90   93.30
2004-01-01 00:20:00    996.57     -8.41    265.01        -9.28   93.40
2004-01-01 00:40:00    996.51     -8.31    265.12        -9.07   94.20
2004-01-01 00:50:00    996.51     -8.27    265.15        -9.04   94.10
2004-01-01 01:00:00    996.53     -8.51    264.91        -9.31   93.90
...                       ...       ...       ...          ...     ...
2020-12-31 23:20:00   1000.07     -4.05    269.10        -8.13   73.10
2020-12-31 23:30:00    999.93     -3.35    269.81        -8.06   69.71
2020-12-31 23:40:00    999.82     -3.16    270.01        -8.21   67.91
2020-12-31 23:50:00    999.81     -4.23    268.94        -8.53   71.80
2021-01-01 00:00:00    999.82     -4.82    268.36        -8.42   75.70

[820551 rows x 5 columns]
 

По какой-либо причине в кадре данных df отсутствовали строки. Чтобы идентифицировать их, можно применить приведенную ниже функцию:

 findnanrows(df.groupby(pd.Grouper(freq='10T')).mean())
 
                      p (mbar)  T (degC)  Tpot (K)  Tdew (degC)  rh (%)
datetime 
2004-01-01 00:30:00       NaN       NaN       NaN          NaN     NaN                                                             
2009-10-08 09:50:00       NaN       NaN       NaN          NaN     NaN
2009-10-08 10:00:00       NaN       NaN       NaN          NaN     NaN
2013-05-16 09:00:00       NaN       NaN       NaN          NaN     NaN
2014-07-30 08:10:00       NaN       NaN       NaN          NaN     NaN
...                       ...       ...       ...          ...     ...
2016-10-28 12:00:00       NaN       NaN       NaN          NaN     NaN
2016-10-28 12:10:00       NaN       NaN       NaN          NaN     NaN
2016-10-28 12:20:00       NaN       NaN       NaN          NaN     NaN
2016-10-28 12:30:00       NaN       NaN       NaN          NaN     NaN
2016-10-28 12:40:00       NaN       NaN       NaN          NaN     NaN

[5440 rows x 5 columns]
 

Цель состоит в том, чтобы заполнить все эти строки NaN. Например, первая строка NaN, соответствующая дате 2004-01-01 00:30:00 -времени, должна быть заполнена значениями not NaN другой строки, собранными в ту же дату xxxx-01-01 00:30:00 -время другого года, например 2005-01-01 00:30:00 или 2006-01-01 00:30:00 и так далее, даже 2003-01-01 00:30:00 2002-01-01 00:30:00 если они существуют. Можно применить среднее значение за все эти другие годы.

Просмотр значений строки с индексом даты и времени 2005-01-01 00:30:00 :

 print(df.loc["2005-01-01 00:30:00", :])
 
                      p (mbar)  T (degC)  Tpot (K)  Tdew (degC)  rh (%)
datetime                                                              
2005-01-01 00:30:00    996.36     12.67    286.13         7.11   68.82
 

После заполнения строки , соответствующей индексу datetime 2004-01-01 00:30:00 , с использованием значений строки, имеющей индекс datetime 2005-01-01 00:30:00 , фрейм данных df будет иметь следующую строку:

 print(df.loc["2004-01-01 00:30:00", :])
 
                      p (mbar)  T (degC)  Tpot (K)  Tdew (degC)  rh (%)
datetime                                                              
2004-01-01 00:30:00    996.36     12.67    286.13         7.11   68.82
 

Две функции, которые я создал, следующие. Первый — определить строки NaN. Второе-заполнить их.

 def findnanrows(df):
    is_NaN = df.isnull() 
    row_has_NaN = is_NaN.any(axis=1) 
    rows_with_NaN = df[row_has_NaN] 
    return rows_with_NaN

def filldata(weatherdata):
    fillweatherdata = weatherdata.copy()
    allyears = fillweatherdata.index.year.unique().tolist()
    dfnan = findnanrows(fillweatherdata.groupby(pd.Grouper(freq='10T')).mean())
    for i in range(dfnan.shape[0]):
        dnan = dfnan.index[i]
        if dnan.year == min(allyears):
            y = 0
            dnew = dnan.replace(year=dnan.year y)
            while dnew in dfnan.index:
                dnew = dnew.replace(year=dnew.year y)
                y  = 1   
        else:
            y = 0
            dnew = dnan.replace(year=dnan.year-y)
            while dnew in dfnan.index:
                dnew = dnew.replace(year=dnew.year-y)
                y  = 1
        new_row = pd.DataFrame(np.array([fillweatherdata.loc[dnew, :]]).tolist(), columns=fillweatherdata.columns.tolist(), index=[dnan])
        fillweatherdata = pd.concat([fillweatherdata, pd.DataFrame(new_row)], ignore_index=False)
    #fillweatherdata = fillweatherdata.drop_duplicates()
    fillweatherdata = fillweatherdata.sort_index()
    return fillweatherdata
 

Комментарии:

1. Было бы полезно, если бы вы привели часть входных данных в качестве примера, например, 10 строк по крайней мере с одной строкой с nan

2. Какой у вас актуальный вопрос?

3. Я переформулировал свой вопрос с помощью входных данных в качестве примера.

4. Не было бы более уместно интерполировать значения из данных непосредственно до и после отсутствующих данных, если таковые имеются? Погода десятиминутной давности, похоже, будет намного лучше предсказывать текущую погоду, чем погода ровно в эту минуту в прошлом году.

5. Если вы решите, что интерполяция-хороший подход, я бы просто использовал df.interpolate() .