[英]How to do a circular (sub)sort of sets in Python?
考慮以下最小化的示例:
代碼:
a = [(1,'A'), (2,'A'), (3,'A'), (4,'A'), (5,'A')]
b = [(1,'B'), (2,'B'), (3,'B')]
c = []
d = [(1,'D'), (2,'D'), (3,'D'), (4,'D')]
print(sorted(a+b+c+d))
結果:
[(1, 'A'), (1, 'B'), (1, 'D'), (2, 'A'), (2, 'B'), (2, 'D'), (3, 'A'), (3, 'B'), (3, 'D'), (4, 'A'), (4, 'D'), (5, 'A')]
Python 按每個集合的第一項然后按第二項對集合列表進行排序。 沒關系。 現在,我需要第二個排序順序是字符串中的“循環”(不確定這是否是正確的術語)。 此外,我想指定新有序列表中的最后一個字符串。 例如,如果我指定'B'
,則有序列表應從'C'
開始。 如果'C'
不存在,它應該從'D'
開始,等等。但是,也可能發生指定的字符可能不在列表中,例如,如果'C'
不存在,新的排序列表應該仍然從'D'
開始。
編輯:
抱歉,我沒有在集合列表中添加所需的 output 順序以使其清楚。 假設我會指定mySpecialSort(myList,'B')
。 然后應該首先包含包含1
作為最高優先級排序順序的所有集合,然后是“循環”字符串(這里從'D'
開始,因為列表中沒有C
)。
所需的排序順序:
[(1, 'D'), (1, 'A'), (1, 'B'), (2, 'D'), (2, 'A'), (2, 'B'), (3, 'D'), (3, 'A'), (3, 'B'), (4, 'D'), (4, 'A'), (5, 'A')]
或縮短可讀形式: 1D, 1A, 1B, 2D, 2A, 2B, 3D, 3A, 3B, 4D, 4A, 5A
我為單個字符列表(這里有重復項)上的“循環”排序提出了一個(但僅限於到目前為止)的(繁瑣的)解決方案,如下所示:
代碼:
myList = ['A', 'D', 'E', 'G', 'Z', 'A', 'J', 'K', 'T']
def myCircularSort(myList,myLast):
myListTmp = sorted(list(set(myList + [myLast]))) # add myLast, remove duplicates and sort
idx = myListTmp.index(myLast) # get index of myLast
myStart = myListTmp[(idx+1)%len(myListTmp)] # get the start list item
myListSorted = sorted(list(set(myList))) # sorted original list
print("Normal sort: {}".format(myListSorted))
idx_start = myListSorted.index(myStart) # find start item and get its index
myNewSort = myListSorted[idx_start:] + myListSorted[0:idx_start] # split list and put in new order
print("Circular sort with {} as last: {}\n".format(myLast,myNewSort))
myCircularSort(myList,'D')
myCircularSort(myList,'X')
結果:
Normal sort: ['A', 'D', 'E', 'G', 'J', 'K', 'T', 'Z']
Circular sort with D as last: ['E', 'G', 'J', 'K', 'T', 'Z', 'A', 'D']
Normal sort: ['A', 'D', 'E', 'G', 'J', 'K', 'T', 'Z']
Circular sort with X as last: ['Z', 'A', 'D', 'E', 'G', 'J', 'K', 'T'] # X actually not in the list
但是,現在我被困在如何獲得這種“循環”排序(在集合列表的第二項上)和“正常”排序(在集合列表的第一項上)。
或者,我可能會想到一種“蠻力”方法來查找最高索引(此處: 4
)和所有現有字符串(此處: A
- Z
)並檢查兩個嵌套 for 循環中每個組合的存在。 我是在正確的軌道上還是我會做一些非常復雜和低效的事情,或者我錯過了一些智能 Python 功能?
編輯2:
經過進一步的搜索,我猜lambda
和cmp(x,y)
會完成這項工作(參見示例),但它似乎不再存在於 Python3 中。 所以,那么可能是operator.itemgetter()
或operator.methodcaller()
的東西,我仍然不知道如何使用,因為我錯過了很好的例子......
您可以使用 dict 到 map 一個字母到其正確的 position:
from string import ascii_uppercase as ABC
start = ABC.index('D') + 1
sorter = {
ABC[(n + start) % len(ABC)]: n
for n in range(len(ABC))
}
myList = ['A', 'D', 'E', 'G', 'Z', 'A', 'J', 'K', 'T']
print(sorted(myList, key=sorter.get))
# ['E', 'G', 'J', 'K', 'T', 'Z', 'A', 'A', 'D']
要使用任意關鍵字,請將它們提取到keys
列表中,根據需要重新排列並使用keys.index(word)
作為排序鍵:
myList = [
(1, 'ARTHUR'),
(2, 'CHARLIE'),
(3, 'GEORGE'),
(4, 'HARRY'),
(5, 'JACK'),
(6, 'LEO'),
(7, 'MUHAMMAD'),
(8, 'NOAH'),
(9, 'OLIVER'),
]
def circ_sorted(lst, start):
keys = sorted(e[1] for e in lst)
less = sum(1 for k in keys if k <= start)
keys = keys[less:] + keys[:less]
return sorted(lst, key=lambda e: (keys.index(e[1]), e[0]))
print(circ_sorted(myList, 'LEO')) ## [MUHAMMAD, NOAH...]
print(circ_sorted(myList, 'IAN')) ## [JACK, LEO...]
唷,這非常耗時,但我想我現在有一個解決方案。 至少結果似乎具有所需的順序。 模塊functools
提供cmp_to_key
來替換cmp()
,后者顯然已在 Python3 中刪除。 至少這是我在這里找到的。
如果有“本機”Python3 解決方案,我將很樂意了解它。 歡迎評論、改進、簡化。
因此,以下代碼首先按數字(此處為 1 到 5)對列表的集合進行排序,然后以循環方式按字符串(此處為:Ag、Au、Ca、Fe、Ti)對列表的集合進行排序,以便確定最后一個字符串通過myRef
。
代碼:
### special numerical and circular alphanumerical sort on a list of sets
from functools import cmp_to_key
# different lists of sets
ag = [(1,'Ag'), (2,'Ag'), (3,'Ag'), (4,'Ag'), (5,'Ag')]
au = [(1,'Au'), (2,'Au')]
ba = []
ca = [(1,'Ca'), (2,'Ca'), (3,'Ca')]
fe = [(1,'Fe'), (2,'Fe')]
ti = [(1,'Ti'), (2,'Ti'), (3,'Ti')]
myList = fe + ti + ag + au + ca + ba # merge all lists
def mySpecialCircularSort(myList,myRef):
myList = list(set(myList)) # remove duplicates
myListNew = sorted(myList, key=cmp_to_key(lambda a, b:
-1 if a[0]<b[0] else 1 if a[0]>b[0] else
-1 if b[1]==myRef else
1 if a[1]==myRef else
-1 if a[1]>myRef and b[1]<myRef else
1 if a[1]<myRef and b[1]>myRef else
-1 if a[1]<b[1] else
1 if a[1]>b[1] else 0))
print("Circular sort with {} as last: {}".format(myRef,myListNew))
print("Unsorted as is: {}\n".format(myList))
mySpecialCircularSort(myList,'Ag')
mySpecialCircularSort(myList,'Au')
mySpecialCircularSort(myList,'Ba') # since Ba-List was empty, the result will be same as 'Au'
mySpecialCircularSort(myList,'Ca')
mySpecialCircularSort(myList,'Fe')
mySpecialCircularSort(myList,'Ti')
結果:
Unsorted as is: [(1, 'Fe'), (2, 'Fe'), (1, 'Ti'), (2, 'Ti'), (3, 'Ti'), (1, 'Ag'), (2, 'Ag'), (3, 'Ag'), (4, 'Ag'), (5, 'Ag'), (1, 'Au'), (2, 'Au'), (1, 'Ca'), (2, 'Ca'), (3, 'Ca')]
Circular sort with Ag as last: [(1, 'Au'), (1, 'Ca'), (1, 'Fe'), (1, 'Ti'), (1, 'Ag'), (2, 'Au'), (2, 'Ca'), (2, 'Fe'), (2, 'Ti'), (2, 'Ag'), (3, 'Ca'), (3, 'Ti'), (3, 'Ag'), (4, 'Ag'), (5, 'Ag')]
Circular sort with Au as last: [(1, 'Ca'), (1, 'Fe'), (1, 'Ti'), (1, 'Ag'), (1, 'Au'), (2, 'Ca'), (2, 'Fe'), (2, 'Ti'), (2, 'Ag'), (2, 'Au'), (3, 'Ca'), (3, 'Ti'), (3, 'Ag'), (4, 'Ag'), (5, 'Ag')]
Circular sort with Ba as last: [(1, 'Ca'), (1, 'Fe'), (1, 'Ti'), (1, 'Ag'), (1, 'Au'), (2, 'Ca'), (2, 'Fe'), (2, 'Ti'), (2, 'Ag'), (2, 'Au'), (3, 'Ca'), (3, 'Ti'), (3, 'Ag'), (4, 'Ag'), (5, 'Ag')]
Circular sort with Ca as last: [(1, 'Fe'), (1, 'Ti'), (1, 'Ag'), (1, 'Au'), (1, 'Ca'), (2, 'Fe'), (2, 'Ti'), (2, 'Ag'), (2, 'Au'), (2, 'Ca'), (3, 'Ti'), (3, 'Ag'), (3, 'Ca'), (4, 'Ag'), (5, 'Ag')]
Circular sort with Fe as last: [(1, 'Ti'), (1, 'Ag'), (1, 'Au'), (1, 'Ca'), (1, 'Fe'), (2, 'Ti'), (2, 'Ag'), (2, 'Au'), (2, 'Ca'), (2, 'Fe'), (3, 'Ti'), (3, 'Ag'), (3, 'Ca'), (4, 'Ag'), (5, 'Ag')]
Circular sort with Ti as last: [(1, 'Ag'), (1, 'Au'), (1, 'Ca'), (1, 'Fe'), (1, 'Ti'), (2, 'Ag'), (2, 'Au'), (2, 'Ca'), (2, 'Fe'), (2, 'Ti'), (3, 'Ag'), (3, 'Ca'), (3, 'Ti'), (4, 'Ag'), (5, 'Ag')]
使用自定義排序鍵 function:
from string import ascii_uppercase
order = {c: i for i, c in enumerate(ascii_uppercase)}
def circular_sort(lst, last):
return sorted(lst, key=lambda x: (x[0], order[x[1]] + 26*(x[1]<=last)))
>>> circular_sort(a+b+c+d, 'B')
[(1, 'D'), (2, 'D'), (3, 'D'), (4, 'D'), (1, 'A'), (2, 'A'), (3, 'A'), (4, 'A'), (5, 'A'), (1, 'B'), (2, 'B'), (3, 'B')]
這只是將 26 添加到小於或等於指定的最后一個字母的任何字母的索引。
我在示例數據中看到了一個模式:
a = [(1,'A'), (2,'A'), (3,'A'), (4,'A'), (5,'A')]
b = [(1,'B'), (2,'B'), (3,'B')]
c = []
d = [(1,'D'), (2,'D'), (3,'D'), (4,'D')]
也許模式誤導了我,而真實數據沒有相同的模式。
在這種情況下,請忽略我的回答。
否則,鑒於 OP 對我的評論的回答:
起點是幾個單獨的列表
我提出這個解決方案:
n
次;這是一個實現示例,定義了一些助手:
from itertools import zip_longest
def rotate(l, n):
return l[n:] + l[:n]
def transpose(l):
return [list(filter(None,i)) for i in zip_longest(*tmp)]
def flatten(l):
return [item for sublist in l for item in sublist]
然后,例如旋轉 3 次以從D
開始:
tmp = [a, b, c, d]
tmp = rotate(tmp, 3)
tmp = transpose(tmp)
tmp = flatten(tmp)
tmp
#=> [(1, 'D'), (1, 'A'), (1, 'B'), (2, 'D'), (2, 'A'), (2, 'B'), (3, 'D'), (3, 'A'), (3, 'B'), (4, 'D'), (4, 'A'), (5, 'A')]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.