Python 对项目进行排序,但防止相同的命名项目相邻;同时在集合中添加随机化

Python 对项目进行排序,但防止相同的命名项目相邻;同时在集合中添加随机化,python,list,algorithm,orders,Python,List,Algorithm,Orders,我正试图找出创建“最近”列表需要使用哪些排序算法,但要防止同名项彼此相邻,而且还要能够在集合中随机排列 例如,如果我们有以下原始数据: Apple 10/02/2020 Apple 15/02/2020 Apple 10/03/2020 Apple 15/03/2020 Apple 10/04/2020 Apple 15/04/2020 Banana 16/03/2020 Banana 21/03/2020 Banana 16/04/2020 Orange 13/

我正试图找出创建“最近”列表需要使用哪些排序算法,但要防止同名项彼此相邻,而且还要能够在集合中随机排列

例如,如果我们有以下原始数据:

Apple   10/02/2020
Apple   15/02/2020
Apple   10/03/2020
Apple   15/03/2020
Apple   10/04/2020
Apple   15/04/2020
Banana  16/03/2020
Banana  21/03/2020
Banana  16/04/2020
Orange  13/03/2020
Orange  15/03/2020
我想对其进行排序,使其大致类似于下面显示的数据(每个项目名称中最新的项目)。当然,我们的橙子和香蕉都用完了,所以最后4个项目都必须是苹果,但这是没办法的

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
Banana  21/03/2020
Apple   10/04/2020
Orange  13/03/2020
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
Apple   15/02/2020
Apple   10/02/2020
问题:重复的“群体” 这个排序顺序的唯一问题是我们有一组重复的
香蕉
苹果
橙色
。因此,我们希望有选择地将一组按某种随机顺序进行排序。“组”的大小将由我们选择的数字定义;不是按项目的数量

因此,如果我们将“组”设置为3,它将查看上述顺序,并将每组3随机化,因此看起来像:

Orange  15/03/2020
Apple   15/04/2020
Banana  16/04/2020
---
Banana  21/03/2020
Orange  13/03/2020
Apple   10/04/2020
---
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
---
Apple   10/02/2020
Apple   15/02/2020
问题:丢失了“最近的”。 上面的排序组的问题是最近的项目被随机放在第一组的底部;当然,情况并非总是如此,但在本例中,情况一直如此

也许有一种方法可以保持第一组的顺序,或者随机化下面的组,或者只是重新排序,这样组的顺序就不同于前一组的顺序;例如:

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
---
Apple   10/04/2020
Orange  13/03/2020
Banana  21/03/2020
---
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
---
Apple   15/02/2020
Apple   10/02/2020

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
---
Banana  21/03/2020
Orange  13/03/2020
Apple   10/04/2020
---
Apple   15/03/2020
Banana  16/03/2020
Apple   10/03/2020
---
Apple   10/02/2020
Apple   15/02/2020

我不完全确定我是否理解整个问题,但我想我得到了“最新的项目,但没有相同的项目出现在彼此旁边”,所以我将重点关注这一部分

如果你能把相同的项目分开,同时保持它们之间的顺序,那就很容易了;首先按日期排序,然后进行分离。我想不出一个可以实现这种分离的现有函数,所以我将一个“足够好”的函数拼凑在一起,以产生合理的输出:

from datetime import datetime
from typing import Any, Callable, List, Optional, TypeVar

_I = TypeVar('_I')


def declump(
    items: List[_I], 
    key: Optional[Callable[[_I], Any]] = None
) -> None:
    """
    Separate identical items by doing repeated swaps of the form:
    AAB -> ABA  (in-place)
    Existing ordering is preserved within sets of identical items,
    but not between non-identical items.
    fixme: we can end up with a clump at the very end of the list!
    Easy workaround is to reverse the list and declump again.
    """
    if key is None:
        key = lambda x: x
    i = 0
    key(items[i])
    while i < len(items) - 2:
        if (
            key(items[i]) == key(items[i+1])
            and key(items[i+1]) != key(items[i+2])
        ):
            items[i+1], items[i+2] = items[i+2], items[i+1]
            if i > 0:
                i -= 1
                continue
        i += 1


produce = [
    ('Apple', '10/02/2020'),
    ('Apple', '15/02/2020'),
    ('Apple', '10/03/2020'),
    ('Apple', '15/03/2020'),
    ('Apple', '10/04/2020'),
    ('Apple', '15/04/2020'),
    ('Banana', '16/03/2020'),
    ('Banana', '21/03/2020'),
    ('Banana', '16/04/2020'),
    ('Orange', '13/03/2020'),
    ('Orange', '15/03/2020'),
]


produce.sort(key=lambda t: datetime.strptime(t[1], r'%d/%m/%Y'), reverse=True)
produce.reverse()
declump(produce, key=lambda t: t[0])
produce.reverse()
declump(produce, key=lambda t: t[0])
print('\n'.join(t[0].ljust(8) + t[1] for t in produce))

我几乎可以肯定有更好的方法来编写
dectlup
函数,但是翻转它并再次调用它似乎弥补了它的不足,因此我将把改进留给读者作为练习。

