Можно ли отсортировать два списка (которые ссылаются друг на друга) одинаковым образом?
Хорошо, это может быть не самая умная идея, но мне было немного любопытно, если это возможно. Скажем, у меня есть два списка:
list1 = [3,2,4,1, 1]
list2 = [three, two, four, one, one2]
Если я бегу list1.sort()
Сортируй [1,1,2,3,4]
но есть ли способ сохранить синхронизацию list2 (так что я могу сказать, что пункт 4 принадлежит "трем")? Моя проблема в том, что у меня довольно сложная программа, которая отлично работает со списками, но мне нужно начать ссылаться на некоторые данные. Я знаю, что это идеальная ситуация для словарей, но я стараюсь избегать словарей в моей обработке, потому что мне нужно отсортировать значения ключей (если я должен использовать словари, я знаю, как их использовать).
В основном природа этой программы такова, что данные поступают в случайном порядке (как выше), мне нужно отсортировать их, обработать и затем отправить результаты (порядок не имеет значения, но пользователи должны знать, к какому результату относится какой ключ). Я думал о том, чтобы сначала поместить его в словарь, а затем отсортировать по списку, но у меня не было бы возможности дифференцировать элементы с одинаковым значением, если порядок не поддерживается (это может повлиять на передачу результатов пользователям). Поэтому в идеале, когда я получу списки, я бы предпочел найти способ сортировки обоих списков. Это возможно?
16 ответов
Один из классических подходов к этой проблеме - использовать идиому "декорировать, сортировать, декорировать", что особенно просто при использовании встроенного в 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
С другой стороны, для больших списков однострочная версия может быть быстрее:
>>> %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
основанный на подходе более гибкий и немного более читаемый, поэтому я предпочитаю его.
Вы можете сортировать индексы, используя значения в качестве ключей:
indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)
Чтобы получить отсортированные списки по заданным отсортированным индексам:
sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)
В вашем случае вы не должны иметь list1
, list2
а точнее один список пар:
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])
Я использовал ответ, данный Senderle в течение долгого времени, пока я не обнаружил 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
не самый быстрый, мне проще в использовании.
Преобразование Шварца. Встроенная сортировка Python стабильна, поэтому два 1
Это не вызывает проблем.
>>> 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')]
Одним из способов является отслеживание того, куда идет каждый индекс, путем сортировки идентификаторов [0,1,2,..n]
Это работает для любого количества списков.
Затем переместите каждый элемент на свою позицию. Использование сростков лучше всего.
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
index = 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)
Вы можете использовать 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']
Надеюсь это поможет
Как насчет:
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')]
Если вы используете 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])
Вы можете использовать ключевой аргумент в методе sorted(), если у вас нет двух одинаковых значений в list2.
Код приведен ниже:
sorted(list2, key = lambda x: list1[list2.index(x)])
Он сортирует list2 в соответствии с соответствующими значениями в list1, но убедитесь, что при его использовании никакие два значения в list2 не считаются равными, потому что функция list.index() дает первое значение
Другой подход к сохранению порядка списка строк при сортировке по другому списку заключается в следующем:
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]
Я хотел бы предложить решение, если вам нужно синхронизировать более двух списков:
def SortAndSyncList_Multi(ListToSort, *ListsToSync):
y = sorted(zip(ListToSort, zip(*ListsToSync)))
w = [n for n in zip(*y)]
return list(w[0]), tuple(list(a) for a in zip(*w[1]))
Основываясь на ответе @pylang на дублирующий вопрос, который я только что закрыл , необходимый алгоритм реализован в популярной сторонней библиотеке.more_itertools
, как sort_together .
Таким образом:
from more_itertools import sort_together
list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
list1, list2 = sort_together(list1, list2)
Я хотел бы расширить открытый ответ jfs, который отлично справился с моей проблемой: сортировка двух списков третьим, украшенным списком:
Мы можем создать наш украшенный список любым способом, но в этом случае мы создадим его из элементов одного из двух исходных списков, которые мы хотим отсортировать:
# 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 для сортировки наших двух списков по третьему
# 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'}]
Изменить: Привет, ребята, я сделал блок-пост об этом, проверьте это, если вам это нравится:)
алгоритмическое решение:
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']
Сортировка двух списков параллельно в Python(сортировка по десятичному значению)
a = ['a','b','c','d','e','f']
b = ['0.23','80.00','5.01','6.58','1.38','79.06']
c=sorted(b,key=lambda x:float(x))
d=[]
for i in range(len(a)):
d.append(a[b.index(c[i])])
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