【问题标题】:Delete numbers from dictionary of lists从列表字典中删除数字
【发布时间】:2013-07-26 07:01:22
【问题描述】:

在诸如d=={1:[1,6,16],2:[1],7:[6]} 这样的排序列表字典中,如何有效地删除列表中小于给定值k 的所有数字(以及列表最终为空的键值对)?就我而言,d 会很大。

例如,如果k = 15 那么我们应该以d == {1:[16]} 结束。

我首先使用d = defaultdict(list) 初始化了字典。

我尝试使用bisect 来加快速度,但我一定是搞错了。

是否可以利用列表排序的事实来加快速度?

【问题讨论】:

  • 列表是否很大?还是只有词条多的字典?
  • @MartijnPieters 两者都很大。有些列表会很小,但有些则长达数十万。
  • 列表的元素是什么。只有整数?只有正整数?
  • @eyquem:排序值;这对bisect 来说已经足够了。
  • Martjin 请让我不想使用 bisect。

标签: python performance


【解决方案1】:
>>> d = {1:[1,6,16],2:[1],7:[6]}
>>> for lst in d.values(): lst[:] = [x for x in lst if x >= 16]
... 
>>> d
{1: [16], 2: [], 7: []}
>>> for k in list(d):
...     if not d[k]:
...         del d[k]
... 
>>> d
{1: [16]}

>>> d = {1:[1,6,16],2:[1],7:[6]}
>>> tmp = [(k, [x for x in lst if x >= 16]) for k, lst in d.items()]
>>> d = {k: v for k, v in tmp if v}
>>> d
{1: [16]}

使用bisect.bisect_left

>>> d = {1:[1,6,16],2:[1],7:[6]}
>>> for k in list(d):
...     d[k] = d[k][bisect.bisect_left(d[k], 16):]
...     if not d[k]:
...         del d[k]
... 
>>> d
{1: [16]}

【讨论】:

  • 两种解决方案都使用两个循环,这有点过头了。
  • 你的最后一个循环是 only 正确的解决方案,真的。
  • 谢谢。为什么 d[k][:] = v 而不是 d[k] = v?另外,我想知道是否创建一个新列表然后用它替换 d[k] 是否比 d[k] = [bisect.bisect_left(d[k], 16):], if len(d[k] == 0): 删除 d[k]。我不知道len是不是python中的一个常数时间操作。
  • @phoenix: len() list上是一个常数时间操作;列表会跟踪它们的长度。
  • @phoenix: 但你不需要这样做len(d[k]) == 0。只需执行not d[k]。或者,就像 falsetru 在这里所做的那样,只是 if v:。空容器在布尔上下文中评估为 false。
【解决方案2】:

你可以这样做:

from collections import defaultdict
from bisect import bisect_left
d = {1:[1,6,16],2:[1],7:[6]}
d1 = defaultdict(list)
k = 15
for key, value in d.iteritems():
    temp = value[bisect_left(value, 16):]
    if temp:
        d1[key] = temp

print d1.items()

打印:

[(1, [16])]

【讨论】:

  • @MartijnPieters Argh,我知道有更好的方法!更新
  • 接下来,使用bisect 而不是一个完整的循环将value 缩小到大小;对可能的 列表进行排序。
  • @MartijnPieters 我以前从未使用过bisect 模块。但这就是 falsetru 所做的吗?
  • 就是这么简单,是的。
  • @MartijnPieters 只需进行一些测试,如果密钥是[1, 2, 16, 32, 4] 会怎样?这将包括4
【解决方案3】:

我和answer of mine 有同样的感觉:我读过的所有答案似乎都在创建一个新对象。
我更喜欢对列表进行就地修改。

在下面的代码中,我删除了每个列表中不需要的部分(因为列表已排序,这很容易),并且我尊重EADP 编码风格(请求宽恕比许可更容易)

d={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]}

k = 15
for ki,li in d.items():
    try:
        x = next(x for x in li if x>=k)
    except:
        del d[ki]
    else:
        i = li.index(x)
        li[0:i] = []

print d
# {1: [16, 32, 50], 2: [15], 13: [23, 55]}

