【问题标题】:Most efficient way to remove duplicates删除重复项的最有效方法
【发布时间】:2017-10-09 22:36:13
【问题描述】:

我有一个需要从中删除重复条目的日志文件。文件中的每一行由逗号分隔的三部分组成,我们分别称它们为A、B和C。

当且仅当它们的 A 和 C 相等时,两个条目是重复的。如果发现重复,则保留 B 最大的那个。

真实日志文件行数较多,以下仅作为简化示例:

日志文件(输入):

hostA, 1507300700.0, xyz
hostB, 1507300700.0, abc
hostB, 1507300800.0, xyz
hostA, 1507300800.0, xyz
hostA, 1507300900.0, xyz

删除重复项后的日志文件(输出):

hostB, 1507300700.0, abc
hostB, 1507300800.0, xyz
hostA, 1507300900.0, xyz

我尝试将文件作为两个列表读取,然后按照以下内容进行比较:

for i in full_log_list_a:
    for j in full_log_list_b:
        if i[0] == j[0] and i[2] == j[2] and i[1] > j[1]:
            print(', '.join(i[0]), file=open(new_file, 'a'))

我还尝试了其他一些方法,但无论我做什么,它最终都会在列表中迭代太多次并创建一堆重复条目,或者它无法仅找到具有最大 B 的项目。我知道可能有一个明显的答案,但我被困住了。有人可以指出我正确的方向吗?

【问题讨论】:

  • 合理方法的每个子步骤都将基于排序(不一定是嵌套循环)。查找集合交集算法(可能还有嵌套排序/稳定排序)。大多数理智的方法也将使用 numpy 或 pandas(尽管 python 也带来了所有必要的组件)。

标签: python-3.x


【解决方案1】:

我认为您要查找的是 dict,而不是列表。

当您阅读日志文件时,您将条目添加到字典中,其中每个条目由一个键(A,C)和一个值 B 组成。如果一个键已经存在,您将 B 与映射到该键的值进行比较,并在必要时重新映射键(即如果 B 大于当前映射到键的值)。

示例(对变量 a、b 和 c 使用更好的名称):

log_file_entries = {}

with open(log_file, 'r') as f:
    for line in f:
        a, b_str, c = line.split(', ')
        b = int(b_str)

        if (a, c) in log_file_entries:
            if b < log_file_entries[(a, c)]:
                continue

        log_file_entries[(a, c)] = b

这是一个循环。由于 dicts 所需的操作(通常)在时间上是恒定的,即 O(1),因此整体 time complexity 将为 O(n),比嵌套循环的 O(n²) 时间复杂度要好得多。

当你稍后重写文件时,你可以像这样循环遍历字典:

with open(new_file, 'a') as f:
    for (a, c), b in log_file_entries.items():
        print('{0}, {1}, {2}'.format(a, b, c), file=f)

抱歉,如果任何代码或术语不正确,我有一段时间没有接触 Python。

(PS 在您的示例代码中,您使用了两个列表,而您可以在两个循环中使用相同的列表。)


更新

如果你希望一个键的值包含日志文件中每一行的每一部分,你可以像这样重写上面的代码:

log_file_entries = {}

with open(log_file, 'r') as f:
    for line in f:
        a, b_str, c = line.split(', ')
        b = int(b_str)

        if (a, c) in log_file_entries:
            if b < log_file_entries[(a, c)][1]:
                continue

        log_file_entries[(a, c)] = (a, b, c)

with open(new_file, 'a') as f:
    for entry in log_file_entries.values():
        print(', '.join(entry), file=f)

【讨论】:

  • 你说得对,我应该一直在使用字典。我已经修改了您提供的内容以使用 k, v 查找原始列表中的完整行(以获取日志中的所有其他字段)。看起来它正在工作,但是有没有办法让我在你编写的单个循环中完成所有这些工作?
  • @DavidJ。我不确定我明白你的意思。您希望将一个键映射到日志文件中一行的每个部分吗?例如,您希望 (a, c) 映射到 (a, b, c)?我已经更新了答案。如果我误解了你,请再问。
  • 这很棒。感谢您的回答和更新!
猜你喜欢
  • 1970-01-01
  • 2018-04-02
  • 2015-08-02
  • 2017-10-18
  • 2016-09-06
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多