2つのリスト(相互参照)をまったく同じ方法でソートする方法


139

2つのリストがあるとします。

list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

を実行するとlist1.sort()、並べ替え[1,1,2,3,4]られますがlist2、同期する方法はありますか(アイテムがに4属していると言えます'three')?したがって、予想される出力は次のようになります。

list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']

私の問題は、リストで正常に動作するかなり複雑なプログラムがあるのですが、データの参照を開始する必要があるということです。これは辞書に最適な状況ですが、キー値をソートする必要があるため、処理中に辞書を避けようとしています(辞書を使用する必要がある場合は、その使用方法を知っています)。

基本的にこのプログラムの性質は、データがランダムな順序で(上記のように)来て、並べ替え、処理して結果を送信する必要があります(順序は重要ではありませんが、ユーザーはどの結果がどの結果に属しているかを知る必要がありますキー)。最初に辞書に入れ、次にリストをソートすることを考えましたが、順序が維持されていないと、同じ値のアイテムを区別する方法がありません(結果をユーザーに通知するときに影響がある可能性があります)。理想的には、リストを取得したら、両方のリストを一緒に並べ替える方法を見つけたいと思います。これは可能ですか?


list2の変数がlist1のintを指していないことを指摘しておきます。たとえば、list1 [0] = 9などの値を変更してlist2を見ると、list2 [0]は3のままです。Pythonの整数では、参照/ポインタを使用せず、値をコピーします。あなたはlist2 = list1 [:]に行くほうがいいでしょう
robert king

回答:


242

この問題に対する古典的なアプローチの1つは、「装飾、並べ替え、装飾解除」のイディオムを使用することです。これは、Pythonの組み込みzip関数を使用して特に簡単です。

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2 
('one', 'one2', 'two', 'three', 'four')

もちろん、これらはもはやリストではありませんが、重要な場合は簡単に修正できます。

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

上記は簡潔さのために速度を犠牲にするかもしれないことに注意する価値があります。3行を占めるインプレースバージョンは、小さなリストの場合、私のマシンでは少し速くなります。

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

一方、リストが大きい場合は、1行バージョンの方が高速です。

>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop

Quantum7が指摘しているように、JSFの提案はまだ少し高速ですが、Pythonはすべてのキーベースのソートに内部でまったく同じDSUイディオムを使用しているため、おそらく少しだけ高速になります。ベアメタルに少し近づいているだけです。(これは、zipルーチンがどれだけ適切に最適化されているかを示しています!)

私が思うにzipベースのアプローチは、より柔軟性があり、私はそれを好むので、もう少し読みやすいです。


6
3行目のアスタリスクは何を表していますか?
ジェフリー、

8
上記について詳しく説明するために、*演算子は引数unpackingを行います
sendle

1
JF Sebastianによって提案されたソートされたインデックス/マップパラダイムは、どちらのzipソリューションよりも約10%高速です(10000のランダムな整数のリストを使用):%timeit index = range(len(l1)); index.sort(key = l1 .__ getitem__); map(l1 .__ getitem__、index); map(l2 .__ getitem__、index)100ループ、最高3:ループあたり8.04 ms(vs 9.17 ms、送信者のティミットでは9.07 ms)
Quantum7

1
list1、list2 = zip(* sorted(zip(list1、list2)))の最初と2番目のzipは、このような異なることを行います。*はすべての違いをもたらします。
ashu、2018年

1
@ashu、ある意味で、はい!しかし、別の意味では、それらはまったく同じです。zip(*x)それはそれ自身の逆であるという興味深い特性を持っています:を l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == l返しますTrue。実質的に転置演算子です。zip()それ自体は同じ演算子ですが、入力シーケンスを手動でアンパックしたと想定しています。
センダーレ2018年

30

値をキーとして使用してインデックスをソートできます。

indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)

ソートされたインデックスを指定してソートされたリストを取得するには:

sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)

あなたのケースでは、あなたが持つべきではないlist1list2ではなく、ペアの単一のリスト:

data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]

