Вы можете использовать правила простого вещания для создания декартового произведения.Функция numpy.ix_
создает список соответствующих массивов.Это эквивалентно следующему:
>>> def pseudo_ix_gen(*arrays):
... base_shape = [1 for arr in arrays]
... for dim, arr in enumerate(arrays):
... shape = base_shape[:]
... shape[dim] = len(arr)
... yield numpy.array(arr).reshape(shape)
...
>>> def pseudo_ix_(*arrays):
... return list(pseudo_ix_gen(*arrays))
Или, более кратко:
>>> def pseudo_ix_(*arrays):
... shapes = numpy.diagflat([len(a) - 1 for a in arrays]) + 1
... return [numpy.array(a).reshape(s) for a, s in zip(arrays, shapes)]
Результатом является список передаваемых массивов:
>>> numpy.ix_(*[[2, 4], [1, 3], [0, 2]])
[array([[[2]],
[[4]]]), array([[[1],
[3]]]), array([[[0, 2]]])]
Сравните это срезультат numpy.ogrid
:
>>> numpy.ogrid[0:2, 0:2, 0:2]
[array([[[0]],
[[1]]]), array([[[0],
[1]]]), array([[[0, 1]]])]
Как видите, он тот же, но numpy.ix_
позволяет использовать непоследовательные индексы.Теперь, когда мы применяем правила простого вещания, мы получаем декартово произведение:
>>> list(numpy.broadcast(*numpy.ix_(*[[2, 4], [1, 3], [0, 2]])))
[(2, 1, 0), (2, 1, 2), (2, 3, 0), (2, 3, 2),
(4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)]
Если вместо передачи результата numpy.ix_
в numpy.broadcast
мы используем его для индексации массива, мы получаемэто:
>>> a = numpy.arange(6 ** 4).reshape((6, 6, 6, 6))
>>> a[numpy.ix_(*[[2, 4], [1, 3], [0, 2]])]
array([[[[468, 469, 470, 471, 472, 473],
[480, 481, 482, 483, 484, 485]],
[[540, 541, 542, 543, 544, 545],
[552, 553, 554, 555, 556, 557]]],
[[[900, 901, 902, 903, 904, 905],
[912, 913, 914, 915, 916, 917]],
[[972, 973, 974, 975, 976, 977],
[984, 985, 986, 987, 988, 989]]]])
Однако caveat emptor .Широковещательные массивы полезны для индексации, но если вы буквально хотите перечислить значений, вам может быть лучше использовать itertools.product
:
>>> %timeit list(itertools.product(range(5), repeat=5))
10000 loops, best of 3: 196 us per loop
>>> %timeit list(numpy.broadcast(*numpy.ix_(*([range(5)] * 5))))
100 loops, best of 3: 2.74 ms per loop
Так что если вы включаете дляцикл в любом случае, тогда itertools.product
, скорее всего, будет быстрее.Тем не менее, вы можете использовать описанные выше методы, чтобы получить некоторые похожие структуры данных в чистом виде:
>> pgrid_idx = numpy.ix_(*[[2, 4], [1, 3], [0, 2]])
>>> sub_indices = numpy.rec.fromarrays(numpy.indices((6, 6, 6)))
>>> a[pgrid_idx].reshape((8, 6))
array([[468, 469, 470, 471, 472, 473],
[480, 481, 482, 483, 484, 485],
[540, 541, 542, 543, 544, 545],
[552, 553, 554, 555, 556, 557],
[900, 901, 902, 903, 904, 905],
[912, 913, 914, 915, 916, 917],
[972, 973, 974, 975, 976, 977],
[984, 985, 986, 987, 988, 989]])
>>> sub_indices[pgrid_idx].reshape((8,))
rec.array([(2, 1, 0), (2, 1, 2), (2, 3, 0), (2, 3, 2),
(4, 1, 0), (4, 1, 2), (4, 3, 0), (4, 3, 2)],
dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8')])