Есть ли быстрый способ разрезать фрейм данных Pandas по индексу, чтобы отсутствующие строки заполнялись NAN?

#python #arrays #pandas #performance #numpy

Вопрос:

Мне нужно как можно эффективнее назначить стек строк из pd.DataFrame по индексу матрице известного размера. Много, если индексы не будут существовать во фрейме данных, и эти строки должны быть заполнены NAN. Эта операция будет выполняться внутри цикла, повторяющегося по каждой строке в кадре данных, и должна выполняться как можно быстрее.

Короче говоря, я хочу ускорить следующую последовательность:

 # DF to iterate over
df = pd.DataFrame({'a': [1,2,3,4], 'b': [2,4,6,8], 'c': [3,6,9,12]})

# (Relative) DF indices to return if possible
indices = [-2, 0, 2, 4]

len_df = len(df)
len_indices = len(indices)
len_cols = len(df.columns)

values = np.zeros((len_df, len_indices, len_cols))

for i in range(len_df):
   for j, n in enumerate(indices):
      idx = i   n
      try:
         assert idx > 0  # avoid wrapping around
         values[i, j] = df.iloc[idx]
      except:
         values[i, j] = np.nan

values  
 

ВОЗВРАТ

 [[[nan, nan, nan],
  [  1,   2,   3],
  [  3,   6,   9],
  [nan, nan, nan]],

 [[nan, nan, nan],
   [  2,   4,   6],
   [  4,   8,  12],
   [nan, nan, nan]],

  [[  1,   2,   3],
   [  3,   6,   9],
   [nan, nan, nan],
   [nan, nan, nan]],

  [[  2,   4,   6],
   [  4,   8,  12],
   [nan, nan, nan],
   [nan, nan, nan]]]

 

По желанию, но довольно медленно. Есть какие-нибудь предложения?

По желанию

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

1. больше похоже на то, что выиграло бы от numpy, чем от панд

Ответ №1:

Вот решение, которое создает таблицу поиска:

 # repeat indices into new dimension
rep_ids = np.tile(indices, (len(df), 1))

# add row index to the tiled ids
range_ids = rep_ids   np.arange(len(df))[:, np.newaxis]

# check which ids make a valid lookup in the df
# TODO: do you really mean > 0 here in the original code, not >= 0?
valid_test = (range_ids > 0) amp; (range_ids < len(df))

# lookup table, using 0 for invalid ids (will be overwritten later)
lookup_ids = np.where(valid_test, range_ids, 0)

# lookup all values
values = df.values[lookup_ids].astype(np.float64)

# set the invalids to nan
values[~valid_test] = np.nan