У меня есть несколько числовых временных рядов различной длины, хранящихся в широком кадре данных панд. Каждая строка соответствует одному ряду, а каждый столбец - точке измерения времени. Из-за разной длины эти ряды могут иметь хвосты пропущенных значений (NA), либо левые (первые моменты времени), либо правые (последние моменты времени), либо оба. В каждом ряду всегда есть непрерывная полоса без NA минимальной длины.
Мне нужно получить случайное подмножество фиксированной длины из каждой из этих строк, не включая NA. В идеале я хочу сохранить исходный фрейм данных и сообщить о подмножествах в новом.
Мне удалось получить этот вывод с очень неэффективным циклом for, который проходит по каждой строке один за другим, определяет начало для позиции обрезки, так что NA не будут включены в вывод, и копирует обрезанный результат. Это работает, но это очень медленно на больших наборах данных. Вот код:
import pandas as pd
import numpy as np
from copy import copy
def crop_random(df_in, output_length, ignore_na_tails=True):
# Initialize new dataframe
colnames = ['X_' + str(i) for i in range(output_length)]
df_crop = pd.DataFrame(index=df_in.index, columns=colnames)
# Go through all rows
for irow in range(df_in.shape[0]):
series = copy(df_in.iloc[irow, :])
series = np.array(series).astype('float')
length = len(series)
if ignore_na_tails:
pos_non_na = np.where(~np.isnan(series))
# Range where the subset might start
lo = pos_non_na[0][0]
hi = pos_non_na[0][-1]
left = np.random.randint(lo, hi - output_length + 2)
else:
left = np.random.randint(0, length - output_length)
series = series[left : left + output_length]
df_crop.iloc[irow, :] = series
return df_crop
И пример с игрушкой:
df = pd.DataFrame.from_dict({'t0': [np.NaN, 1, np.NaN],
't1': [np.NaN, 2, np.NaN],
't2': [np.NaN, 3, np.NaN],
't3': [1, 4, 1],
't4': [2, 5, 2],
't5': [3, 6, 3],
't6': [4, 7, np.NaN],
't7': [5, 8, np.NaN],
't8': [6, 9, np.NaN]})
# t0 t1 t2 t3 t4 t5 t6 t7 t8
# 0 NaN NaN NaN 1 2 3 4 5 6
# 1 1 2 3 4 5 6 7 8 9
# 2 NaN NaN NaN 1 2 3 NaN NaN NaN
crop_random(df, 3)
# One possible output:
# X_0 X_1 X_2
# 0 2 3 4
# 1 7 8 9
# 2 1 2 3
Как я мог достичь тех же результатов способом, адаптированным к большим фреймам данных?
Редактировать: мое улучшенное решение перенесено в раздел ответов.