我决定将其视为按日期对单个水果进行排序,然后将它们交错排列。 如果我将每个fruitdate设置为在时间上依赖于下一个fruitdate,但在相同的结果之间只有时间依赖关系B;然后可用于将项目划分为不同水果的子组,如上所述。然后我可以按时间顺序排列第一组,然后随机排列其他子组

守则:

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 29 09:21:15 2020

Answer to:
    https://stackoverflow.com/questions/61485884/sorting-items-but-preventing-same-named-items-being-next-to-eachother-also-addi

@author: Paddy3118
"""

from random import shuffle
from functools import reduce


def toposort2(data):
    "Based on: http://rosettacode.org/wiki/Topological_sort#Python"

    for k, v in data.items():
        v.discard(k) # Ignore self dependencies
    extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
    data.update({item:set() for item in extra_items_in_deps})
    while True:
        ordered = set(item for item,dep in data.items() if not dep)
        if not ordered:
            break
        #yield ' '.join(sorted(ordered))  ## The one change!!
        yield sorted(ordered)
        data = {item: (dep - ordered) for item,dep in data.items()
                if item not in ordered}
    assert not data, "A cyclic dependency exists amongst %r" % data

#%%

raw = """
Apple   10/02/2020
Apple   15/02/2020
Apple   10/03/2020
Apple   15/03/2020
Apple   10/04/2020
Apple   15/04/2020
Banana  16/03/2020
Banana  21/03/2020
Banana  16/04/2020
Orange  13/03/2020
Orange  15/03/2020
"""


def fruit_yearmonthday(fruitdate):
    "Order fields, (esp. dates), for item sorting"
    fruit, date = fruitdate
    d, m, y = date.split('/')
    return fruit, y, m, d


def ymd(fruitdate_string):
    "Order order field dates for partial order sorting"
    fruit, date = fruitdate_string.split()
    d, m, y = date.split('/')
    return y, m, d

#%%
# raw data into individual (frit, date) ruples
items = [tuple(line.split()) for line in raw.strip().split('\n')]
        # [('Apple', '10/02/2020'), ...]

# Order by date of each fruit kind
items.sort(key=fruit_yearmonthday)
    # [('Apple', '10/02/2020'),
    #  ('Apple', '15/02/2020'),
    #  ('Apple', '10/03/2020'),
    #  ('Apple', '15/03/2020'),
    #  ('Apple', '10/04/2020'),
    #  ('Apple', '15/04/2020'),
    #  ('Banana', '16/03/2020'),
    #  ('Banana', '21/03/2020'),
    #  ('Banana', '16/04/2020'),
    #  ('Orange', '13/03/2020'),
    #  ('Orange', '15/03/2020')]
# Stringify
items = [f"{i[0]:6}  {i[1]}" for i in items]

# Initial time-based dependencies only between _same_ fruits
last = items[0]
depends = {}
for fruitdate in items[1:]:
    if last[0] == fruitdate[0]:
        depends[last] = {fruitdate}
    last = fruitdate

partial_order = list(toposort2(depends))
#print("Partial ordering. items on same line could be in any order:\n")
#print ('\n'.join(str(line) for line in partial_order))
    # ['Apple   15/04/2020', 'Banana  16/04/2020', 'Orange  15/03/2020']
    # ['Apple   10/04/2020', 'Banana  21/03/2020', 'Orange  13/03/2020']
    # ['Apple   15/03/2020', 'Banana  16/03/2020']
    # ['Apple   10/03/2020']
    # ['Apple   15/02/2020']
    # ['Apple   10/02/2020']

# An ordering:
# First line by date, then other lines *randomly*
order = []
for linenum, line in enumerate(partial_order):
    if linenum == 0:
        order += sorted(line, key=ymd, reverse=True)
    else:
        shuffle(line)
        order += line

print("\nAN ORDERING OF ITEMS:")
for item in order:
    print(f'  {item}')
几次测试表明,前三项和后三项总是相同的,但第四项到第六项是随机的,第七项到第八项也是随机的

样本运行:

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Banana  16/03/2020
  Apple   15/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

你为什么不考虑像苹果香蕉苹果橙苹果香蕉这样的订单?你能澄清你的意思吗?如果你想要一个随机的顺序,当然他们不会再被最近的排序了。如果你所关心的只是将项目分成不同的组,那么就按组对其进行排序,然后按最新的组对第一组进行排序?也许你尝试过的一个例子会对你有所帮助。@billy因为我想获得尽可能多的独特项目的最新信息,否则在某些列表中,它会在显示其他项目之前在两个项目之间切换太长时间。@r.ook我在我的帖子中提到了组,通过对项目进行排序,然后将其分为多个组(在我的示例中,我选择了3个组的大小),并仅对组应用随机性,以使每个显示的组看起来更独特。您的原始数据类型是什么?纯文本<代码>数据帧?其他一些…?我们第一次看到橙色的问题是,在第8项中,如果我们将数据拆分/显示为3组,例如,我们的前3组都将显示两个苹果。不将相同的项目显示在一起的重要性随着“分组”的深入而变得越来越不重要,因此在列表的末尾有4个苹果基本上是可以的。
AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Banana  16/03/2020
  Apple   15/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020