2010-07-24 6 views
3

j'ai besoin de trier la liste suivante de tuples en Python:Python tri Question

ListOfTuples = [('10', '2010 Jan 1;', 'Rapoport AM', 'Role of antiepileptic drugs as preventive agents for migraine', '20030417'), ('21', '2009 Nov;', 'Johannessen SI', 'Antiepilepticdrugs in epilepsy and other disorders--a population-based study of prescriptions', '19679449'),...] 

Mon but est de le commander par décroissant année (listOfTuples [2]) et par Croissant Auteur (listOfTuples [2]):

sorted(result, key = lambda item: (item[1], item[2])) 

Mais cela ne fonctionne pas. Comment puis-je obtenir la stabilité de tri?

+0

Quels sont les formats de date possibles? ici nous voyons "année/monthabbr/jour" et "année/monthabbr". Est-il possible de rencontrer seulement "l'année"? – tokland

+0

Oui c'est possible car le format de données n'est pas régulier. –

Répondre

4
def descyear_ascauth(atup): 
    datestr = atup[1] 
    authstr = atup[2] 
    year = int(datestr.split(None, 1)[0]) 
    return -year, authstr 

... sorted(result, key=descyear_ascauth) ... 

Notes: vous devez extraire l'année comme un entier (non comme une chaîne), de sorte que vous pouvez changer son signe - ce dernier étant l'affaire clé afin de satisfaire la partie "décroissante" des spécifications. Presser tout cela dans un lambda serait possible, mais il n'y a absolument aucune raison de le faire et sacrifier encore plus de lisibilité, quand un def fonctionnera aussi bien (et beaucoup plus lisible).

+0

Grazie mille, sei sempre gentilissimo! :) Quelle approche devrais-je utiliser pour ajouter une autre clé de commande telle que "mois"? Devrais-je associer les noms des mois à un dict ('jan': 1, 'feb: 2')? –

+0

@Gianluca, en utilisant un dict explicite vous donne un contrôle total, et est donc ce que je recommanderais. Vous pouvez jouer avec 'list (calendar.month_name)' pour construire le dict par exemple. d'une manière dépendante des paramètres régionaux, mais c'est beaucoup plus compliqué que nécessaire à moins que vous ayez des besoins très spécifiques dans cette direction. –

+0

Merci d'avoir répondu :). En ce moment je ne peux pas décider quelle réponse choisir parce que @Duncan a également affiché une approche de travail sur mon problème. Jusqu'à présent, c'est une question de goût (lisibilité vs compacité) et de performance (en utilisant "astuces" vs "faire la façon Python") ... –

0

Voici un idiome qui fonctionne pour tout, même chose que vous ne pouvez pas niez, par exemple des chaînes:

data = [ ('a', 'a'), ('a', 'b'), ('b','a') ] 

def sort_func(a, b): 
    # compare tuples with the 2nd entry switched 
    # this inverts the sorting on the 2nd entry 
    return cmp((a[0], b[1]), (b[0], a[1])) 

print sorted(data)     # [('a', 'a'), ('a', 'b'), ('b', 'a')] 
print sorted(data, cmp=sort_func)  # [('a', 'b'), ('a', 'a'), ('b', 'a')] 
+0

'cmp' ne fonctionne plus dans Python 3, bien qu'il y ait' cmp_to_key' dans functools. – kennytm

2

Le plus simple est de trier sur chaque valeur de clé séparément. Commencez par la touche la moins significative et progressez jusqu'au plus significatif.

Donc dans ce cas:

import operator 
ListOfTuples.sort(key=operator.itemgetter(2)) 
ListOfTuples.sort(key=lambda x: x[1][:4], reverse=True) 

Cela fonctionne parce que le tri de Python est toujours stable, même lorsque vous utilisez l'inverse drapeau: à savoir inverse ne veut pas juste une sorte puis inverse (qui perdrait la stabilité, elle conserve la stabilité après la marche arrière.

Bien sûr, si vous avez beaucoup de colonnes clés cela peut être inefficace car il fait une sorte complète à plusieurs reprises.

vous n'avez pas à convertir l'année à un numéro ainsi que c'est un véritable tri inverse, tu gh tu pourrais si tu le voulais.

+0

Votre solution est compacte et pythonique mais @ Alex est plus rapide. Impossible de décider qui est le gagnant :) –

0

est ici une solution grossière qui prend mois abbreviature et jour (si elle est trouvée) en compte:

import time 
import operator 

def sortkey(seq): 
    strdate, author = seq[1], seq[2] 
    spdate = strdate[:-1].split() 
    month = time.strptime(spdate[1], "%b").tm_mon 
    date = [int(spdate[0]), month] + map(int, spdate[2:]) 
    return map(operator.neg, date), author 

print sorted(result, key=sortkey) 

« % b » est le nom abrégé du mois de locale, vous pouvez utiliser un dictionnaire si vous préférez ne pas traiter locales.

0

Voici la version lambda de la réponse d'Alex. Je pense que cela semble plus compact que la réponse de Duncan maintenant, mais de toute évidence, beaucoup de la lisibilité de la réponse d'Alex a été perdue.

sorted(ListOfTuples, key=lambda atup: (-int(atup[1].split(None, 1)[0]), atup[2])) 

La lisibilité et l'efficacité doivent généralement être préférées à la compacité.