[英]Subset pandas data frame based on tuple
我有這樣的數據集
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
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
你也可以使用list comprehension和pd.concat
來獲得更小的lst
>>> 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)
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
您可以在這里使用numpy
廣播來獲得高性能的解決方案。 在大量的DataFrame上,您可能會開始看到性能pd.Index.isin
,此時您應該使用pd.Index.isin
。 您應該避免轉換為tuple
並對所有大小的DataFrame使用該類型的比較。
設定
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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.