Панды сбрасываются перед первым действительным индексом и после последнего действительного индекса для каждого столбца кадра данных

1

У меня есть dataframe, как это:

df = pd.DataFrame({'timestamp':pd.date_range('2018-01-01', '2018-01-02', freq='2h', closed='right'),'col1':[np.nan, np.nan, np.nan, 1,2,3,4,5,6,7,8,np.nan], 'col2':[np.nan, np.nan, 0, 1,2,3,4,5,np.nan,np.nan,np.nan,np.nan], 'col3':[np.nan, -1, 0, 1,2,3,4,5,6,7,8,9], 'col4':[-2, -1, 0, 1,2,3,4,np.nan,np.nan,np.nan,np.nan,np.nan]
              })[['timestamp', 'col1', 'col2', 'col3', 'col4']]

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

             timestamp  col1  col2  col3  col4
0  2018-01-01 02:00:00   NaN   NaN   NaN  -2.0
1  2018-01-01 04:00:00   NaN   NaN  -1.0  -1.0
2  2018-01-01 06:00:00   NaN   0.0   NaN   0.0
3  2018-01-01 08:00:00   1.0   1.0   1.0   1.0
4  2018-01-01 10:00:00   2.0   NaN   2.0   2.0
5  2018-01-01 12:00:00   3.0   3.0   NaN   3.0
6  2018-01-01 14:00:00   NaN   4.0   4.0   4.0
7  2018-01-01 16:00:00   5.0   NaN   5.0   NaN
8  2018-01-01 18:00:00   6.0   NaN   6.0   NaN
9  2018-01-01 20:00:00   7.0   NaN   7.0   NaN
10 2018-01-01 22:00:00   8.0   NaN   8.0   NaN
11 2018-01-02 00:00:00   NaN   NaN   9.0   NaN

Теперь я хочу найти эффективный и питонический способ измельчения (для каждого столбца! Не считая отметки времени) до первого действительного индекса и после последнего действительного индекса. В этом примере у меня 4 столбца, но на самом деле у меня намного больше, 600 или около того. Я ищу способ измельчения всех значений NaN до первого действительного индекса и всех значений NaN после последнего действительного индекса.

Один из способов мог бы пройти по петле, я думаю... Но есть ли лучший способ? Этот способ должен быть эффективным. Я попытался "развернуть" dataframe с использованием расплава, но тогда это не помогло.

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

             timestamp  col1   
3  2018-01-01 08:00:00   1.0  
4  2018-01-01 10:00:00   2.0   
5  2018-01-01 12:00:00   3.0   
6  2018-01-01 14:00:00   NaN   
7  2018-01-01 16:00:00   5.0   
8  2018-01-01 18:00:00   6.0   
9  2018-01-01 20:00:00   7.0   
10 2018-01-01 22:00:00   8.0    

Моя попытка

Я пробовал вот так:

final = []
columns = [c for c in df if c !='timestamp']
for col in columns:
    first = df.loc[:, col].first_valid_index()
    last = df.loc[:, col].last_valid_index()
    final.append(df.loc[:, ['timestamp', col]].iloc[first:last+1, :])
Теги:
pandas

3 ответа

1

Вы можете использовать возможности функционального программирования и применить функцию к каждому столбцу. Это может ускорить процесс. Кроме того, как вы timestamps выглядит отсортирована, вы можете использовать их в качестве индекса вашего Datarame.

df.set_index('timestamp', inplace=True)

final = []
def func(col):
    first = col.first_valid_index()
    last = col.last_valid_index()
    final.append(col.loc[first:last])
    return

df.apply(func)

Кроме того, вы можете сжать все в одном лайнере:

final = []
df.apply(lambda col: final.append(col.loc[col.first_valid_index() : col.last_valid_index()]))
1

Мой подход состоит в том, чтобы найти кумулятивную сумму NaN для каждого столбца и ее обратно и фильтровать те записи, которые больше 0. Затем я использую определение dict, чтобы возвращать dataframe для каждого столбца (вы можете изменить это на список, если это то, что вы предпочитаете).

Для вашего примера мы имеем

cols = [c for c in df.columns if c!='timestamp']

result_dict = {c: df[(df[c].notnull().cumsum() > 0) &
                     (df.ix[::-1,c].notnull().cumsum()[::-1] > 0)][['timestamp', c]]
               for c in cols}
1

Одна из идей заключается в использовании понимания списка или словаря после установки индекса как timestamp. Вы должны проверить свои данные, чтобы убедиться, что это устраняет проблему с производительностью. Это вряд ли поможет, если ваше ограничение - это память.

df = df.set_index('timestamp')

final = {col: df[col].loc[df[col].first_valid_index(): df[col].last_valid_index()] \
         for col in df}

print(final)

{'col1': timestamp
2018-01-01 08:00:00    1.0
2018-01-01 10:00:00    2.0
2018-01-01 12:00:00    3.0
2018-01-01 14:00:00    4.0
2018-01-01 16:00:00    5.0
2018-01-01 18:00:00    6.0
2018-01-01 20:00:00    7.0
2018-01-01 22:00:00    8.0
Name: col1, dtype: float64,
...
'col4': timestamp
2018-01-01 02:00:00   -2.0
2018-01-01 04:00:00   -1.0
2018-01-01 06:00:00    0.0
2018-01-01 08:00:00    1.0
2018-01-01 10:00:00    2.0
2018-01-01 12:00:00    3.0
2018-01-01 14:00:00    4.0
Name: col4, dtype: float64}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню