別のリストの値に基づいてリストを並べ替えていますか?


370

次のような文字列のリストがあります。

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

Yの値を使用してXをソートし、次の出力を取得する最も簡単な方法は何ですか?

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

同じ「キー」を持つ要素の順序は関係ありません。forコンストラクトの使用に頼ることはできますが、もっと短い方法があるかどうか知りたいです。助言がありますか?


ソートされたXとYの両方が、Xの値でソートされた戻り値:データをプロットするときにジッパー(対[0])*は(ZIP(X、Y)、キー=ラムダ対ソート)のでリザの答えは、有用であるかもしれない
ジョジョを

回答:


479

最短コード

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

例:

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"]

一般的に言えば

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

説明:

  1. zip2つlistの。
  2. 新しいを作成し、ソートlistに基づいてzip使用しますsorted()
  3. リスト内包表記を使用して、ソートされたzip形式から各ペアの最初の要素を抽出listます。

使用\設定方法の詳細についてはkey、パラメータだけでなく、sorted一般的に機能を、見とるこれを



117
これは正しいですが、複数の配列を同じ配列で並べ替えようとすると、並べ替えに使用されているキーが(y、x)であるため、期待どおりに機能しないことに注意してください。 、yだけではありません。代わりに[x for(y、x)in sorted(zip(Y、X)、key = lambda pair:pair [0])]を使用する必要があります
gms7777

1
良い解決策!しかし、それはそうである必要があります:リストはペアの最初の要素に関して順序付けられ、内包表記はペアの「2番目の」要素を抽出します。
MasterControlProgram

このソリューションは、ストレージに関しては不十分です。可能な限りインプレースソートが推奨されます。
Hatefiend

107

2つのリストを圧縮して並べ替え、必要な部分を取り出します。

>>> 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']

これらを組み合わせて以下を取得します。

[x for y, x in sorted(zip(Y, X))]

1
これはXのリストの場合は問題ありませんが、たとえばのアイテムのペアに対して定義されていないstr可能性がある場合は注意<してくださいX。たとえば、それらの一部が定義されている場合None
John La Rooy

1
zipオブジェクトでsortを使おうとするAttributeError: 'zip' object has no attribute 'sort'と、今のところです。
Ash Upadhyay

2
Python 3を使用しています。Python2では、zipによってリストが作成されました。これで、反復可能なオブジェクトが生成されます。 sorted(zip(...))まだ動作、または必要があります: them = list(zip(...)); them.sort()
ネッドBatchelder

77

また、numpy配列を使用することを気にしない場合(または実際にはすでにnumpy配列を処理している場合...)、別の良い解決策を次に示します。

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]

私はそれをここで見つけました:http : //scienceoss.com/sort-one-list-by-another-list/


1
より大きな配列/ベクトルの場合、numpyを使用したこのソリューションは有益です!
MasterControlProgram

1
それらがすでに乱暴な配列である場合、それは単にsortedArray1= array1[array2.argsort()]です。また、これにより、2D配列の特定の列で複数のリストを簡単に並べ替えるsortedArray1= array1[array2[:,2].argsort()]こともできます。
アーロンブラムソン

40

私にとって最も明白な解決策は、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']

次の点に注意する場合は、これをワンライナーに短縮できることに注意してください。

>>> X.sort(key=dict(zip(X, Y)).get)

2
これには、Xの値が一意である必要がありますか?
ジャックペン

15

実際にここに来て、値が一致するリストでリストを並べ替えました。

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

1
これはパフォーマンスですか?
AFP_555

全く分からない。あなたが見つけたものを報告してください。
nackjicholson

1
これは悪い考えです。結果のソートでO(N)検索indexを実行します。list_aO(N² log N)
Richard

ありがとう、パフォーマンスが重要なときはこれを行わないでください!
nackjicholson

15

more_itertools イテラブルを並列にソートするツールがあります:

与えられた

from more_itertools import sort_together


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

デモ

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

13

ソートされたインデックスのリストが好きです。そうすれば、ソースリストと同じ順序で任意のリストを並べ替えることができます。並べ替えられたインデックスのリストを取得したら、単純なリスト内包でうまくいきます。

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"]

ソートされたインデックスリストはを使用して取得することもできますnumpy.argsort()


12

別の選択肢として、いくつかの回答を組み合わせます。

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

python3のために働くために:

list(zip(*sorted(zip(B,A))))[1]

7

zip、2番目の列で並べ替え、最初の列を返します。

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

注:key = operator.itemgetter(1)は重複問題を解決します
Keith

zipは下付きではありません...実際に使用する必要がありますlist(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]
raphael

@キース何重複問題?
Josh

一致するものが複数ある場合は、最初に取得します
キース

3

迅速なワンライナー。

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

リストaとリストbを一致させたいとします。

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

これは、小さいリストを大きい値に並べ替える必要がある場合に役立ちます。大きい方のリストに小さい方のリストのすべての値が含まれていると仮定すると、それを行うことができます。


これはOPの問題を解決しません。あなたは、サンプルのリストでそれを試してみましたXY
Aryeh Leib Taurog 2018年

これは悪い考えです。結果のソートでO(N)検索indexを実行します。list_bO(N² log N)
Richard

1

pandas Seriesプライマリリストas dataと他のリストas を使用してを作成indexし、インデックスで並べ替えることができます。

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

出力:

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

1

ソートされた両方のリストを取得する場合のPythonの回答は次のとおりです(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']

ZxとZyはタプルであることを覚えておいてください。それを行うより良い方法があるかどうか私も迷っています。

警告:空のリストで実行すると、クラッシュします。


1

@Whatangの回答に触発されて、別のリストに基づいて3つ以上のリストをソートする、より一般的な関数を作成しました。

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

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

output=[]
cur_loclist = []

に存在する一意の値を取得するには list2

list_set = set(list2)

インデックスの場所を見つけるには list2

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

インデックスの場所 list2使用して追跡されます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

これは古い質問ですが、私が投稿した回答の一部はzipスクリプト化できないため実際には機能しません。他の答えは気になりませんでしたimport operator、このモジュールとその利点についての情報を提供。

この問題には、少なくとも2つの優れたイディオムがあります。入力した入力例から始めます。

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

Decolate-Sort-Undecorate」イディオムを使用する

これは、90年代にPerlでこのパターンを普及させたR. シュワルツの後のSchwartzian_transformとしても知られています。

# Zip (decorate), sort and unzip (undecorate).
# Converting to list to script the output and extract X
list(zip(*(sorted(zip(Y,X)))))[1]                                                                                                                       
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')

この場合YXがソートされ、辞書編集的に比較されることに注意してください。つまり、(からのY)最初の項目が比較されます。そして、それらが同じである場合、2番目の項目(からのX)が比較されます。重複を元の順序に保つために辞書式順序付けの元のリストインデックスを含めない限り、これは不安定な出力を作成する可能性があります。

モジュールの使用operator

これにより、入力を並べ替える方法をより直接制御できるため、並べ替える特定のキーを指定するだけで、並べ替えの安定性を得ることができます。その他の例については、こちらをご覧ください。

import operator    

# Sort by Y (1) and extract X [0]
list(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]                                                                                                 
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.