【问题标题】:Slicing keys of a dictionary to assign data切片字典的键以分配数据
【发布时间】:2021-06-04 05:46:07
【问题描述】:

现在我正在通过以下循环填充字典:

import numpy as np
data = np.random.choice(2, 10)
output = dict()
for i in range(10):
    output[i] = data[i]

本着output[:] = data[:]的精神,还有什么更高效的东西吗?

解决方案:output = dict(zip(range(10), np.random.choice(2, 10)))

跟进: 如果键是元组怎么办?

import numpy as np
data = np.random.choice(2, 10)
output = dict()
for i in range(10):
    output[1, i] = data[i]

跟进-跟进: 我试图将我的实际问题减少到最低限度,但最终没有问我要问的问题。我实际上在做的是:

import numpy as np
output = dict()
for j in range(2):
    for k in range(3):
        data = np.random.choice(2, 10)
        for i in range(10):
            output[j, k, i] = data[i]

我尝试循环使用答案,但弹出名称错误。

【问题讨论】:

  • 你为什么不使用 dict(enumerate(data.flatten(), 1)) 甚至 .flatten() 它然后 zip 它与 np.arange() 并最后将 zip 结构传递给 dict() 对象。跨度>
  • 查看更新的答案以解决您的后续问题...

标签: python dictionary slice


【解决方案1】:

编辑(在看到“follow-follow up”之后):

如果您想要更高维度的元组,根据您的“后续跟进”问题):

def gen_nd(size):
    n = np.product(size)
    data = np.random.choice(2, n).tolist()
    idx = np.unravel_index(np.arange(n, dtype=int), size)
    return dict(zip(list(zip(*idx)), data))

>>> gen_nd((2,3,2))
{(0, 0, 0): 0,
 (0, 0, 1): 0,
 (0, 1, 0): 1,
 (0, 1, 1): 1,
 (0, 2, 0): 0,
 (0, 2, 1): 1,
 (1, 0, 0): 0,
 (1, 0, 1): 0,
 (1, 1, 0): 0,
 (1, 1, 1): 0,
 (1, 2, 0): 0,
 (1, 2, 1): 1}

还有:

%timeit gen_nd((100, 100, 100))
647 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

另外 2 倍加速:

def gen_nd(size):
    m = len(size)
    n = np.product(size)
    data = np.random.choice(2, n).tolist()
    ranges = [np.arange(k) for k in size]
    idx = np.stack(np.meshgrid(*ranges, indexing='ij'), -1).reshape(-1, m).T.tolist()
    return dict(zip(list(zip(*idx)), data))

%timeit gen_nd((100, 100, 100))
297 ms ± 423 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

原答案

简单(可读且快速)怎么样:

data = np.random.choice(2, 10)
output = dict(enumerate(data.tolist()))

对于tuple 键(“后续问题”):

output = {(1, k): v for k, v in enumerate(data.tolist())}

速度

如果您对速度感兴趣,那么这里有一个有趣的基准:

def gen0(n):
    return dict(enumerate(data))

def tupgen0(n):
    data = np.random.choice(2, n)
    {(1,k): v for k, v in enumerate(data)}

def gen1(n):
    data = np.random.choice(2, n)
    return dict(enumerate(data.tolist()))

def tupgen1(n):
    data = np.random.choice(2, n)
    {(1,k): v for k, v in enumerate(data.tolist())}

def gen2(n):
    data = np.stack((np.arange(n, dtype=int), np.random.choice(2, n)))
    return dict(zip(*data.tolist()))

def tupgen2(n):
    data = np.stack((np.ones(n, dtype=int), np.arange(n, dtype=int), np.random.choice(2, n)))
    k, v = data[:2].tolist(), data[-1].tolist()
    return dict(zip(tuple(zip(*k)), v))

def gen3(n):
    return dict(zip(range(n), np.random.choice(2, n)))

def tupgen3(n):
    return dict(zip(((1, i) for i in range(n)), np.random.choice(2, n)))


n = 1_000_000

g0 = %timeit -o gen0(n)
g1 = %timeit -o gen1(n)
g2 = %timeit -o gen2(n)
g3 = %timeit -o gen3(n)

t0 = %timeit -o tupgen0(n)
t1 = %timeit -o tupgen1(n)
t2 = %timeit -o tupgen2(n)
t3 = %timeit -o tupgen3(n)

给予:

202 ms ± 256 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
114 ms ± 37.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
139 ms ± 53.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
203 ms ± 212 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
350 ms ± 721 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
245 ms ± 321 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
305 ms ± 699 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
375 ms ± 2.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

所以gen1()tupgen1() 方法是同类中最快的。

【讨论】:

  • 谢谢,也很优雅。
  • 优秀。是的,速度很重要。
【解决方案2】:

dictzip 参数一起使用:

output = dict(zip(range(10), np.random.choice(2, 10)))

这使得来自range(10) 的每个元素都成为一个键,来自np.random.choice(2, 10) 的元素作为值。

如果你想要一个元组的键,你可以将一个生成器传递给zip,它将“预先创建”可迭代的元组:

output = dict(zip(((1, i) for i in range(10)), np.random.choice(2, 10)))

另一个可读性较差的替代方案是map

output = dict(zip(map(lambda n: (1, n), range(10)), np.random.choice(2, 10)))

【讨论】:

  • 谢谢。我添加了一个后续问题。如果键是元组怎么办?
  • 好的。我现在感觉到了。实际上我自己想出了一个替代方案dict(zip(zip([1 for i in range(10)], range(10)), np.random.choice(2, 10)))
  • @clueless 这效率有点低,因为它实际上创建了一个列表并使用了 2 个 range 对象
  • 但是,我似乎无法避免使用元组的循环。
  • @clueless 不确定您的意思。我的答案显示了 2 个示例,但没有创建列表并使用单个 range 对象
猜你喜欢
  • 2013-03-16
  • 2015-05-26
  • 1970-01-01
  • 2017-03-19
  • 2019-01-23
  • 2022-08-09
  • 2018-12-31
  • 2020-02-08
相关资源
最近更新 更多