Оптимизация поиска с помощью мультииндекса панд - PullRequest
0 голосов
/ 15 ноября 2018

Я хочу знать, есть ли способ оптимизировать поиск, который я делаю.У меня есть многоиндексный (3 уровня) фрейм данных df, например:

IndexID IndexDateTime IndexAttribute ColumnA ColumnB
   1      2015-02-05        8           A       B
   1      2015-02-05        7           C       D
   1      2015-02-10        7           X       Y

Моя проблема в том, что я хочу знать, есть ли у определенной даты, скажем, например, 2015-02-10 данные вColumnA, с теми же IndexID и IndexAttribute, заданным днем ​​ранее (5 в данном случае), и, если оно есть, получите его и добавьте в новый столбец, например:

IndexID IndexDateTime IndexAttribute ColumnA ColumnB NewColumn
   1      2015-02-05        8           A       B       -1
   1      2015-02-05        7           C       D       -1
   1      2015-02-10        7           X       Y        C

Я хочу выполнить этот поиск для каждой строки в моем фрейме данных, который имеет 19 миллионов строк.Вот как я это делаю:

df['NewColumn'] = df.apply(lambda r: get_data(df, r.IndexID, r.IndexDateTime , r.IndexAttribute , 5), axis=1)

Где get_data:

def get_data(df, IndexID, IndexDateTime , IndexAttribute , days_before):
    idx = pd.IndexSlice
    date = (IndexID - pd.to_timedelta(days_before, 'd'))
    try:
        res = df.loc[idx[IndexID, date, IndexAttribute ],'ColumnA']
        return res
    except KeyError:
        return -1

Это очень медленно, занимает более 2 часов.Мне было интересно, может ли это быть быстрее.Проблемы:

  • Дата для поиска может существовать, а может и не существовать.
  • Для каждого IndexDateTame я не могу знать, сколько IndexAttributes.Они считаются целыми числами и располагаются в порядке убывания.

Я не могу сделать сдвиг, потому что не знаю, сколько данных находится в середине двух строк.Некоторые идеи?Спасибо!

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Использование numpy может быть довольно быстрым. Вам просто нужно перебрать столбцы в кадре данных в виде массива numpy. Надеюсь, это поможет:

%time
def myfunc(df, days_before=5):

     # Fill A column witH -1s
     result = -np.ones_like(df.values[:, -1:])

     # Slice the first 3 columns and shift the dates 
     # to get the index that we are looking for
     idx = np.array((df['IndexID'].values,
                     df['IndexDateTime'] - pd.to_timedelta(days_before, 'd'),
                     df['IndexAttribute'].values)).T

     # Look for days matching in the first 3 columns
     _idx_comp = df.values[:, :3][np.newaxis, :] == np.array(idx)[:, np.newaxis]

     # Get the index where there is a match
     # between the row of the dataframe and the desired searched rows
     idx_found = np.where(np.all(_idx_comp, axis=-1))

     # Assign the corresponding rows to its required value
     result[idx_found[0]] = df['ColumnA'].values[idx_found[-1]]

     return result

df.assign(NewColumn=myfunc(df))

CPU times: user 2 µs, sys: 1e+03 ns, total: 3 µs
Wall time: 5.96 µs

   IndexID IndexDateTime  IndexAttribute ColumnA ColumnB NewColumn
0        1    2015-02-05               8       A       B        -1
1        1    2015-02-05               7       C       D        -1
2        1    2015-02-10               7       X       Y         C
0 голосов
/ 15 ноября 2018

Это решение O (mn), но должно быть быстрее, чем оригинальное

l = []
for _, y in df.groupby(level=[0, 2], sort=False):
    s = y.index.get_level_values(level=1).values
    l.append(((s - s[:, None]) / np.timedelta64(1, 'D') == -5).dot(y.ColumnA.values))

df['NewCOL'] = np.concatenate(l)
df

Out[48]: 
                                     ColumnA ColumnB NewCOL
IndexID IndexDateTime IndexAttribute                       
1       2015-02-05    8                    A       B       
                      7                    C       D       
        2015-02-10    7                    X       Y      C
...