.

编辑 1

我更改了代码。这不太好,因为我不得不在 d.items() 而不是 d.iteritems() 中进行迭代:在最后一种情况下,在迭代期间无法修改字典。

.

编辑 2

我试过bisect_left(),它确实是最快的解决方案。这是下面的第三个代码。第二个是更正了 RussW 的一个。第一个是我以前的代码

k = 15

te = clock()
for jj in xrange(10000):
    d={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]}
    for ki,li in d.items():
        try:
            x = next(x for x in li if x>=k)
        except:
            del d[ki]
        else:
            i = li.index(x)
            li[0:i] = []
print clock() - te
print d
            
print '------------------------------------------'

d={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]}
te = clock()
for jj in xrange(10000):
    dct={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]}
    for key, lst in dct.items():
        gn = None
        for i, x in enumerate(lst):
            if x >= k:
                gn = i
                break
        if gn is None:
            del dct[key]
        else:
            dct[key] = lst[gn:]
print clock() - te
print dct
print '------------------------------------------'

te = clock()
for jj in xrange(10000):
    d={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]}
    for ki,li in d.items():

    i = bisect_left(li,15)
    if i==len(li):
        del d[ki]
    else:
        li[0:i] = []
print clock() - te
print d

结果

0.22918869577
{1: [16, 32, 50], 2: [15], 13: [23, 55]}
------------------------------------------
0.163871665254
{1: [16, 32, 50], 2: [15], 13: [23, 55]}
------------------------------------------
0.100142057161
{1: [16, 32, 50], 2: [15], 13: [23, 55]}

【讨论】:

  • 谢谢。输出似乎不对。它应该是 {1:[16,32,50],2:[15],13:[23,55]} 。
【解决方案4】:
>>> import bisect
>>> d = {1: [1,6,16], 2: [1], 7: [6]}
>>> for k in d.keys():
...     d[k] = d[k][bisect.bisect_left(d[k], 16):]
...     if not d[k]:
...             del d[k]
...
>>> d
{1: [16]}

【讨论】:

  • 再循环两次;为什么不结合这两个循环(不是dict理解)?
  • 当然,我们可以写像{k:v[bisect_left(v, 16):] for k, v in d.items() if v[bisect_left(v, 16):]} 这样的东西,但是对于 bisect_left 的两次调用,它看起来有点难看(而且它是一个很长的单行)。我认为你是对的,@falsetru 的 bisect 解决方案是正确的。
  • 您现在正在就地修改字典;对于 Python 2,d.keys() 可以正常工作,对于 Python 3,您需要改用 list(d)。否则,终于正确了!
  • 啊,Python 3 玩得还不够,但现在我知道了,所以谢谢。
【解决方案5】:
>>> def sieve(dct, n):
    for key, lst in dct.iteritems():
        gn = None
        for i, x in enumerate(lst):
            if x >= n:
                gn = i
                            break
        if gn is None:
            del dct[key]
        else:
            dct[key] = lst[gn:]


>>> d = {1:[1, 6, 16], 2:[1], 7:[6]}
>>> sieve(d, 15)
>>> d
{1: [16]}
>>> 

【讨论】:

  • 问题是如果 k = 15 你应该得到相同的答案。 k 不必在列表中。
  • 谢谢。现在它只需要对长列表进行 bisect :)
  • 错误代码。它需要if x >= n: 而不是if x > n:breakgn = i 之后。在d={1:[1,6,16,32,50],2:[1,5,15],7:[6,7,9],13:[10,12,23,55]} 上尝试您的代码,它给出了{1: [50], 13: [55]}
  • 无法正常工作。如果你放两条指令print id(dct[key]),一条在dct[key] = lst[gn:]之前,一条在它之后,你得到不同的身份
  • 是的“就地”有点误导。由于切片,只有字典被改变,但列表没有被改变。删除它。也感谢代码更正。
猜你喜欢
  • 2020-07-14
  • 2010-11-17
  • 1970-01-01
  • 2022-11-27
  • 2016-05-06
  • 1970-01-01
  • 2012-02-21
相关资源
最近更新 更多