J'ai un dataframe dans les pandas:
In [10]: df
Out[10]:
col_a col_b col_c col_d
0 France Paris 3 4
1 UK Londo 4 5
2 US Chicago 5 6
3 UK Bristol 3 3
4 US Paris 8 9
5 US London 44 4
6 US Chicago 12 4
J'ai besoin de compter des villes uniques. Je peux compter des états uniques
In [11]: df['col_a'].nunique()
Out[11]: 3
et je peux essayer de compter des villes uniques
In [12]: df['col_b'].nunique()
Out[12]: 5
mais c'est faux parce que les États-Unis et Paris en France sont des villes différentes. Alors maintenant, je fais comme ça:
In [13]: df['col_a_b'] = df['col_a'] + ' - ' + df['col_b']
In [14]: df
Out[14]:
col_a col_b col_c col_d col_a_b
0 France Paris 3 4 France - Paris
1 UK Londo 4 5 UK - Londo
2 US Chicago 5 6 US - Chicago
3 UK Bristol 3 3 UK - Bristol
4 US Paris 8 9 US - Paris
5 US London 44 4 US - London
6 US Chicago 12 4 US - Chicago
In [15]: df['col_a_b'].nunique()
Out[15]: 6
Peut-être qu'il y a un meilleur moyen? Sans créer une colonne supplémentaire.
En utilisant ngroups
df.groupby(['col_a', 'col_b']).ngroups
Out[101]: 6
Ou en utilisant set
len(set(Zip(df['col_a'],df['col_b'])))
Out[106]: 6
Vous pouvez sélectionner col_a et col_b , supprimer les doublons, puis vérifier le shape/len du bloc de données de résultat:
df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 6
len(df[['col_a', 'col_b']].drop_duplicates())
# 6
Étant donné que groupby
ignore NaN
s et peut invoquer inutilement un processus de tri, choisissez en conséquence la méthode à utiliser si vous avez NaN
s dans les colonnes:
Considérons un bloc de données comme suit:
df = pd.DataFrame({
'col_a': [1,2,2,pd.np.nan,1,4],
'col_b': [2,2,3,pd.np.nan,2,pd.np.nan]
})
print(df)
# col_a col_b
#0 1.0 2.0
#1 2.0 2.0
#2 2.0 3.0
#3 NaN NaN
#4 1.0 2.0
#5 4.0 NaN
Calendrier :
df = pd.concat([df] * 1000)
%timeit df.groupby(['col_a', 'col_b']).ngroups
# 1000 loops, best of 3: 625 µs per loop
%timeit len(df[['col_a', 'col_b']].drop_duplicates())
# 1000 loops, best of 3: 1.02 ms per loop
%timeit df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 1000 loops, best of 3: 1.01 ms per loop
%timeit len(set(Zip(df['col_a'],df['col_b'])))
# 10 loops, best of 3: 56 ms per loop
%timeit len(df.groupby(['col_a', 'col_b']))
# 1 loop, best of 3: 260 ms per loop
Résultat :
df.groupby(['col_a', 'col_b']).ngroups
# 3
len(df[['col_a', 'col_b']].drop_duplicates())
# 5
df[['col_a', 'col_b']].drop_duplicates().shape[0]
# 5
len(set(Zip(df['col_a'],df['col_b'])))
# 2003
len(df.groupby(['col_a', 'col_b']))
# 2003
Donc la différence:
Option 1:
df.groupby(['col_a', 'col_b']).ngroups
est rapide et exclut les lignes contenant NaN
s.
Option 2 et 3:
len(df[['col_a', 'col_b']].drop_duplicates())
df[['col_a', 'col_b']].drop_duplicates().shape[0]
Plutôt rapide, il considère NaN
s comme une valeur unique.
Option 4 & 5:
len(set(Zip(df['col_a'],df['col_b'])))
len(df.groupby(['col_a', 'col_b']))
lent, et il suit la logique que numpy.nan == numpy.nan
est False, donc différent (nan, nan) les lignes sont considérées comme différentes.
In [105]: len(df.groupby(['col_a', 'col_b']))
Out[105]: 6