Подмножество панд данных, основанное на кортеже - PullRequest
0 голосов
/ 14 октября 2018

У меня есть такой набор данных

Firstnames = ['AA','BB','CC','AA','CC']
Lastnames = ['P', 'Q', 'R', 'P', 'R']
values = [10, 13, 3, 22, 45]

df = pd.DataFrame(data = list(zip(Firstnames,Lastnames,values)), \
                  columns=['Firstnames','Lastnames','values'])
df

    Firstnames  Lastnames   values
0   AA          P           10
1   BB          Q           13
2   CC          R           3
3   AA          P           22
4   CC          R           45

У меня есть массив кортежей, подобный этому

lst = array([('AA', 'P'), ('BB', 'Q')])

Я хочу установить поднабор df, такой что Firstname == 'AA' & Lastnames == 'P' или Firstname == 'BB' & Lastnames == 'Q'

Я могу сделать это вручную, однако мой массив довольно большой, и я хочу сделать это программно

Мой ожидаемый результат будет

Firstnames  Lastnames   values
AA          P           10
AA          P           22
BB          Q           13

Ответы [ 3 ]

0 голосов
/ 14 октября 2018

pd.Index.isin

Преобразовать выбранную серию в объект MultiIndex, затем использовать pd.Index.isin со списком кортежей:

lst = [('AA', 'P'), ('BB', 'Q')]
df_masked = df[df.set_index(['Firstnames', 'Lastnames']).index.isin(lst)]

Сравнительный анализ производительности

# Python 3.6.5, Pandas v0.23.0

lst = [('AA', 'P'), ('BB', 'Q')]
df = pd.concat([df]*10000).reset_index()

%timeit df[df.set_index(['Firstnames', 'Lastnames']).index.isin(lst)]  # 23.4 ms
0 голосов
/ 14 октября 2018

Вы можете использовать numpy трансляцию здесь для эффективного решения.На массивных DataFrames вы можете начать видеть снижение производительности, после чего вы должны использовать pd.Index.isin.Вам следует избегать приведения к tuple и использовать этот тип сравнения для всех размеров DataFrames.

Настройка

lst = np.array([('AA', 'P'), ('BB', 'Q')])

idx = (df.values[:, :2] == lst[:, None]).any(axis=(0, -1))
df[idx]

  Firstnames Lastnames  values
0         AA         P      10
1         BB         Q      13
3         AA         P      22

Производительность

df = pd.concat([df]*10000).reset_index()

In [228]: %timeit df[df.set_index(['Firstnames', 'Lastnames']).index.isin(lst)]
15.5 ms ± 270 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [236]: %%timeit
     ...: mask = df[['Firstnames', 'Lastnames']].agg(tuple, 1).isin(lst)
     ...: df[mask]
     ...:
853 ms ± 22.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [231]: %%timeit
     ...: idx = (df.values[:, :2] == lst[:, None]).any(axis=(0, -1))
     ...: df[idx]
     ...:
11.6 ms ± 264 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
0 голосов
/ 14 октября 2018

agg + isin

Поскольку кортежи можно хэшировать, вы можете использовать isin и сравнить агрегированные значения со своими last.Помогает lst и список напрямую вместо np.array.

>>> lst = [('AA', 'P'), 
           ('BB', 'Q')]

>>> mask = df[['Firstnames', 'Lastnames']].agg(tuple, 1).isin(lst)
>>> df[mask]

    Firstnames  Lastnames   values
0   AA          P           10
1   BB          Q           13
3   AA          P           22

Если хотите, вы можете sort_values по именам

>>> df[mask].sort_values(by=['Firstnames', 'Lastnames'])

    Firstnames  Lastnames   values
0   AA          P           10
3   AA          P           22
1   BB          Q           13

pd.concat

Вы также можете использовать список понимания и pd.concat для меньших lst s

>>> pd.concat([df[df.Firstnames.eq(a) & df.Lastnames.eq(b)] for a,b in lst])

    Firstnames  Lastnames   values
0   AA          P           10
3   AA          P           22
1   BB          Q           13

Время:

Маленький lst, большой df

df = pd.concat([df]*10000).reset_index(drop=True)

%timeit mask = df[['Firstnames', 'Lastnames']].agg(tuple, 1).isin(lst); df[mask].sort_values(by=['Firstnames', 'Lastnames'])
942 ms ± 71.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit pd.concat([df[df.Firstnames.eq(a) & df.Lastnames.eq(b)] for a,b in lst])
16.2 ms ± 355 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Для больших lst и маленький df

c = list(map(''.join, itertools.product(string.ascii_uppercase, string.ascii_uppercase)))
lst = [(a,b) for a,b in zip(c, list(string.ascii_uppercase)*26)]
df = pd.DataFrame({'Firstnames': c, 'Lastnames': list(string.ascii_uppercase)*26, 'values': 10})

%timeit mask = df[['Firstnames', 'Lastnames']].agg(tuple, 1).isin(lst); df[mask].sort_values(by=['Firstnames', 'Lastnames'])
15.1 ms ± 301 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit pd.concat([df[df.Firstnames.eq(a) & df.Lastnames.eq(b)] for a,b in lst])
781 ms ± 33.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...