Pandas | Fillna между указанными c индексами (которые меняются со строкой) - PullRequest
1 голос
/ 24 марта 2020

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

Проблема заключается в заполнении нулями в pandas DataFrame по строкам, но с учетом индекса начала и конца для каждого столбца (поэтому цель состоит не в том, чтобы заполнить весь столбец, а только между предоставленными индексами)

Пример:

Мы начинаем с определения нашего фрейма данных, который должен быть заполнен, и другого с индексами для каждой строки

a = pd.DataFrame(index=range(3), columns=range(10))
values = {0: [3, 7], 1: [2, 4], 2: [1, 5]}
for k, v in values.items():
    a.iloc[k, v] = 1

b = pd.DataFrame({'start': [1, 2, 1], 'end': [7,6,8]})


>>>a
     0    1    2    3    4    5    6    7    8    9
0  NaN  NaN  NaN    1  NaN  NaN  NaN    1  NaN  NaN
1  NaN  NaN    1  NaN    1  NaN  NaN  NaN  NaN  NaN
2  NaN    1  NaN  NaN  NaN    1  NaN  NaN  NaN  NaN

>>>b
   start  end
0      1    7
1      2    6
2      1    8

Ожидаемый результат (конечный индекс не включается)

    0    1  2  3  4  5    6    7   8   9
0 NaN  0.0  0  1  0  0  0.0  1.0 NaN NaN
1 NaN  NaN  1  0  1  0  NaN  NaN NaN NaN
2 NaN  1.0  0  0  0  1  0.0  0.0 NaN NaN

В настоящий момент я создал функцию, которая берет zip из каждой строки в кадре данных, выполняет заполнение строки и возвращает ее возвращает значения, а затем я воссоздаю фрейм данных

def _fill_slice(row_ind, value=0):
    row, ind = row_ind
    row[1].iloc[int(ind[0]):int(ind[1])].fillna(value, inplace=True)
    return row[1].values

>>>pd.DataFrame(map(_fill_slice, zip(a.iterrows(), b.values)))
    0    1  2  3  4  5    6    7   8   9
0 NaN  0.0  0  1  0  0  0.0  1.0 NaN NaN
1 NaN  NaN  1  0  1  0  NaN  NaN NaN NaN
2 NaN  1.0  0  0  0  1  0.0  0.0 NaN NaN

Это позволяет мне позже отправлять строки в несколько процессов через imap

pd.DataFrame(pool.imap(_fill_slice, zip(a.iterrows(), b.values), chunksize=chunksize))

В данный момент моя производительность составляет ~ 15 минут для ~ 4M строк, но я чувствую, что должен быть лучший способ сделать это.

Одно из решений, которое я собираюсь попробовать, состоит в том, чтобы сгруппировать одинаковые начало и конец и передать фрагменты объекта. тасет в каждый раз. Другая возможность состоит в том, чтобы запустить два цикла с вышеупомянутой идеей разбиения на части, заполнив все до конечных индексов, а затем вернув np.nan до начальных индексов. Это может уменьшить количество общих итераций.

Есть еще идеи? Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 24 марта 2020

Вот один из рычагов broadcasting + masking -

s = b.start.values
e = b.end.values
R = np.arange(a.shape[1])
a.values[a.isnull().values & (s[:,None]<=R) & (e[:,None]>R)] = value
0 голосов
/ 24 марта 2020

вот еще один способ с pandas, конечно, не такой эффективный, как numpy, хотя:

s = b.agg(tuple,1).map(lambda x: range(*x)).explode().to_frame()
a = a.fillna(s.assign(val=0).set_index(0,append=True)['val'].unstack(0))

print(a)

     0    1  2  3  4  5    6    7    8    9
0  NaN    0  0  1  0  0    0    1  NaN  NaN
1  NaN  NaN  1  0  1  0  NaN  NaN  NaN  NaN
2  NaN    1  0  0  0  1    0    0  NaN  NaN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...