web-dev-qa-db-fra.com

Comment compter les enregistrements uniques par deux colonnes dans les pandas?

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.

5
GhostKU

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
17
Wen-Ben

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 NaNs et peut invoquer inutilement un processus de tri, choisissez en conséquence la méthode à utiliser si vous avez NaNs 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 NaNs.

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 NaNs 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.

5
Psidom
In [105]: len(df.groupby(['col_a', 'col_b']))
Out[105]: 6
4
MaxU