web-dev-qa-db-fra.com

Trier la liste en fonction des valeurs d'une autre liste?

J'ai une liste de chaînes comme celle-ci:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Quel est le moyen le plus rapide de trier X en utilisant les valeurs de Y pour obtenir le résultat suivant?

["a", "d", "h", "b", "c", "e", "i", "f", "g"]

L'ordre des éléments ayant la même "clé" n'a pas d'importance. Je peux avoir recours à des constructions for mais je suis curieux de savoir s'il existe un moyen plus court. Aucune suggestion?

250
Legend

Code le plus court

[x for _,x in sorted(Zip(Y,X))]

Exemple:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Z = [x for _,x in sorted(Zip(Y,X))]
print(Z)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

En général

[x for _, x in sorted(Zip(Y,X), key=lambda pair: pair[0])]

Expliqué:

  1. Zip les deux lists.
  2. créer une nouvelle list triée en fonction de Zip à l’aide de sorted() .
  3. en utilisant une liste de compréhension extrait les premiers éléments de chaque paire de la list zippée et triée.

Pour plus d'informations sur la manière de définir\utiliser le paramètre key ainsi que la fonction sorted en général, consultez this .


332
Whatang

Compressez les deux listes ensemble, triez-les, puis prenez les parties souhaitées:

>>> yx = Zip(Y, X)
>>> yx
[(0, 'a'), (1, 'b'), (1, 'c'), (0, 'd'), (1, 'e'), (2, 'f'), (2, 'g'), (0, 'h'), (1, 'i')]
>>> yx.sort()
>>> yx
[(0, 'a'), (0, 'd'), (0, 'h'), (1, 'b'), (1, 'c'), (1, 'e'), (1, 'i'), (2, 'f'), (2, 'g')]
>>> x_sorted = [x for y, x in yx]
>>> x_sorted
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Combinez ces éléments pour obtenir:

[x for y, x in sorted(Zip(Y, X))]
92
Ned Batchelder

Aussi, si cela ne vous dérange pas d'utiliser des tableaux numpy (ou si vous avez déjà déjà affaire à des tableaux numpy ...), voici une autre solution intéressante:

people = ['Jim', 'Pam', 'Micheal', 'Dwight']
ages = [27, 25, 4, 9]

import numpy
people = numpy.array(people)
ages = numpy.array(ages)
inds = ages.argsort()
sortedPeople = people[inds]

Je l'ai trouvé ici: http://scienceoss.com/sort-one-list-by-another-list/

56
Tom

La solution la plus évidente pour moi consiste à utiliser le mot clé key arg.

>>> X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
>>> Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
>>> keydict = dict(Zip(X, Y))
>>> X.sort(key=keydict.get)
>>> X
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Notez que vous pouvez réduire cela à une ligne si vous vous souciez de:

>>> X.sort(key=dict(Zip(X, Y)).get)
30
senderle

J'aime avoir une liste d'index triés. De cette façon, je peux trier n'importe quelle liste dans le même ordre que la liste source. Une fois que vous avez une liste d'index triés, une compréhension simple de la liste fera l'affaire:

    X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

    sorted_y_idx_list = sorted(range(len(Y)),key=lambda x:Y[x])
    Xs = [X[i] for i in sorted_y_idx_list ]

    print( "Xs:", Xs )
    # prints: Xs: ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Notez que la liste d'index triée peut également être obtenue en utilisant numpy argsort ().

12
1-ijk

Une autre alternative, combinant plusieurs des réponses.

Zip(*sorted(Zip(Y,X)))[1]

Afin de travailler pour python3:

list(Zip(*sorted(Zip(B,A))))[1]
9
TMC

Zip, triez par la deuxième colonne, retournez la première colonne.

Zip(*sorted(Zip(X,Y), key=operator.itemgetter(1)))[0]
4
riza

more_itertools a un outil pour trier les iterables en parallèle:

from more_itertools import sort_together

sort_together([Y, X])[1]
# ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
4
pylang

En fait, je suis venu ici pour chercher une liste dans laquelle les valeurs correspondent.

list_a = ['foo', 'bar', 'baz']
list_b = ['baz', 'bar', 'foo']
sorted(list_b, key=lambda x: list_a.index(x))
# ['foo', 'bar', 'baz']
3
nackjicholson

Un one-liner rapide.

list_a = [5,4,3,2,1]
list_b = [1,1.5,1.75,2,3,3.5,3.75,4,5]

Disons que vous voulez lister la liste correspondante b.

orderedList =  sorted(list_a, key=lambda x: list_b.index(x))

Ceci est utile lorsque vous devez commander une liste plus petite à des valeurs plus grandes. En supposant que la liste la plus longue contienne toutes les valeurs de la liste la plus petite, cela peut être fait.

2
Evan Lalo

J'ai créé une fonction plus générale, qui trie plus de deux listes en fonction d'une autre, inspirée par la réponse de @ Whatang.

def parallel_sort(*lists):
    """
    Sorts the given lists, based on the first one.
    :param lists: lists to be sorted

    :return: a Tuple containing the sorted lists
    """

    # Create the initially empty lists to later store the sorted items
    sorted_lists = Tuple([] for _ in range(len(lists)))

    # Unpack the lists, sort them, Zip them and iterate over them
    for t in sorted(Zip(*lists)):
        # list items are now sorted based on the first list
        for i, item in enumerate(t):    # for each item...
            sorted_lists[i].append(item)  # ...store it in the appropriate list

    return sorted_lists
1
pgmank

Vous pouvez créer un pandas Series, en utilisant la liste primaire en tant que data et l'autre liste en tant que index, puis en triant simplement par l'index:

import pandas as pd
pd.Series(data=X,index=Y).sort_index().tolist()

sortie:

['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
0
Binyamin Even

Whatangs répond si vous souhaitez obtenir les deux listes triées (python3).

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Zx, Zy = Zip(*[(x, y) for x, y in sorted(Zip(Y, X))])

print(list(Zx))  # [0, 0, 0, 1, 1, 1, 1, 2, 2]
print(list(Zy))  # ['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Rappelez-vous juste que Zx et Zy sont des n-uplets… Je me promène également s’il existe un meilleur moyen de le faire.

Attention: Si vous le lancez avec des listes vides, il se plante.

0
Anoroah
list1 = ['a','b','c','d','e','f','g','h','i']
list2 = [0,1,1,0,1,2,2,0,1]

output=[]
cur_loclist = []

Pour obtenir des valeurs uniques présentes dans list2

list_set = set(list2)

Pour trouver la localisation de l'index dans list2 

list_str = ''.join(str(s) for s in list2)

La position de l'index dans list2 est suivie à l'aide de cur_loclist

[0, 3, 7, 1, 2, 4, 8, 5, 6]

for i in list_set:
cur_loc = list_str.find(str(i))

while cur_loc >= 0:
    cur_loclist.append(cur_loc)
    cur_loc = list_str.find(str(i),cur_loc+1)

print(cur_loclist)

for i in range(0,len(cur_loclist)):
output.append(list1[cur_loclist[i]])
print(output)
0
VANI