【问题标题】:Python sort multiple lists by date and print list namesPython按日期对多个列表进行排序并打印列表名称
【发布时间】:2016-08-24 04:41:31
【问题描述】:

在 Python 3.5.2 中工作 我有四个日期列表,每个都按升序排列,其中列表的长度不相等。每个日期列表都是通过查找更长的日期列表来生成的。示例日期值和数据类型如下所示:

In: print (date, type(date))
Out: 725722.0 <class 'numpy.float64'>

我使用相应的循环构建每个日期列表。要查看我转换为字符串的值并打印每个列表。所以我可以将数据类型排序为 numpy float64 或转换为字符串。每个列表中实际数据的相关值(基于特定的过滤器设置)如下所示:

a = [12-17-1987, 11-22-1989, 03-05-1990, 11-12-1990]
b = [12-16-1987, 03-02-1990, 11-12-1990]
c = [10-09-1986, 12-16-1987, 03-05-1990, 11-12-1990]
d = [10-16-1985, 08-20-1986, 10-15-1986, 12-16-1987, 03-02-1990]

我需要将所有四个列表中的日期按 mm-dd-yyyy 升序排序,打印每个日期,并在每个日期旁边打印相应列表的名称,如下例所示:

# Desired Printout
10-16-1985  d
08-20-1986  d
10-09-1986  c
10-15-1986  d
12-16-1987  b
12-16-1987  c
12-16-1987  d
12-17-1987  a
11-22-1989  a
03-02-1990  b
03-02-1990  d
03-05-1990  a
03-05-1990  c
11-12-1990  a
11-12-1990  b
11-12-1990  c

这将使我能够直观地确认四组不同数据中的一系列事件。我会尝试创建一个字典并按日期排序以打印到屏幕或磁盘,但我注意到使用 map 或 lambda 函数的类似答案可能会提供更优雅的解决方案。如果我将这些信息存储在磁盘上,最好的数据结构和解决方案是什么?

【问题讨论】:

  • 我已阅读您的问题描述,并在下面的回答中做出了一些假设。如果我忽略了某些事情或做出了错误的假设,请随时发表评论。
  • 我接受了@tedmiston 的解决方案,因为我的问题通过在我的应用程序中应用方法 2 得到了解决。谢谢!

标签: python list sorting


【解决方案1】:

我有几个关于这个的:

  1. “最佳”不明确。这可能意味着最小化算法复杂性、最小化运行时间、最小化内存使用、最简单的实现或读取、最少的代码量等。

  2. 除非您有数千个条目,否则可能不值得优化您的数据结构或算法。社区公认的最佳做法是分析和优化整个程序的缓慢之处。

一个简单的实现无非就是加入列表并使用内置的sorted 对它们进行排序。例如,您可以考虑以下几个排序选项:

import datetime

a = ['7-1-1987', '1-1-1990']
b = ['7-2-1987', '1-5-1990']
c = ['7-1-1987', '1-3-1990']
d = ['1-10-1985', '7-10-1986']

# hold on to list name
a = [(i, 'a') for i in a]  # [(date, list_name), ...]
b = [(i, 'b') for i in b]
c = [(i, 'c') for i in c]
d = [(i, 'd') for i in d]

dates = a + b + c + d  # combine into one flat list
for i in dates: print(i)

输出

('7-1-1987', 'a')
('1-1-1990', 'a')
('7-2-1987', 'b')
('1-5-1990', 'b')
('7-1-1987', 'c')
('1-3-1990', 'c')
('1-10-1985', 'd')
('7-10-1986', 'd')

方法 1 - 将每个日期字符串解析为一个日期时间对象,对它们进行就地排序,并输出一个日期时间对象列表。

dates_1 = [(datetime.datetime.strptime(d, '%m-%d-%Y').date(), l) for d, l in dates]
dates_1.sort()
for i in dates_1: print(i)

输出

(datetime.date(1985, 1, 10), 'd')
(datetime.date(1986, 7, 10), 'd')
(datetime.date(1987, 7, 1), 'a')
(datetime.date(1987, 7, 1), 'c')
(datetime.date(1987, 7, 2), 'b')
(datetime.date(1990, 1, 1), 'a')
(datetime.date(1990, 1, 3), 'c')
(datetime.date(1990, 1, 5), 'b')

方法 2 - 使用动态解析日期的 lambda 函数对日期进行排序,并输出(新)字符串列表。

dates_2 = sorted(dates, key=lambda d: (datetime.datetime.strptime(d[0], '%m-%d-%Y').date(), d[1]))
for i in dates_2: print(i)

输出

('1-10-1985', 'd')
('7-10-1986', 'd')
('7-1-1987', 'a')
('7-1-1987', 'c')
('7-2-1987', 'b')
('1-1-1990', 'a')
('1-3-1990', 'c')
('1-5-1990', 'b')

方法 3 - 使用 heapq.merge 更有效地排序。感谢@friendlydog 的建议。

import datetime
import heapq

a = ['7-1-1987', '1-1-1990']
b = ['7-2-1987', '1-5-1990']
c = ['7-1-1987', '1-3-1990']
d = ['1-10-1985', '7-10-1986']

