Il y a beaucoup de questions ( 1 , 2 , 3 ) traitant du comptage des valeurs dans un série unique .
Cependant, les questions portant sur la meilleure façon de compter sont moins nombreuses. des combinaisons de deux ou plusieurs séries . Des solutions sont présentées ( 1 , 2 ), mais la question de savoir quand et pourquoi il faut les utiliser n'est pas abordée.
Vous trouverez ci-dessous une analyse comparative de trois méthodes potentielles. J'ai deux questions spécifiques :
- Pourquoi est-ce que
grouper
plus efficace quecount
? J'attendaiscount
est le plus efficace, car il est implémenté en C. La performance supérieure degrouper
persiste même si le nombre de colonnes passe de 2 à 4. - Pourquoi est-ce que
value_counter
sous-performancegrouper
à ce point ? Cela est-il dû au coût de la construction d'une liste ou d'une série à partir d'une liste ?
Je comprends que les résultats sont différents, et cela devrait également éclairer le choix. Par exemple, le filtrage par nombre est plus efficace avec des données contiguës. numpy
des tableaux par rapport à la compréhension d'un dictionnaire :
x, z = grouper(df), count(df)
%timeit x[x.values > 10] # 749µs
%timeit {k: v for k, v in z.items() if v > 10} # 9.37ms
Cependant, ma question porte sur performance du bâtiment résultats comparables dans une série contre le dictionnaire. Mes connaissances en C sont limitées, mais j'apprécierais toute réponse qui pourrait m'indiquer la logique sous-jacente à ces méthodes.
Code d'évaluation comparative
import pandas as pd
import numpy as np
from collections import Counter
np.random.seed(0)
m, n = 1000, 100000
df = pd.DataFrame({'A': np.random.randint(0, m, n),
'B': np.random.randint(0, m, n)})
def grouper(df):
return df.groupby(['A', 'B'], sort=False).size()
def value_counter(df):
return pd.Series(list(zip(df.A, df.B))).value_counts(sort=False)
def count(df):
return Counter(zip(df.A.values, df.B.values))
x = value_counter(df).to_dict()
y = grouper(df).to_dict()
z = count(df)
assert (x == y) & (y == z), "Dictionary mismatch!"
for m, n in [(100, 10000), (1000, 10000), (100, 100000), (1000, 100000)]:
df = pd.DataFrame({'A': np.random.randint(0, m, n),
'B': np.random.randint(0, m, n)})
print(m, n)
%timeit grouper(df)
%timeit value_counter(df)
%timeit count(df)
Résultats de l'analyse comparative
Exécuté sur python 3.6.2, pandas 0.20.3, numpy 1.13.1
Spécifications de la machine : Windows 7 64-bit, Dual-Core 2.5 GHz, 4GB RAM.
Clé : g = grouper
, v = value_counter
, c = count
.
m n g v c
100 10000 2.91 18.30 8.41
1000 10000 4.10 27.20 6.98[1]
100 100000 17.90 130.00 84.50
1000 100000 43.90 309.00 93.50
1 Ce n'est pas une faute de frappe.
2 votes
Une petite barre latérale -
pd.Series(list(zip(df.A, df.B))).value_counts(sort=False)
améliore un petit - donc je suppose que le tri à contribuer en tant que frais généraux en plus dulist
moulage0 votes
Je ne suis pas du tout surpris que la fonction conçue sur mesure pour ce cas d'utilisation précis soit la plus performante.
pandas
en sait bien plus sur la structure de ses données queCounter
fait. en outre,pandas
est probablement beaucoup moins gourmand en mémoire puisqu'il sait réutiliser sa mémoire existante.0 votes
@BallpointBen, D'un point de vue philosophique, votre commentaire est parfaitement logique. Pouvez-vous identifier les raisons spécifiques sous-jacentes (par exemple, le hachage, le coût de l'itération, etc.) en vous référant au code source ?
0 votes
De même, pour une version encore plus performante de
groupby
, passesort=False
agroupby
.0 votes
@Parfait, Mis à jour avec (a)
np.random.seed(0)
(b) des versions plus récentes de Python / numpy / pandas + spécifications machine incluses, (c)sort=False
parapandas
méthodes.