J'aimerais savoir s'il existe un moyen de trouver l'emplacement (index de colonne et de ligne) de la valeur la plus élevée dans un cadre de données. Donc si par exemple mon dataframe ressemble à ceci:
A B C D E
0 100 9 1 12 6
1 80 10 67 15 91
2 20 67 1 56 23
3 12 51 5 10 58
4 73 28 72 25 1
Comment puis-je obtenir un résultat qui ressemble à ceci: [0, 'A']
en utilisant des pandas?
np.argmax
Les numéros argmax
de NumPy peuvent être utiles:
>>> df.stack().index[np.argmax(df.values)]
(0, 'A')
df.values
est un tableau NumPy à deux dimensions:
>>> df.values
array([[100, 9, 1, 12, 6],
[ 80, 10, 67, 15, 91],
[ 20, 67, 1, 56, 23],
[ 12, 51, 5, 10, 58],
[ 73, 28, 72, 25, 1]])
argmax
vous donne l'index pour la valeur maximale du tableau "aplati":
>>> np.argmax(df.values)
0
Maintenant, vous pouvez utiliser cet index pour trouver l'emplacement de la ligne-colonne sur le dataframe empilé:
>>> df.stack().index[0]
(0, 'A')
Si vous en avez besoin rapidement, faites aussi peu que possible. Travailler uniquement sur le tableau NumPy pour trouver les indices np.argmax
semble être préférable:
v = df.values
i, j = [x[0] for x in np.unravel_index([np.argmax(v)], v.shape)]
[df.index[i], df.columns[j]]
Résultat:
[0, 'A']
La synchronisation convient mieux aux trames de données lareg:
df = pd.DataFrame(data=np.arange(int(1e6)).reshape(-1,5), columns=list('ABCDE'))
Trié le plus lent au plus rapide:
%timeit df.mask(~(df==df.max().max())).stack().index.tolist()
33.4 ms ± 982 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit list(df.stack().idxmax())
17.1 ms ± 139 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df.stack().index[np.argmax(df.values)]
14.8 ms ± 392 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
i,j = np.where(df.values == df.values.max())
list((df.index[i].values.tolist()[0],df.columns[j].values.tolist()[0]))
4.45 ms ± 84.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
v = df.values
i, j = [x[0] for x in np.unravel_index([np.argmax(v)], v.shape)]
[df.index[i], df.columns[j]]
499 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
d = {'name': ['Mask', 'Stack-idmax', 'Stack-argmax', 'Where', 'Argmax-unravel_index'],
'time': [33.4, 17.1, 14.8, 4.45, 499],
'unit': ['ms', 'ms', 'ms', 'ms', 'µs']}
timings = pd.DataFrame(d)
timings['seconds'] = timings.time * timings.unit.map({'ms': 1e-3, 'µs': 1e-6})
timings['factor slower'] = timings.seconds / timings.seconds.min()
timings.sort_values('factor slower')
Sortie:
name time unit seconds factor slower
4 Argmax-unravel_index 499.00 µs 0.000499 1.000000
3 Where 4.45 ms 0.004450 8.917836
2 Stack-argmax 14.80 ms 0.014800 29.659319
1 Stack-idmax 17.10 ms 0.017100 34.268537
0 Mask 33.40 ms 0.033400 66.933868
Ainsi, la version "Argmax-unravel_index" semble être plus rapide d’un à deux ordres de grandeur pour les grandes trames de données, c’est-à-dire où la vitesse importe souvent le plus.
Utilisez stack
pour Series
avec MultiIndex
et idxmax
pour l'index de valeur max:
print (df.stack().idxmax())
(0, 'A')
print (list(df.stack().idxmax()))
[0, 'A']
Détail:
print (df.stack())
0 A 100
B 9
C 1
D 12
E 6
1 A 80
B 10
C 67
D 15
E 91
2 A 20
B 67
C 1
D 56
E 23
3 A 12
B 51
C 5
D 10
E 58
4 A 73
B 28
C 72
D 25
E 1
dtype: int64
mask
+ max
df.mask(~(df==df.max().max())).stack().index.tolist()
Out[17]: [(0, 'A')]
À mon avis, pour les grands ensembles de données, stack () devient inefficace, utilisons np.where
pour renvoyer les positions d'index:
i,j = np.where(df.values == df.values.max())
list((df.index[i].values.tolist()[0],df.columns[j].values.tolist()[0]))
Sortie:
[0, 'A']
df = pd.DataFrame(data=np.arange(10000).reshape(-1,5), columns=list('ABCDE'))
> %%timeit i,j = np.where(df.values == df.values.max())
> list((df.index[i].values.tolist()[0],df.columns[j].values.tolist()[0]))
1000 boucles, meilleur de 3: 364 µs par boucle
> %timeit df.mask(~(df==df.max().max())).stack().index.tolist()
100 boucles, le meilleur de 3: 7,68 ms par boucle
> %timeit df.stack().index[np.argmax(df.values)`]
10 boucles, meilleur de 3: 50,5 ms par boucle
> %timeit list(df.stack().idxmax())
1000 boucles, le meilleur des 3: 1,58 ms par boucle
Une base de données encore plus grande:
df = pd.DataFrame(data=np.arange(100000).reshape(-1,5), columns=list('ABCDE'))
Respectivement:
1000 loops, best of 3: 1.62 ms per loop
10 loops, best of 3: 18.2 ms per loop
100 loops, best of 3: 5.69 ms per loop
100 loops, best of 3: 6.64 ms per loop
print('Max value:', df.stack().max())
print('Parameters :', df.stack().idxmax())
C'est la meilleure façon à mon humble avis.
Cela devrait fonctionner:
def max_df(df):
m = None
p = None
for idx, item in enumerate(df.idxmax()):
c = df.columns[item]
val = df[c][idx]
if m is None or val > m:
m = val
p = idx, c
return p
Ceci utilise la fonction idxmax , puis compare toutes les valeurs renvoyées par celle-ci.
Exemple d'utilisation:
>>> df
A B
0 100 9
1 90 8
>>> max_df(df)
(0, 'A')
Voici un one-liner (pour le fun):
def max_df2(df):
return max((df[df.columns[item]][idx], idx, df.columns[item]) for idx, item in enumerate(df.idxmax()))[1:]