帮助排序:首先由此,然后由此

时间:2009-11-01 14:05:00

标签: python sorting

我有一个我要排序的元组列表,可以使用一些帮助。

我想在元组中排序的字段看起来像“XXX_YYY”。首先,我想以相反的顺序对XXX值进行分组,然后,在这些组中,我想以正常的排序顺序放置YYY值。 (注意:我同样高兴,实际上,以这种方式排序元组中的第二项,反向顺序第一个字,正常顺序第二个。)

以下是我所拥有的以及最终我想要的内容的一个例子...不知道该怎么做。

mylist = [
    (u'community_news', u'Community: News & Information'), 
    (u'kf_video', u'KF: Video'), 
    (u'community_video', u'Community: Video'), 
    (u'kf_news', u'KF: News & Information'), 
    (u'kf_magazine', u'KF: Magazine')
]

我想在此列表中执行某种sort(),将输出更改为:

sorted = [
    (u'kf_magazine', u'KF: Magazine'),
    (u'kf_news', u'KF: News & Information'), 
    (u'kf_video', u'KF: Video'), 
    (u'community_news', u'Community: News & Information'), 
    (u'community_video', u'Community: Video'), 
]

我怀疑可能有一种pythonic方式来处理这个问题,但我无法绕过它。

3 个答案:

答案 0 :(得分:10)

def my_cmp(x, y):
  x1, x2 = x[0].split('_')
  y1, y2 = y[0].split('_')
  return -cmp(x1, y1) or cmp(x2, y2)

my_list = [
    (u'community_news', u'Community: News & Information'), 
    (u'kf_video', u'KF: Video'), 
    (u'community_video', u'Community: Video'), 
    (u'kf_news', u'KF: News & Information'), 
    (u'kf_magazine', u'KF: Magazine')
]

sorted_list = [
    (u'kf_magazine', u'KF: Magazine'),
    (u'kf_news', u'KF: News & Information'), 
    (u'kf_video', u'KF: Video'), 
    (u'community_news', u'Community: News & Information'), 
    (u'community_video', u'Community: Video'), 
]

my_list.sort(cmp=my_cmp)
assert my_list == sorted_list

答案 1 :(得分:8)

用于排序的自定义比较函数,如现有答案所示,确实可以轻松地按升序和降序排序 - 但它们存在严重的性能问题,并已在Python中删除3,只留下首选的自定义方法 - 自定义密钥提取功能......更加快速,但对于相对罕见的混合升序/降序分类用例更加精巧。

在Python 2.*中,支持任意一种自定义(在sortsorted: - )的同一调用中不支持 ,自定义比较function可以作为cmp=命名参数传递;或者,自定义键提取函数可以作为key=命名参数传递。在Python 3.*中,只有后一个选项可用。

绝对值得理解密钥提取方法,即使您认为自己刚刚使用自定义比较方法解决了问题:不仅仅是为了提高性能,还为了面向未来(Python 3)和一般性( key=方法也适用于minmaxitertools.groupby ......比cmp=方法更为通用!)。

当所有关键子字段以相同的方式排序(所有提升或全部降序)时,密钥提取非常简单 - 您只需提取它们;如果“另一种方式”的子字段是数字(你只是在提取时改变它们的符号),它仍然很容易;精巧的案例就是你所拥有的 - 多个字符串字段必须以相反的方式进行比较。

解决问题的一个相当简单的方法是一个小小的垫片类:

class Reverser(object):
  def __init__(self, s): self.s = s
  def __lt__(self, other): return other.s < self.s
  def __eq__(self, other): return other.s == self.s

请注意,您只需提供__lt____eq__<==运营商) - sort,并且朋友会综合所有其他比较,如果需要的话,基于这两个。

因此,有了这个小辅助工具,我们可以轻松地进行......:

def getkey(tup):
    a, b = tup[0].split('_')
    return Reverser(a), b

my_list.sort(key=getkey)

如您所见,一旦您“获得”反向器和密钥提取概念,您基本上不需要为使用密钥提取而不是自定义比较付出代价:我建议的代码是反向器类的4个语句(您可以编写一次并将其放入你的“好东西包”模块中,三个用于密钥提取功能,当然还有一个用于sortsorted调用 - 总共八个与4 + 1 == 5最简洁形式的自定义比较方法(即使用带有符号更改的cmp或带有交换参数的cmp)。三个陈述对于密钥提取的优势来说并不是一个代价! - )

对于这么短的名单来说,表现显然不是一个大问题,但是平均时间稍长(10倍)......:

# my_list as in the Q, my_cmp as per top A, getkey as here

def bycmp():
  return sorted(my_list*10, cmp=my_cmp)

def bykey():
  return sorted(my_list*10, key=getkey)

...

$ python -mtimeit -s'import so' 'so.bykey()'
1000 loops, best of 3: 548 usec per loop
$ python -mtimeit -s'import so' 'so.bycmp()'
1000 loops, best of 3: 995 usec per loop

也就是说,key=方法在处理50个项目列表时已经显示了近两倍的性能提升(将列表排序速度提高了两倍) - 非常值得“8行”的适度价格超过5“,特别是我已经提到的所有其他优点!

答案 2 :(得分:2)

>>> def my_cmp(tuple_1, tuple_2):
    xxx_1, yyy_1 = tuple_1[0].split('_')
    xxx_2, yyy_2 = tuple_2[0].split('_')
    if xxx_1 > xxx_2:
        return -1
    elif xxx_1 < xxx_2:
        return 1
    else:
        return cmp(yyy_1, yyy_2)


>>> import pprint
>>> pprint.pprint(sorted(mylist, my_cmp))
[(u'kf_magazine', u'KF: Magazine'),
 (u'kf_news', u'KF: News & Information'),
 (u'kf_video', u'KF: Video'),
 (u'community_news', u'Community: News & Information'),
 (u'community_video', u'Community: Video')]

不是世界上最漂亮的解决方案......