Je recherche un moyen simple de trier une série pandas par données décroissantes, puis par index croissant. J'ai cherché dans les documents et sur Stackoverflow mais je n'ai pas trouvé de moyen simple.

La série compte environ 5000 entrées et est le résultat d'une analyse tf-idf avec NLTK.

Cependant, ci-dessous, je fournis un très petit échantillon des données pour illustrer le problème.

import pandas as pd

index = ['146tf150p', 'anytime', '645', 'blank', 'anything']
tfidf = [1.000000, 1.000000, 1.000000, 0.932702, 1.000000]

tfidfmax = pd.Series(tfidf, index=index)

Pour l'instant, je ne fais que convertir la série en un DataFrame, réinitialiser l'index, faire le tri puis définir l'index, mais je pense que c'est un grand détour.

frame = pd.DataFrame(tfidfmax , columns=['data']).reset_index().sort_values(['data','index'], ascending=[False, True]).set_index(['index'])
3.02 ms ± 102 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

J'attends vos suggestions avec impatience!

EDIT 24/04 - Résumé de la solution:

Utilisation de Numpy lexsort:

%timeit tfidfmax[np.lexsort((tfidfmax.index, -tfidfmax.values))]
261 µs ± 9.27 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Utilisation de listes triées () et zippées

def a():
    a = list(zip(*sorted(zip(index, tfidf),key=lambda x:(-x[1],x[0]))))
    pd.Series(a[1], index=a[0])
%timeit a() 
203 µs ± 12.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
2
Steven Van Dorpe 13 avril 2018 à 13:28

3 réponses

Meilleure réponse

Vous pouvez utiliser numpy.lexsort pour cela :

res = tfidfmax[np.lexsort((tfidfmax.index, -tfidfmax.values))]

print(res)

# 146tf150p    1.000000
# 645          1.000000
# anything     1.000000
# anytime      1.000000
# blank        0.932702
# dtype: float64

Notez l'ordre inverse dans la syntaxe: le code ci-dessus trie d'abord par valeurs décroissantes, puis par index croissant.

2
jpp 13 avril 2018 à 10:32

Facile:

In [15]: pd.Series(tfidfmax.sort_values(ascending=False),index=tfidfmax.sort_index().index)
Out[15]: 
146tf150p    1.000000
645          1.000000
anything     1.000000
anytime      1.000000
blank        0.932702
dtype: float64

Ou moyen plus rapide:

In [26]: pd.Series(-np.sort(-tfidfmax),index=np.sort(tfidfmax.index))
Out[26]: 
146tf150p    1.000000
645          1.000000
anything     1.000000
anytime      1.000000
blank        0.932702
dtype: float64

In [17]: %timeit tfidfmax[np.lexsort((tfidfmax.index, -tfidfmax.values))]
10000 loops, best of 3: 104 µs per loop

In [18]: %timeit pd.Series(tfidfmax.sort_values(ascending=False),index=tfidfmax.sort_index().index)
1000 loops, best of 3: 406 µs per loop

In [27]: %timeit pd.Series(-np.sort(-tfidfmax),index=np.sort(tfidfmax.index))
10000 loops, best of 3: 91.2 µs per loop
1
shivsn 13 avril 2018 à 10:42

Utilisez la fonction sorted par zip les deux list s créez de nouveaux Series par zip :

index = ['146tf150p', 'anytime', '645', 'blank', 'anything']
tfidf = [1.000000, 1.000000, 2.000000, 0.932702, 2.000000]

a = list(zip(*sorted(zip(index, tfidf),key=lambda x:(-x[1],x[0]))))

#if input is Series
#a = list(zip(*sorted(zip(tfidfmax.index,tfidfmax),key=lambda x:(-x[1],x[0]))))
s = pd.Series(a[1], index=a[0])
print (s)
645          2.000000
anything     2.000000
146tf150p    1.000000
anytime      1.000000
blank        0.932702
dtype: float64
3
jezrael 13 avril 2018 à 11:22