【问题标题】:Differences in lists of lists using Python使用 Python 的列表列表的差异
【发布时间】:2015-02-10 18:08:14
【问题描述】:

我有两个列表,如下所示。我知道我可以使用 set(list1)-set(list2) 或反之亦然来打印与其他相应列表不同的列表。但是,我不想打印完整的列表,我只想要列表中被修改的部分。

例如,list1:

[['Code', 'sID', 'dID', 'cID', 'ssID'], ['ABCD-00', 'ABCD-00-UNK', '358', '1234', '9999'], ['ABCD-01', 'ABCD-00-UNK', 160, '993', '587']]

列表2:

[['Code', 'sID', 'dID', 'cID', 'ssID', 'AddedColumn'], ['ABCD-00', 'ABCD-00-UNK', '358', '1234', '9999', 'AddedValue1'], ['ABCD-01', 'ABCD-00-UNK', 160, '993', 'ChangedValue', 'AddedValue2']]

如果我确实设置了差异,它会打印出整个列表。当“代码”、“sID”相同时,我希望输出仅显示不同/添加/删除的列。

每个列表列表的第一个列表是标题。所以我想在“代码”、“sID”列中的值匹配时比较列表。

期望的输出:

Added - ['AddedColumn', 'AddedValue1', 'AddedValue2']
Deleted - []
Changed - ['Code', 'ABCD-01', 'ssID', 'ChangeValue']

类似的东西或者更简单的东西也可以。

我试过的代码:

from difflib import SequenceMatcher

matcher = SequenceMatcher()
for a, b in zip(list1, list2):
    matcher.set_seqs(a, b)
    for tag, i1, i2, j1, j2 in matcher.get_opcodes():
        if tag == 'equal': continue
        print('{:>7s} {} {}'.format(tag, a[i1:i2], b[j1:j2]))

它在比较相应的列表时效果很好,即 list1 中的 sub-list1 与 list2 中的 sub-list1。但我希望它在整个列表中进行比较,因为如果缺少特定的子列表,它会打印出一切都不同。子列表是指,例如['Code', 'sID', 'dID', 'cID', 'ssID'] in list1 是 sub-list1。

【问题讨论】:

  • 你真的不能做“set(...) - set(...)”的事情:列表是可变对象,不能被设置成员
  • 您是否受困于列表和特定格式?好像不太好。
  • @jsbueno 我将它们更改为集合,我没有将其包含在问题中。
  • @user3467349 我不应该只使用列表,本质上是比较两个 CSV 文件,我以列表的形式拥有。
  • 这让事情变得更糟:-) - 您的数据被视为按列排列并且严格依赖于顺序 - 您的程序如何知道“ABCD-00”是关于“代码”的,如果它是不是子序列中的第一个元素?

标签: python list set compare


【解决方案1】:

所以 - 正如人们在 cmets 中所说,您真正应该做的是将您称为“子列表”的每组数据读入适当的对象 - 然后他们比较这些对象的属性。

例如,要坚持使用本机类型,如果“代码”和“sID”构成您的键,则每一行都可以是由您的代码和 sid 值的元组键控的字典。

但 htis 问题似乎需要自定义类 - -

鉴于上述列表之一 - 您几乎可以从以下内容开始:

class MyThing(object):
     def __init__(self, *args):
         for attrname, arg in zip(['Code', 'sID', 'dID', 'cID', 'ssID'], args):
            setattr(self, attrname, arg)

     def __hash__(self):
         # This is not needed for the OrderedDict bwellow, but allows you
         # to use sets with the objects if you want
         return hash(self.Code + self.sID)

from collections import OrderedDict
myobjs = OrderedDict()
for line in list1[1:]:
    obj = MyThing(line)
    id = obj.Code + obj.sId
    if id in myobjs:
        # do your comparisson -logging -printing stuff here
    else:
        myobjs[id] = obj

它实际上可以在没有类和对象创建部分的情况下完成 - 只需将“行”存储在字典中 - 但类使您能够以更清洁的方式做很多事情。复杂的__init__ 只是为了避免重复很多self.sId = sId 行的简写。

【讨论】:

  • 感谢您的回复。但是,棘手的部分是我有多个文件,每个文件的标题可能会改变。
  • 使用上面的代码应该不难——只需使用我对标题进行硬编码的那一行,然后在其中引用文件的第一行。如果您的关键字段并不总是“代码”和“sId”,您将不得不适应它们。
【解决方案2】:

这是我的基本解释。 OP 对他们想要的 changed 列表不太清楚 - 所以他们应该更具体地更新他们的要求。正如 jsbueno 建议的那样,dict 可能会更好 - 这真的取决于,如果它是它的格式,列表会更便宜。

added = []
deleted = []
changed = []
for  sub_l1, sub_l2 in zip(l1, l2): 
    for i in range(min(len(sub_l1), len(sub_l2))): 
        if sub_l1[i] != sub_l2[i]: 
            changed.append(sub_l2[i])
    if len(sub_l2) > len(sub_l1): 
        added.append(sub_l2[len(sub_l1):len(sub_l2)])
    elif len(sub_l1) > len(sub_l2):
        deleted.append(sub_l1[len(sub_l2):len(sub_l1)])

样本输出:

In [66]: added
Out[66]: [['AddedColumn'], ['AddedValue1'], ['AddedValue2']]
In [67]: deleted
Out[67]: []
In [68]: changed
Out[68]: ['ChangedValue'] 

请注意,changed 不会告诉您更改了哪个值,通常您可能需要一个包含 CSV 子列表和列号的元组。

【讨论】:

  • 如果想法是对两个列表中的所有元素进行无序比较 - 即 O(m X n) 与 O(m x 1)
  • 这不是乱序比较(至少就我对 OP 问题的回答和理解而言)。如果所有数据都已组织在一个表中,则哈希查找不会提供太多优势。 (您解析一次以获取所有修改值的行、列元组,之后查找很便宜)。 p.s.我同意总的来说哈希对象更容易使用,就像我在帖子中所说的那样,它取决于应用程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
相关资源
最近更新 更多