【问题标题】:Python Sorting QuestionPython排序问题
【发布时间】:2010-07-24 15:03:16
【问题描述】:

我需要在 Python 中对以下元组列表进行排序:

ListOfTuples = [('10', '2010 Jan 1;', 'Rapoport AM', 'Role of antiepileptic drugs as preventive agents for migraine', '20030417'), ('21', '2009 Nov;', 'Johannessen SI', 'Antiepilepticdrugs in epilepsy and other disorders--a population-based study of prescriptions', '19679449'),...]

我的目的是按降序年(listOfTuples[2])和升序作者(listOfTuples[2])排序:

sorted(result, key = lambda item: (item[1], item[2]))

但它不起作用。我怎样才能获得排序稳定性?

【问题讨论】:

  • 可能的日期格式有哪些?这里我们看到“年/月/日”和“年/月/日”。是否可以只遇到“年”?
  • 是的,因为数据格式不规则。

标签: python list sorting stability


【解决方案1】:
def descyear_ascauth(atup):
  datestr = atup[1]
  authstr = atup[2]
  year = int(datestr.split(None, 1)[0])
  return -year, authstr

... sorted(result, key=descyear_ascauth) ...

注意:您需要将年份提取为整数(而不是字符串),以便更改其符号 - - 后者是满足规范“降序”部分的关键技巧。在lambda 中压缩所有内容是可能的,但绝对没有理由这样做并牺牲更多的可读性,因为def 也可以正常工作(并且更具可读性)。

【讨论】:

  • Grazie Mille,sei semper gentilissimo! :) 我应该使用什么方法来添加另一个订单键,例如“月”?我应该将月份名称映射到字典('jan':1, 'feb:2')吗?
  • @Gianluca,使用显式 dict 可以让您完全控制,因此是我推荐的。您可以使用list(calendar.month_name) 来构建字典,例如以依赖于语言环境的方式,但它比保证的复杂得多,除非您在这个方向上有非常具体的需求。
  • 感谢您的回答:)。现在我无法决定选择哪个答案,因为@Duncan 也针对我的问题发布了一个可行的方法。到目前为止,这是一个品味(可读性与紧凑性)和性能(使用“技巧”与“使用 Python 方式”)的问题......
  • 做两种(根据@Duncan 的想法)和用复合键做一个(我的回答)都是非常完美的 Python 方式(不涉及任何技巧);但是,执行一次排序将节省大约一半的运行时间。 (就像@THC4k 的回答一样,老式的、近乎弃用的cmp 可能会慢得多)。可读性和紧凑性是关于lambda(邓肯拼错)与def(如我的回答)的选择,后者不会影响速度(正如我提到的,你可以将我的方法挤进lambda,这样做是个非常糟糕的主意)。
  • 当我意识到我不能只使用 itemgetter 因为年份是较长日期的一部分时,lambda 是作为后期编辑添加的(因此是错字)。您的答案可能几乎总是更快,但是,例如,如果您想以区域设置感知方式反转排序字符串而不是一年,那么解决如何做到这一点可能会很混乱。对多个键进行排序较慢,但具有清晰明了的优点。我认为 Gianluca 应该将这两个选项都保留在他的工具箱中。
【解决方案2】:

最简单的方法是分别对每个键值进行排序。从最不重要的键开始,一直到最重要的键。

所以在这种情况下:

import operator
ListOfTuples.sort(key=operator.itemgetter(2))
ListOfTuples.sort(key=lambda x: x[1][:4], reverse=True)

这是可行的,因为即使您使用 reverse 标志,Python 的排序也始终是稳定的:即 reverse 不只是先排序然后再反转(这会失去稳定性,它会在反转后保持稳定性。

当然,如果您有很多关键列,这可能会降低效率,因为它会执行多次完整排序。

您不必以这种方式将年份转换为数字,因为它是一种真正的反向排序,但如果您愿意,您也可以这样做。

【讨论】:

  • 您的解决方案紧凑且 Python 风格,但 @Alex 的速度更快。无法决定谁是赢家:)
【解决方案3】:

这是一个适用于所有事物的成语,即使是你无法否定的事物,例如字符串:

data = [ ('a', 'a'), ('a', 'b'), ('b','a') ]

def sort_func( a, b ):
    # compare tuples with the 2nd entry switched
    # this inverts the sorting on the 2nd entry
    return cmp( (a[0], b[1]), (b[0], a[1]) ) 

print sorted( data )                    # [('a', 'a'), ('a', 'b'), ('b', 'a')]
print sorted( data, cmp=sort_func )     # [('a', 'b'), ('a', 'a'), ('b', 'a')]

【讨论】:

  • cmp 不再适用于 Python 3,尽管 functools 中有 cmp_to_key
【解决方案4】:

这是一个粗略的解决方案,其中考虑了月份缩写和日期(如果找到):

import time
import operator

def sortkey(seq):
    strdate, author = seq[1], seq[2]
    spdate = strdate[:-1].split()
    month = time.strptime(spdate[1], "%b").tm_mon
    date = [int(spdate[0]), month] + map(int, spdate[2:])
    return map(operator.neg, date), author  

print sorted(result, key=sortkey)

"%b" 是区域设置的月份缩写名称,如果您不想处理区域设置,可以使用字典。

【讨论】:

    【解决方案5】:

    这是亚历克斯答案的 lambda 版本。我认为它现在看起来比 Duncan 的答案更紧凑,但显然 Alex 的答案的很多可读性已经丢失。

    sorted(ListOfTuples, key=lambda atup: (-int(atup[1].split(None, 1)[0]), atup[2]))
    

    可读性和效率通常比紧凑性更重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-29
      • 2017-06-30
      • 1970-01-01
      相关资源
      最近更新 更多