Лучше избегать DataFrames с MultiIndex при фильтрации? - PullRequest
0 голосов
/ 12 марта 2020

Эксперимент 1: выбор из DataFrame с индексом диапазона по умолчанию -

In [167]: df_range = pd.read_csv('extract.csv')                                                      

In [168]: df_range                                                                                   
Out[168]: 
        frame  face  lmark   x   y
0           1   NaN    NaN NaN NaN
1           2   NaN    NaN NaN NaN
2           3   NaN    NaN NaN NaN
3           4   NaN    NaN NaN NaN
4           5   NaN    NaN NaN NaN
...       ...   ...    ...  ..  ..
312809   5146   NaN    NaN NaN NaN
312810   5147   NaN    NaN NaN NaN
312811   5148   NaN    NaN NaN NaN
312812   5149   NaN    NaN NaN NaN
312813   5150   NaN    NaN NaN NaN

[312814 rows x 5 columns]

выбор значений индекса, исключая frame 5148 -

In [170]: ind = df_range.loc[(df_range['frame'] != 5148)].index.values                               

In [171]: ind                                                                                        
Out[171]: array([     0,      1,      2, ..., 312810, 312812, 312813])

выбор записей из df_range исключая frame 5148 -

In [173]: df_range.loc[ind]                                                                          
Out[173]: 
        frame  face  lmark   x   y
0           1   NaN    NaN NaN NaN
1           2   NaN    NaN NaN NaN
2           3   NaN    NaN NaN NaN
3           4   NaN    NaN NaN NaN
4           5   NaN    NaN NaN NaN
...       ...   ...    ...  ..  ..
312808   5145   NaN    NaN NaN NaN
312809   5146   NaN    NaN NaN NaN
312810   5147   NaN    NaN NaN NaN
312812   5149   NaN    NaN NaN NaN
312813   5150   NaN    NaN NaN NaN

[312813 rows x 5 columns]

In [174]: timeit df_range.loc[ind]                                                                   
14.1 ms ± 12.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Эксперимент 2: выбор из DataFrame с MuitiIndex -

In [177]: df_multi = pd.read_csv('extract.csv').set_index(['frame', 'face', 'lmark'])                

In [178]: df_multi                                                                                   
Out[178]: 
                   x   y
frame face lmark        
1     NaN  NaN   NaN NaN
2     NaN  NaN   NaN NaN
3     NaN  NaN   NaN NaN
4     NaN  NaN   NaN NaN
5     NaN  NaN   NaN NaN
...               ..  ..
5146  NaN  NaN   NaN NaN
5147  NaN  NaN   NaN NaN
5148  NaN  NaN   NaN NaN
5149  NaN  NaN   NaN NaN
5150  NaN  NaN   NaN NaN

[312814 rows x 2 columns]

выбор значений кадра, исключая frame 5148 -

In [215]: frames = df_range.loc[ind]['frame'].drop_duplicates().values                               

In [216]: frames                                                                                     
Out[216]: array([   1,    2,    3, ..., 5147, 5149, 5150])

выбор записей из df_multi, исключая frame 5148 -

In [218]: df_multi.loc[frames]                                                                       
Out[218]: 
                   x   y
frame face lmark        
1     NaN  NaN   NaN NaN
2     NaN  NaN   NaN NaN
3     NaN  NaN   NaN NaN
4     NaN  NaN   NaN NaN
5     NaN  NaN   NaN NaN
...               ..  ..
5145  NaN  NaN   NaN NaN
5146  NaN  NaN   NaN NaN
5147  NaN  NaN   NaN NaN
5149  NaN  NaN   NaN NaN
5150  NaN  NaN   NaN NaN

[312813 rows x 2 columns]

In [219]: timeit df_multi.loc[frames]                                                                
7.83 s ± 607 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Заключение

Оба метода выбирают правильный результат установлен, но фильтрация DataFrame с MultiIndex выглядит на несколько порядков медленнее, чем при использовании индекса диапазона по умолчанию. Вы согласны?

Обновление 13-03-2020 @ALollz - спасибо за вдохновение. Вот гораздо более быстрый способ фильтрации DataFrame с помощью MultiIndex -

In [40]: timeit df_multi.loc[df_multi.index.get_level_values('frame') != 5148]                       
4.53 ms ± 15.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [41]: df_multi.loc[df_multi.index.get_level_values('frame') != 5148]                              
Out[41]: 
                   x   y
frame face lmark        
1     NaN  NaN   NaN NaN
2     NaN  NaN   NaN NaN
3     NaN  NaN   NaN NaN
4     NaN  NaN   NaN NaN
5     NaN  NaN   NaN NaN
...               ..  ..
5145  NaN  NaN   NaN NaN
5146  NaN  NaN   NaN NaN
5147  NaN  NaN   NaN NaN
5149  NaN  NaN   NaN NaN
5150  NaN  NaN   NaN NaN

[312813 rows x 2 columns]

1 Ответ

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

Не совсем.

MultiIndex имеет кортежи в качестве индексов. Вы переключаетесь на MultiIndex, но затем по-прежнему предоставляете один массив скаляров в качестве индекса, поэтому pandas тратит много времени, пытаясь выяснить, как именно их выровнять. Если вместо этого вы предоставите правильный массив MultiIndex locs, скорость будет почти одинаковой (хотя, может быть, ~ 10x медленнее)

Пример данных

import pandas as pd
df = pd.concat([pd.DataFrame(range(10**3))]*5, axis=1)
df.columns = range(5)

df_mult = df.copy().set_index([0,1], append=True)

ids = df[df[4].ne(4)].index

%timeit df.loc[ids]
#398 µs ± 5.47 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df_mult.loc[ids]
#121 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# Use the correct MultiIndex locs
ids_mult = df_mult[df_mult[4].ne(4)].index

%timeit df_mult.loc[ids_mult]
#2.57 ms ± 54.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Хотя вы можете просто срез по Boolean Series, который имеет тенденцию быть очень быстрым для большинства больших выборов.

%timeit df_mult[df_mult[4].ne(4)]
#705 µs ± 5.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...