Étant donné les données dans un Pandas DataFrame comme le suivant:
Name Amount
---------------
Alice 100
Bob 50
Charlie 200
Alice 30
Charlie 10
Je souhaite sélectionner toutes les lignes où Name
est l'une des nombreuses valeurs d'une collection {Alice, Bob}
Name Amount
---------------
Alice 100
Bob 50
Alice 30
Quel est un moyen efficace de le faire dans Pandas?
Options telles que je les vois
Sélectionnez et fusionnez de nombreuses instructions comme les suivantes
merge(df[df.name = specific_name] for specific_name in names) # something like this
Effectuer une sorte de jointure
Quels sont les compromis de performance ici? Quand une solution est-elle meilleure que les autres? Quelles solutions me manquent?
Bien que l'exemple ci-dessus utilise des chaînes, mon travail réel utilise des correspondances sur 10 à 100 entiers sur des millions de lignes et des opérations NumPy rapides peuvent donc être pertinentes.
Vous pouvez utiliser la méthode isin Series:
In [11]: df['Name'].isin(['Alice', 'Bob'])
Out[11]:
0 True
1 True
2 False
3 True
4 False
Name: Name, dtype: bool
In [12]: df[df.Name.isin(['Alice', 'Bob'])]
Out[12]:
Name Amount
0 Alice 100
1 Bob 50
3 Alice 30
Étant donné que dans votre cas d'utilisation réel, les valeurs dans df['Name']
Sont ints
, vous pourrez peut-être générer le masque booléen plus rapidement en utilisant l'indexation NumPy au lieu de Series.isin
.
idx = np.zeros(N, dtype='bool')
idx[names] = True
df[idx[df['Name'].values]]
Par exemple, compte tenu de cette configuration:
import pandas as pd
import numpy as np
N = 100000
df = pd.DataFrame(np.random.randint(N, size=(10**6, 2)), columns=['Name', 'Amount'])
names = np.random.choice(np.arange(N), size=100, replace=False)
In [81]: %timeit idx = np.zeros(N, dtype='bool'); idx[names] = True; df[idx[df['Name'].values]]
100 loops, best of 3: 9.88 ms per loop
In [82]: %timeit df[df.Name.isin(names)]
10 loops, best of 3: 107 ms per loop
In [83]: 107/9.88
Out[83]: 10.82995951417004
N
est (essentiellement) la valeur maximale que df['Names']
peut atteindre. Si N
est plus petit, l'avantage de vitesse n'est pas aussi important. Avec N = 200
,
In [93]: %timeit idx = np.zeros(N, dtype='bool'); idx[names] = True; df[idx[df['Name'].values]]
10 loops, best of 3: 62.6 ms per loop
In [94]: %timeit df[df.Name.isin(names)]
10 loops, best of 3: 178 ms per loop
In [95]: 178/62.6
Out[95]: 2.8434504792332267
Attention: comme indiqué ci-dessus, il semble y avoir un avantage de vitesse, en particulier lorsque N
devient grand. Cependant, si N
est trop grand, la formation de idx = np.zeros(N, dtype='bool')
peut ne pas être possible.
Verification sanitaire:
expected = df[df.Name.isin(names)]
idx = np.zeros(N, dtype='bool')
idx[names] = True
result = df[idx[df['Name'].values]]
assert expected.equals(result)