【问题标题】:Python loop through list and return "out of sequence" valuesPython循环遍历列表并返回“乱序”值
【发布时间】:2016-07-09 16:05:53
【问题描述】:

考虑这个列表:

dates = [
    ('2015-02-03', 'name1'),
    ('2015-02-04', 'nameg'),
    ('2015-02-04', 'name5'),
    ('2015-02-05', 'nameh'),
    ('1929-03-12', 'name4'),
    ('2023-07-01', 'name7'),
    ('2015-02-07', 'name0'),
    ('2015-02-08', 'nameh'),
    ('2015-02-15', 'namex'),
    ('2015-02-09', 'namew'),
    ('1980-12-23', 'name2'),
    ('2015-02-12', 'namen'),
    ('2015-02-13', 'named'),
]

我如何识别那些乱序的日期。我不在乎他们是否重复或跳过,我只需要那些不合时宜的方式。即,我应该回来:

('1929-03-12', 'name4'),
('2023-07-01', 'name7'),
('2015-02-15', 'namex'),
('1980-12-23', 'name2'),

Namex 不太明显,但不在列表的一般顺序中。

我的简单开头(我为了简化问题而删除了)显然是不完整的。


更新:基于 cmets,Longest Increase Subsequence (LIS) 的实现似乎可以帮助我入门,这里可以找到一个 python 实现:

似乎一旦我获得了 LIS,我就可以将它与原始列表进行比较,看看差距在哪里……令人着迷。 SO 是令人敬畏的蜂巢思维。

【问题讨论】:

  • 我们如何判断namex 出现故障?
  • 请更准确地定义您的“乱序”
  • 计算“平均值”和标准差,找到超出两个标准差的任何值?
  • ok ok... 引用和去错字。教我写草率的差不多代码;)
  • 你的问题的表述让我想起了en.wikipedia.org/wiki/Longest_increasing_subsequence

标签: python list loops


【解决方案1】:

简答,通用解决方案

使用我的answer to the "Longest increasing subsequence" question,这可以简单地实现为:

def out_of_sequence(seq):
  indices = set(longest_subsequence(seq, 'weak', key=lambda x: x[0], index=True))
  return [e for i, e in enumerate(seq) if i not in indices]

更长的答案,具体的解决方案

基于question at Code Reviewquestion about non-decreasing sequences(因为这就是您所追求的),这是您问题的解决方案:

from bisect import bisect_right
from operator import itemgetter


def out_of_sequence(seq, key = None):
  if key is None: key = lambda x: x 

  lastoflength = [0] # end position of subsequence with given length
  predecessor = [None] # penultimate element of l.i.s. ending at given position

  for i in range(1, len(seq)):
    # find length j of subsequence that seq[i] can extend
    j = bisect_right([key(seq[k]) for k in lastoflength], key(seq[i]))
    # update old subsequence or extend the longest
    try: lastoflength[j] = i
    except: lastoflength.append(i)
    # record element preceding seq[i] in the subsequence for backtracking
    predecessor.append(lastoflength[j-1] if j > 0 else None)

  indices = set()
  i = lastoflength[-1]
  while i is not None:
    indices.add(i)
    i = predecessor[i]

  return [e for i, e in enumerate(seq) if i not in indices]


print(*out_of_sequence(dates, itemgetter(0)), sep='\n')

输出:

('1929-03-12', 'name4')
('2023-07-01', 'name7')
('2015-02-15', 'namex')
('1980-12-23', 'name2')

key 参数(受 sorted 内置函数的启发)指定一个参数的函数,用于从每个列表元素中提取比较键。默认值为None,因此调用者可以方便地说“我想直接比较元素”。如果设置为None,我们使用lambda x: x 作为identity function,因此在比较之前元素不会发生任何变化。

在您的情况下,您希望使用日期作为比较键,因此我们将itemgetter(0) 用作key。而itemgetter(1) 将使用名称作为key,请参阅:

>>> print(*map(itemgetter(1), dates))
name1 nameg name5 nameh name4 name7 name0 nameh namex namew name2 namen named

使用itemgetter(k) 等价于lambda x: x[k]

>>> print(*map(lambda x: x[1], dates))
name1 nameg name5 nameh name4 name7 name0 nameh namex namew name2 namen named

将它与map 一起使用相当于生成器表达式:

>>> print(*(x[1] for x in dates))
name1 nameg name5 nameh name4 name7 name0 nameh namex namew name2 namen named

但如果我们使用类似的列表推导将序列传递给out_of_sequence,我们会得到与预期不同的结果:

>>> print(*out_of_sequence([x[0] for x in dates]), sep='\n')
1929-03-12
2023-07-01
2015-02-15
1980-12-23

同样,如果我们直接比较日期-名称对,我们会得到错误的结果(因为 'nameg''name5' 更大):

>>> print(*out_of_sequence(dates), sep='\n')
('2015-02-04', 'nameg')
('1929-03-12', 'name4')
('2023-07-01', 'name7')
('2015-02-15', 'namex')
('1980-12-23', 'name2')

因为我们想要返回日期和名称,并且我们只想按日期排序,所以我们需要传递一个使用 key 参数提取日期的函数。

另一种方法是去掉key,直接写:

j = bisect_right([seq[k][0] for k in lastoflength], seq[i][0])

但由于这是stackoverflow,也许有一天另一个人会得到这个答案并且需要其他一些密钥提取,因此我决定在这里发布更通用的解决方案。

【讨论】:

  • @Trees4theForest 如果您单击“X 时间前编辑”链接,您可以看到所有编辑。但是在这种情况下,我只更改了一些 cmets 和变量名,没有任何实际意义。
  • 定期进行测试,效果很好——太棒了!据我了解,key=None arg(以及随后的 lambda x: x)通过这个做什么
  • 我在回答中添加了解释。
  • 嗨@arekolek - 我非常感谢你的工作和解释。但是,我确实发现了一个限制:如果 out-of-sequence 项目的第一次运行比 in-sequence 项目的第一次运行长,那么无论如何随后有许多按顺序的项目,该函数返回按顺序项目的初始运行。 IE 1,2,3,991,992,993,994,5,6,7,8,9,10,11... 返回:1,2,3
  • 这很有趣,但也很奇怪,因为it returns 991,992,993,994 for me。但如果您确定有问题,我认为最好发布一个新问题。
【解决方案2】:

如果当前日期大于最后一个好日期,这将为您建立一个新的 anchor_date。

import arrow

out_of_order = []
anchor_date = arrow.get(dates[0][0])
for dt, name in dates:
  if arrow.get(dt) < anchor_date:
    out_of_order.append((dt, name))
  else:
    anchor_date = arrow.get(dt)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    • 2019-04-15
    • 2015-01-23
    • 2017-09-17
    • 2012-03-10
    • 2020-06-18
    相关资源
    最近更新 更多