作成は簡単です。Pythonでの並べ替えは簡単です。

data.sort() # sort using a pair as a key

最初の値のみで並べ替え:

data.sort(key=lambda pair: pair[0])

これのクールな点は、list1が他のいくつかの配列に影響する重要な座標である場合に備えて、インデックスを保持し、後で他のものをソートできることです。
EL_DON 2018年

3
index

あなたもする必要が@DonQuiKong list() 周りのmap()あなたは、Python 3でこのコードを使用したい場合
JFSは、

または、代わりにsorted_list1 = list(map(list1.__getitem__, indexes))行うことができますsorted_list1 = [list1[i] for i in indexes]
ネイサン

20

私は発見するまで長い間、センダーレから与えられた答えを使いましたnp.argsort。これがどのように機能するかです。

# idx works on np.array and not lists.
list1 = np.array([3,2,4,1])
list2 = np.array(["three","two","four","one"])
idx   = np.argsort(list1)

list1 = np.array(list1)[idx]
list2 = np.array(list2)[idx]

私はこの解決策がより直感的であると思います、そしてそれは本当にうまく機能します。パフォーマンス:

def sorting(l1, l2):
    # l1 and l2 has to be numpy arrays
    idx = np.argsort(l1)
    return l1[idx], l2[idx]

# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop

# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop

# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop

たとえnp.argsort最速の一つではないですが、私は使用にそれが簡単にわかります。


1
あなたの例を実行するとエラーが発生します:TypeError: only integer arrays with one element can be converted to an index(Python 2.7.6、numpy 1.8.2)。これを修正するには、list1とlist2をnumpy配列として宣言する必要があります。
BenB、2015

ありがとう。これは関数のコメントに書いたものではありませんか?とにかく、内部np.argsortに変換しようとしないのはばかげていると思いますnp.array
Daniel Thaagaard Andreasen 2015

最初のコードスニペットは、記述
どおりに

numpy配列に割り当てられたときにリストを変換して修正しました。コメントをありがとう:)
Daniel Thaagaard Andreasen

現在、それらはNumpy配列に2回変換されています;)
BenB '

13

変換シュワルツ。組み込みのPythonソートは安定しているため、2つ1のsは問題を引き起こしません。

>>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]

2
ただし、これを行う必要がある場合は、2タプル(ペア)のリストを保持するのではなく、データの2つの「並列」リストがあることを強く再検討する必要があります。 。
Karl Knechtel、2012年

3

何について:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]

2

これを実現するには、zip()およびsort()関数を使用できます。

