从列表列表中删除重复项

2023-12-07

我有一个Python列表列表:

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

我想从中删除重复的元素。如果它是一个普通列表而不是我可以使用的列表set。但不幸的是,该列表不可散列,并且无法创建列表集。仅元组。所以我可以将所有列表转换为元组,然后使用 set 并返回列表。但这并不快。

如何才能以最有效的方式做到这一点?

上面列表的结果应该是:

k = [[5, 6, 2], [1, 2], [3], [4]]

我不在乎维持秩序。

Note: 这个问题类似,但不完全是我需要的。搜索过,但没有找到完全相同的重复项。


基准测试:

import itertools, time


class Timer(object):
    def __init__(self, name=None):
        self.name = name

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print '[%s]' % self.name,
        print 'Elapsed: %s' % (time.time() - self.tstart)


k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] * 5
N = 100000

print len(k)

with Timer('set'):
    for i in xrange(N):
        kt = [tuple(i) for i in k]
        skt = set(kt)
        kk = [list(i) for i in skt]


with Timer('sort'):
    for i in xrange(N):
        ks = sorted(k)
        dedup = [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]


with Timer('groupby'):
    for i in xrange(N):
        k = sorted(k)
        dedup = list(k for k, _ in itertools.groupby(k))

with Timer('loop in'):
    for i in xrange(N):
        new_k = []
        for elem in k:
            if elem not in new_k:
                new_k.append(elem)

对于短列表,“循环”(二次方法)速度最快。对于长列表,它比除 groupby 方法之外的其他方法都快。这有道理吗?

对于短列表(代码中的列表),迭代 100000 次:

[set] Elapsed: 1.3900001049
[sort] Elapsed: 0.891000032425
[groupby] Elapsed: 0.780999898911
[loop in] Elapsed: 0.578000068665

对于更长的列表(代码中的列表重复 5 次):

[set] Elapsed: 3.68700003624
[sort] Elapsed: 3.43799996376
[groupby] Elapsed: 1.03099989891
[loop in] Elapsed: 1.85900020599

>>> k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
>>> import itertools
>>> k.sort()
>>> list(k for k,_ in itertools.groupby(k))
[[1, 2], [3], [4], [5, 6, 2]]

itertools通常为此类问题提供最快、最强大的解决方案,并且well值得深入熟悉!-)

Edit:正如我在评论中提到的,正常的优化工作集中在大输入(大 O 方法)上,因为它更容易,并且可以提供良好的努力回报。但有时(本质上是对于代码深层内部循环中的“悲剧性关键瓶颈”,这些瓶颈推动了性能限制的边界)人们可能需要更详细地了解,提供概率分布,决定要优化哪些性能度量(可能是上限或第 90 个百分位比平均值或中位数更重要,具体取决于应用程序),在开始时执行可能的启发式检查以根据输入数据特征选择不同的算法,等等。

仔细测量“点”性能(特定输入的代码 A 与代码 B)是这个极其昂贵的过程的一部分,并且标准库模块timeit在这里有帮助。然而,在 shell 提示符下使用它更容易。例如,这里有一个简短的模块来展示此问题的一般方法,将其另存为nodup.py:

import itertools

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

def doset(k, map=map, list=list, set=set, tuple=tuple):
  return map(list, set(map(tuple, k)))

def dosort(k, sorted=sorted, xrange=xrange, len=len):
  ks = sorted(k)
  return [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]

def dogroupby(k, sorted=sorted, groupby=itertools.groupby, list=list):
  ks = sorted(k)
  return [i for i, _ in itertools.groupby(ks)]

def donewk(k):
  newk = []
  for i in k:
    if i not in newk:
      newk.append(i)
  return newk

# sanity check that all functions compute the same result and don't alter k
if __name__ == '__main__':
  savek = list(k)
  for f in doset, dosort, dogroupby, donewk:
    resk = f(k)
    assert k == savek
    print '%10s %s' % (f.__name__, sorted(resk))

注意健全性检查(当你这样做时执行python nodup.py)和基本的提升技术(使每个函数都本地化常量全局名称以提高速度)将事物放在平等的基础上。

现在我们可以对这个小示例列表进行检查:

$ python -mtimeit -s'import nodup' 'nodup.doset(nodup.k)'
100000 loops, best of 3: 11.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort(nodup.k)'
100000 loops, best of 3: 9.68 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby(nodup.k)'
100000 loops, best of 3: 8.74 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.donewk(nodup.k)'
100000 loops, best of 3: 4.44 usec per loop

确认二次方法具有足够小的常数,使其对于重复值很少的小型列表具有吸引力。有一个没有重复的简短列表:

$ python -mtimeit -s'import nodup' 'nodup.donewk([[i] for i in range(12)])'
10000 loops, best of 3: 25.4 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby([[i] for i in range(12)])'
10000 loops, best of 3: 23.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.doset([[i] for i in range(12)])'
10000 loops, best of 3: 31.3 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort([[i] for i in range(12)])'
10000 loops, best of 3: 25 usec per loop

二次方法还不错,但排序和分组方法更好。等等等等.

如果(正如对性能的痴迷所表明的那样)此操作位于突破边界的应用程序的核心内循环中,则值得在其他代表性输入样本上尝试相同的测试集,可能会检测到一些简单的测量,这些测量可以启发式地让您选择一种或另一种方法(当然,测量必须很快)。

也很值得考虑为以下内容保留不同的表示形式:k-- 为什么它首先必须是列表的列表而不是一组元组?例如,如果重复删除任务很频繁,并且分析表明它是程序的性能瓶颈,则始终保留一组元组并仅在需要时从其中获取列表列表,总体上可能会更快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从列表列表中删除重复项 的相关文章