def strs_to_dates(date_strs, list_name):
    """
    Convert a list of date strings to a generator of (date, str) tuples.
    """
    return ((datetime.datetime.strptime(date, '%m-%d-%Y').date(), list_name) for date in date_strs)

a = strs_to_dates(a, 'a')
b = strs_to_dates(b, 'b')
c = strs_to_dates(c, 'c')
d = strs_to_dates(d, 'd')

dates_3 = heapq.merge(a, b, c, d)
for i in dates_3: print(i)

输出

(datetime.date(1985, 1, 10), 'd')
(datetime.date(1986, 7, 10), 'd')
(datetime.date(1987, 7, 1), 'a')
(datetime.date(1987, 7, 1), 'c')
(datetime.date(1987, 7, 2), 'b')
(datetime.date(1990, 1, 1), 'a')
(datetime.date(1990, 1, 3), 'c')
(datetime.date(1990, 1, 5), 'b')

注意事项:

  1. 我假设您输入字符串的格式是“日-月-年”。
  2. 我假设当同一个日期在多个列表中时,您希望按列表名称按字母数字顺序排序。
  3. 我将输出列表的格式留给读者作为练习。
  4. 这两个示例都在 Python 2 / 3 下运行。

在本例中,key 参数是一个 lambda。没有它,它将按字母顺序对字符串进行排序。这让我们可以覆盖它并按年 > 月 > 日排序。

更精细的实现可以利用对列表进行预排序的保证。维基百科有一个merge algorithms 列表供您考虑。

【讨论】:

  • +1 用于datetime 模块。如果你想通过合并进行排序,Python 很方便地提供了heapq.merge
  • @friendlydog 好点。这种方式肯定有提高效率的空间,直接使用itertools,等等。我通过添加第三个使用heapq.merge(...)的示例更新了答案。
  • 请注意,我想要的输出要求两个日期都按升序排列,并且必须向用户指示每个日期旁边的列表名称。所以我需要生成日期-名称对,而不仅仅是排序日期。
  • @SystemTheory 谢谢,我刚刚更新了它。幸运的是一个简单的改变——我基本上只是在日期时间标量的地方使用元组。这使得按多个标准进行排序变得非常容易。
  • 应用方法 2 解决了我的特殊问题。感谢您详细描述几个有用的选项。
【解决方案2】:

假设您的日期都格式化为mm-dd-yyyy(与您的示例不同),这应该可以解决问题:

import itertools

lists = dict(a=['7-1-1987', '1-1-1990'],
             b=['7-2-1987', '1-5-1990'],
             c=['7-1-1987', '1-3-1990'],
             d=['1-10-1985', '7-10-1986'])

for d, v in sorted(itertools.chain(*([(e, n) for e in v] for n, v in lists.items()))):
    print d, v

如果日期格式不正确,则必须向 sorted 函数添加自定义排序键,以将日期解析为可正确比较的对象。

【讨论】:

  • 以上代码导致 Python 3.5.2 抛出以下错误:AttributeError: 'dict' object has no attribute 'iteritems'。另外,如果我已经有列表 a、b、c 和 d,我该如何从列表中构造字典?
  • @SystemTheory 在 Python 3 中是 dict.items。更多信息 - stackoverflow.com/questions/10458437/…
  • @SystemTheory 如果您提供有关如何构建列表的更多信息,那么我们可以提供有关构建字典或以不同方式映射列表名称的好主意。
【解决方案3】:
#  Create the list of all dates, combining the four lists you have. Keep
#  the information about which list value comes from
all_dates = [(x, 'a') for x in a] + [(x, 'b') for x in b] + [(x, 'c') for x in c] + [(x, 'd') for x in d]

#  Sort with key a simple date parser. The way it works is:
#     1. It takes a date 11-12-2012 and splits it by '-' so that we get ['11', '12', '2012']
#     2. Reverses the list ([::-1]) so that the year is the most significant (['2012', '12', '11'])
#     3. Applies int to each so that they are compared as numbers ([2012, 12, 11]). Note that Python can automatically compare things like that
all_dates.sort(key = lambda x: list(map(int, x[0].split('-')[::-1])))

#  Print the result
for date in all_dates:
    print ' '.join(date)

【讨论】:

  • 这个答案解决了我在修复用于 Python 3.5.2 的 print 语句后的问题:print (' '.join(date))。
  • 我很快就批准了这个答案。在进一步测试后,某些日期 (mm-dd-yyyy) 不会以严格的升序打印。明显错误显示在给定年份的 6 月、7 月、8 月或 9 月之前的 10 月和 11 月。
【解决方案4】:

你真的不需要任何花哨的东西。只需在每个列表中的第一项上做一分钟。然后检查最小值是否在任何列表中,然后执行 list.pop() 和打印。这是一种有效且有意义的简单方法。我可以为您提供代码,但这应该足够清楚。

【讨论】:

  • 我可能会尝试使用 list.index() 而不是 list.pop() 这种方法,尽管我没有立即掌握控制结构中的必要逻辑。
  • 当然,祝你好运!如果你不能让它以这种方式工作,我会感到惊讶。而且我敢打赌,您会发现您的代码比使用 reduce/lambda 版本更具可读性。有时这些东西很好,但很多时候你可以编写几乎一样快的代码,并且更容易阅读、调试和纠正。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
  • 2017-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多