Python 2.6.5 (r265:79063, Jun 12 2010, 17:07:01)
[GCC 4.3.4 20090804 (release) 1] on cygwin
>>> list1 = [3,2,4,1,1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> zipped = zip(list1, list2)
>>> zipped.sort()
>>> slist1 = [i for (i, s) in zipped]
>>> slist1
[1, 1, 2, 3, 4]
>>> slist2 = [s for (i, s) in zipped]
>>> slist2
['one', 'one2', 'two', 'three', 'four']

お役に立てれば


2

list2に2つの同じ値がない限り、sorted()メソッドでキー引数を使用できます。

コードを以下に示します。

sorted(list2, key = lambda x: list1[list2.index(x)]) 

list1の対応する値に従ってlist2をソートしますが、これを使用している間は、list.index()関数が最初の値を与えるため、list2の2つの値が等しいと評価されないことを確認してください


ソートは機能しますが、条件によっては多少遅くなります。
tyan 2018年

2

1つの方法は、ID [0,1,2、.. n]をソートして、各インデックスがどこに行くかを追跡することです。

これは、任意の数のリストで機能します。

次に、各アイテムをその位置に移動します。スプライスを使用するのが最適です。

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

index = list(range(len(list1)))
print(index)
'[0, 1, 2, 3, 4]'

index.sort(key = list1.__getitem__)
print(index)
'[3, 4, 1, 0, 2]'

list1[:] = [list1[i] for i in index]
list2[:] = [list2[i] for i in index]

print(list1)
print(list2)
'[1, 1, 2, 3, 4]'
"['one', 'one2', 'two', 'three', 'four']"

リストを並べ替えることなくリストを反復することもできます:

list1_iter = (list1[i] for i in index)

1

numpyを使用np.argsortしている場合は、を使用してソートされたインデックスを取得し、それらのインデックスをリストに適用できます。これは、並べ替えたいリストの数に関係なく機能します。

import numpy as np

arr1 = np.array([4,3,1,32,21])
arr2 = arr1 * 10
sorted_idxs = np.argsort(arr1)

print(sorted_idxs)
>>> array([2, 1, 0, 4, 3])

print(arr1[sorted_idxs])
>>> array([ 1,  3,  4, 21, 32])

print(arr2[sorted_idxs])
>>> array([ 10,  30,  40, 210, 320])

0

アルゴリズムによる解決策:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']


lis = [(list1[i], list2[i]) for i in range(len(list1))]
list1.sort()
list2 = [x[1] for i in range(len(list1)) for x in lis if x[0] == i]

出力: -> 出力速度: 0.2s

>>>list1
>>>[1, 1, 2, 3, 4]
>>>list2
>>>['one', 'one2', 'two', 'three', 'four']

0

別のリストに対してソートするときに文字列リストの順序を保持する別のアプローチは次のとおりです。

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

# sort on list1 while retaining order of string list
sorted_list1 = [y for _,y in sorted(zip(list1,list2),key=lambda x: x[0])]
sorted_list2 = sorted(list1)

print(sorted_list1)
print(sorted_list2)

出力

['one', 'one2', 'two', 'three', 'four']
[1, 1, 2, 3, 4]

0

私の問題にうまく機能したopen jfsの回答を展開したいと思います。2つのリストを3番目の装飾されたリストでソートします

装飾されたリストはどのような方法でも作成できますが、この場合は、並べ替えたい2つの元のリストのいずれかの要素から作成します。

# say we have the following list and we want to sort both by the algorithms name 
# (if we were to sort by the string_list, it would sort by the numerical 
# value in the strings)
string_list = ["0.123 Algo. XYZ", "0.345 Algo. BCD", "0.987 Algo. ABC"]
dict_list = [{"dict_xyz": "XYZ"}, {"dict_bcd": "BCD"}, {"dict_abc": "ABC"}]

# thus we need to create the decorator list, which we can now use to sort
decorated = [text[6:] for text in string_list]  
# decorated list to sort
>>> decorated
['Algo. XYZ', 'Algo. BCD', 'Algo. ABC']

これで、jfsのソリューションを適用して、2つのリストを3番目のリストでソートできます。

# create and sort the list of indices
sorted_indices = list(range(len(string_list)))
sorted_indices.sort(key=decorated.__getitem__)

# map sorted indices to the two, original lists
sorted_stringList = list(map(string_list.__getitem__, sorted_indices))
sorted_dictList = list(map(dict_list.__getitem__, sorted_indices))

# output
>>> sorted_stringList
['0.987 Algo. ABC', '0.345 Algo. BCD', '0.123 Algo. XYZ']
>>> sorted_dictList
[{'dict_abc': 'ABC'}, {'dict_bcd': 'BCD'}, {'dict_xyz': 'XYZ'}]

編集:こんにちは私はこれについてブロック投稿をしました、あなたがそれが好きだと感じたらそれをチェックしてください:)🐍🐍🐍


-1
newsource=[];newtarget=[]
for valueT in targetFiles:
    for valueS in sourceFiles:
            l1=len(valueS);l2=len(valueT);
            j=0
            while (j< l1):
                    if (str(valueT) == valueS[j:l1]) :
                            newsource.append(valueS)
                            newtarget.append(valueT)
                    j+=1

2
数行の説明が役立つでしょう
saiedmomen 2018

@saiedmomen私はを参照して、それを掲示stackoverflow.com/questions/53829160/...ここでターゲット文字列がソース文字列の上に検索されます。
user10340258
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.