随机推荐

  • Angular:为组件字段提供对服务功能的引用并从模板调用它,但未按预期工作

    在我的 Plunker 里 修改的英雄之旅来自官方文档的应用程序 我在hero service doHeroesExist boolean console log doHeroesExist called this heroesExist
  • 如何使用 PHP 或 Ruby 从图像中删除某些颜色?

    假设有 3 个圆圈 红 蓝 黑 我只希望保留黑色圆圈 如何去除红色和蓝色圆圈 既然您要求 PHP 解决方案 首先加载你的图片图像从png创建或其他图像格式的类似功能 然后 使用imagesx and imagesy来获取图像的大小 现在 您
  • 如何使用CSS将子div居中对齐父div内?

    我是 html 和 css 新手 我不知道如何在父 div 内居中对齐子 div 这是我的代码 请回答并解决我的问题 CSS page position relative width 1220px height 670px backgrou
  • Flutter apk已安装但无法找到/打开

    我花了几周的时间试图解决这个问题 但我无法让它发挥作用 Context 颤振运行 我可以执行 flutter run 它将在我的手机上启动该应用程序 关闭应用程序后 我在应用程序页面中看不到它 我无法搜索它 访问 应用程序的唯一方法是转到设
  • 你能在 kivy 文件中换行吗?

    我的 kv 中有几行 文件非常长 80多个字符 我想知道是否有办法在下一行包装 继续它们 例如 我该如何从这个 Line points self pos 0 5 self pos 1 2 self pos 0 self width 5 se
  • Win32 - 从 C 代码回溯

    我目前正在寻找一种在 Windows 下从 C 代码 非 C 获取回溯信息的方法 我正在构建一个跨平台 C 库 具有引用计数内存管理功能 它还具有集成的内存调试器 可提供有关内存错误的信息 XEOS C 基础库 当发生故障时 启动调试器 提
  • Oracle JSON_OBJECT NULL ON NULL 子句不起作用

    我正在尝试让 Oracle 生成 JSONnullSQL 上的值NULL数据 如下 select json object key a value 1 key b value null null on null c1 json object
  • 在 mocha 中测试 NodeJS 时,域无法正确捕获错误

    当运行利用域进行错误处理的测试时 即使库内的域处理程序应该捕获错误 Mocha 仍然会抛出错误 如果我在 Mocha 之外执行代码 它会正常运行 让我相信问题出在 Mocha 上 Example foo js module exports
  • Java 中删除字符串中的空格

    我有一个像这样的字符串 mysz name john age 13 year 2001 我想删除字符串中的空格 我试过trim 但这只会删除整个字符串前后的空格 我也尝试过replaceAll W 但随后 也会被删除 我怎样才能获得一个字符
  • 如何在ajax响应中从字节流渲染pdf

    我正在开发一个移动应用程序 我们正在使用 jquery mobile 我们可以选择查看或下载 pdf 格式的记录 我无法控制后端 我将在 json 对象中获取 pdf 数据作为 ajax 响应 我想读取该数据并以 pdf 形式显示 我的下一
  • 共享 SD 卡中的图像

    我花了两周时间寻找如何共享存储在 SD 卡上的图像 但没有成功 This answer对我不起作用 也不是我正在寻找的 我正在与凸轮预览应用程序将图像存储到 SD 然后在应用程序内图库中显示它们 public class GalleryVi
  • 特定存储库的 Git 全局配置?

    意思是 有类似每个回购部分的东西 repo url 覆盖全局 不适用于特定存储库 选项 core filemode false editor notepad repo example com repo1 git core filemode
  • R 中的“错误恢复文件幻数”错误

    As in 加载 R 工作区时什么可能导致 错误的幻数 错误以及如何避免它 and R 有幻数 PNG 错误 我得到一个错误恢复文件幻数 error gt load fossilien dat Error bad restore file
  • 沙盒阻止我格式化字符串

    我有一个简单的常规脚本 node master echo I am about to try to use String format def jjj String format bob echo jjj 如果我将此脚本直接放入我的作业配置
  • GridLayout 未填充 JPanel

    我遇到 GridLayout 问题 并且整个 JPanel 未填充 我有一个 N M 网格 我用 N M 瓷砖填充它 它们扩展了 JPanel 添加所有这些图块后 JPanel 的底部和右侧仍然有空间 我认为 GridLayout 应该填满
  • Stateflow 在 reyclerview android kotlin 中引用整个数据

    嘿嘿我正在学习状态流在 Android 科特林中 我正在创建一个反向对话日历视图回收者视图 In my mainactivity有一个fragment 里面有我reyclerview 我的目标是做分页的东西 in my 回收者视图所以我先加
  • 使用 procf//status 了解进程状态

    我正在 Solaris 上工作 我知道如果有一个进程正在运行 就会有一个名为 proc
  • D lang - 在同一程序中使用 read 和 readln()

    我的 D 程序遇到了一个非常奇怪的问题 read s variable 本身工作得很好 而 readln variable 本身工作得很好 但是当我将两者放在一起时 readln 似乎被忽略了 使用 gdc 和 dmd 均发生错误 impo
  • 从头开始制作Android聊天应用程序

    我需要为Android 制作聊天应用程序 我想到使用PHP脚本来实现聊天应用程序 基本思想是将消息从android客户端发送到PHP脚本 并利用PHP脚本将消息发送到MySQL数据库 这些消息将广播给其他人 但问题是自动向其他人广播消息 有
  • 从列表列表中删除重复项

    我有一个Python列表列表 k 1 2 4 5 6 2 1 2 3 4 我想从中删除重复的元素 如果它是一个普通列表而不是我可以使用的列表set 但不幸的是 该列表不可散列 并且无法创建列表集 仅元组 所以我可以将所有列表转换为元组 然后