切片字典的键来分配数据

Slicing keys of a dictionary to assign data

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

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 with a zip参数:

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)))

编辑(看到“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() 在它们的 class 中是最快的。