Python:根据两个属性对列表进行排序

4

我有一个如下所示的列表:

class Ind(object):
    def __init__(self,ID,mate):
        self.ID=ID
        self.mate=mate

population=[Ind(8,None), Ind(1,2), Ind(20,3), Ind(2,1), Ind(12,None), Ind(3,20), Ind(10,11), Ind(11,10)]

您可以将这个列表 population 看作是一个人口群体,其中所有人都有一个 ID。其中一些人有一个配偶(即存在于相同的人口群体或相同的列表中的个体)。mate 值实际上就是配偶的 ID!因此,如果有一个实例 Ind,其属性 ID 等于 12,mate 等于 34,则必然存在一个 ID 等于 34,且配偶为 12 的个体在该列表中。没有配偶的个体在 mate 属性中写入了 None。您明白了吗?
我想对这个列表进行排序,使第一个个体与最后一个个体配对,第二个个体与倒数第二个个体配对,以此类推。具有 mate 属性等于 None 的个体应位于列表的中间。
有很多可能符合我的要求的输出结果。以下是以上列表的一个示例:
population=[Ind(1,2), Ind(20,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,20), Ind(2,1)]

需要实际排序结果吗? - user3850
2
看了你的例子,似乎一个个体可以有两个不同的配偶:Ind(2,3)(2与3结为配偶)和Ind(2,1)(2与1结为配偶)。这是打字错误吗?还是在你的种群中交配真的是一对多的关系? - Miklos Aubert
我的例子中确实有一个错误,对此很抱歉。我已经纠正了它! - Remi.b
4个回答

5
您可以尝试这样做:
def custom_sort(population):
    pop_dict = { ind.ID: ind for ind in population }

    start = []
    nones = []
    end = []
    for ind in population:
        if ind.mate is None:
            nones.append(ind)
        elif pop_dict[ind.mate] not in start:
            start.insert(0, ind)
            end.append(pop_dict[ind.mate])
    return start + nones + end

这是在假设“成为伙伴”是一对一关系的前提下进行的。

2

你只需要一个用于排序函数的键。下面的示例要求个体是一夫一妻制且不与自己结婚。它还要求如果(a,b)被列出,则(b,a)也会被列出。如果这些先决条件未得到满足,Ind(2,1)可以在没有 Ind(1,2)的情况下发生,此函数将把Ind(2,1)放在列表末尾。键函数中的第一个索引是类型:"first"是在关系中(其中IDmate)排在第三位的。这些第一和第二类型按其id顺序排序;最后一种类型按其伴侣的相反顺序排序。

def keyfun(x):
   if x.mate==None: 
     return (1,x.ID)
   elif x.ID<x.mate: 
     return (0,x.ID)
   else:
     return (2,-x.mate)

sorted(population,key=keyfun)

另一种处理方式是,在假设列表中有 (a,b) 的情况下,(b,a) 也会在列表中出现,因此预处理时只需删除 (b,a) 的情况,然后后处理时以相反的顺序将它们添加回去即可。


你说得对...我不知道为什么我会想错了。已经修改 :) - user1470788

0
这样怎么样?将列表分成三个列表,一个包含 ID < mate,第二个包含 ID > mate,第三个包含 mate is None。然后,通过 ID 对每个排序后的列表进行连接。
我已经为 Ind 类添加了一个 __repr__ 方法以提高输出可读性。
class Ind(object):
    def __init__(self,ID,mate):
        self.ID=ID
        self.mate=mate

    def __repr__(self):
        return 'Ind({},{})'.format(self.ID,self.mate)

population=[Ind(8,None), Ind(1,2), Ind(2,3), Ind(2,1), Ind(12,None), Ind(3,2), Ind(10,11), Ind(11,10)]

def custom_sort(pop):
    singles, less, more = [], [], []
    for p in pop:
        if p.mate is None:
            singles.append(p)
        elif p.ID < p.mate:
            less.append(p)
        elif p.ID > p.mate:
            more.append(p)
    comp = lambda x,y: cmp(x.ID,y.ID)
    return sorted(less,cmp=comp) + sorted(singles,cmp=comp) + sorted(more,cmp=comp,reverse=True)


print custom_sort(population)

这将输出:

[Ind(1,2), Ind(2,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,2), Ind(2,1)]

0

使用自定义键函数可以做很多事情:

def my_key(ind):
    if ind.mate is None:
        return 0
    if ind.ID < ind.mate:
        return -ind.ID - 1
    else:
        return ind.mate + 1
population.sort(key=my_key)

这里假设ID永远不会是负数。如果ID总是大于0,那么您可以忽略-1+1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接