Je veux vérifier rapidement et facilement si toutes les valeurs de colonne pour counts
sont les mêmes dans une trame de données:
Dans:
import pandas as pd
d = {'names': ['Jim', 'Ted', 'Mal', 'Ted'], 'counts': [3, 4, 3, 3]}
pd.DataFrame(data=d)
En dehors:
names counts
0 Jim 3
1 Ted 4
2 Mal 3
3 Ted 3
Je veux juste une condition simple que if all counts = same value
Puis print('True')
.
Existe-t-il un moyen rapide de procéder?
Une façon efficace de le faire est de comparer la première valeur avec les autres et d'utiliser all
:
def is_unique(s):
a = s.to_numpy() # s.values (pandas<0.24)
return (a[0] == a[1:]).all()
is_unique(df['counts'])
# False
Dans le cas où vous souhaitez effectuer la même tâche sur une trame de données entière, nous pouvons étendre ce qui précède en définissant axis=0
dans all
:
def unique_cols(df):
a = df.to_numpy() # df.values (pandas<0.24)
return (a[0] == a[1:]).all(0)
Voici un point de référence des méthodes ci-dessus par rapport à d'autres approches, telles que l'utilisation de nunique
(pour a pd.Series
):
s_num = pd.Series(np.random.randint(0, 1_000, 1_100_000))
perfplot.show(
setup=lambda n: s_num.iloc[:int(n)],
kernels=[
lambda s: s.nunique() == 1,
lambda s: is_unique(s)
],
labels=['nunique', 'first_vs_rest'],
n_range=[2**k for k in range(0, 20)],
xlabel='N'
)
Dans le cas d'un pd.DataFrame
, une bonne option pourrait aussi être numba
, d'autant plus que nous pouvons profiter du raccourci dès que nous voyons une valeur répétée dans une colonne donnée (cela ne fonctionnera qu'avec des données numériques):
from numba import njit
@njit
def unique_cols_nb(a):
n_cols = a.shape[1]
out = np.zeros(n_cols, dtype=np.int8)
for i in range(n_cols):
init = a[0, i]
for j in a[1:, i]:
if j != init:
break
else:
out[i] = 1
return out
Si nous comparons les trois méthodes:
df = pd.DataFrame(np.concatenate([np.random.randint(0, 1_000, (500_000, 200)),
np.zeros((500_000, 10))], axis=1))
perfplot.show(
setup=lambda n: df.iloc[:int(n),:],
kernels=[
lambda df: (df.nunique(0) == 1).values,
lambda df: unique_cols_nb(df.values).astype(bool),
lambda df: unique_cols(df)
],
labels=['nunique', 'unique_cols_nb', 'unique_cols'],
n_range=[2**k for k in range(0, 20)],
xlabel='N'
)
Mettre à jour à l'aide de np.unique
len(np.unique(df.counts))==1
False
Ou
len(set(df.counts.tolist()))==1
Ou
df.counts.eq(df.counts.iloc[0]).all()
False
Ou
df.counts.std()==0
False
Je pense que nunique
fait beaucoup plus de travail que nécessaire. L'itération peut s'arrêter à la première différence. Cette solution simple et générique utilise itertools
:
import itertools
def all_equal(iterable):
"Returns True if all elements are equal to each other"
g = itertools.groupby(iterable)
return next(g, True) and not next(g, False)
all_equal(df.counts)
On peut même l'utiliser pour trouver en une seule fois toutes colonnes avec un contenu constant:
constant_columns = df.columns[df.apply(all_equal)]
Une alternative un peu plus lisible mais moins performante:
df.counts.min() == df.counts.max()
Ajouter skipna=False
ici si nécessaire.