web-dev-qa-db-fra.com

Existe-t-il un Python équivalent de range (n) pour les gammes multidimensionnelles?

Sur Python, range (3) renverra [0,1,2]. Existe-t-il un équivalent pour les plages multidimensionnelles?

range((3,2)) # [(0,0),(0,1),(1,0),(1,1),(2,0),(2,1)]

Ainsi, par exemple, boucler si les tuiles d'une zone rectangulaire sur un jeu basé sur les tuiles pourrait s'écrire:

for x,y in range((3,2)):

Notez que je ne demande pas d'implémentation. Je voudrais savoir s'il s'agit d'un modèle reconnu et s'il existe une fonction intégrée sur Python ou ses bibliothèques standard/communes.

50
MaiaVictor

En numpy, c'est numpy.ndindex . Jetez également un œil à numpy.ndenumerate .

Par exemple.

import numpy as np
for x, y in np.ndindex((3,2)):
    print x, y

Cela donne:

0 0
0 1
1 0
1 1
2 0
2 1
62
Joe Kington

Vous pouvez utiliser itertools.product():

>>> import itertools
>>> for (i,j,k) in itertools.product(xrange(3),xrange(3),xrange(3)):
...     print i,j,k

Les multiples instructions xrange() répétées peuvent être exprimées de cette façon, si vous souhaitez mettre cela à l'échelle en une boucle à dix dimensions ou quelque chose de ridicule de la même manière:

>>> for combination in itertools.product( xrange(3), repeat=10 ):
...     print combination

Qui boucle sur dix variables, variant de (0,0,0,0,0,0,0,0,0,0) À (2,2,2,2,2,2,2,2,2,2).


En général, itertools est un module incroyablement génial. De la même manière que les expressions rationnelles sont beaucoup plus expressives que les méthodes de chaîne "simples", itertools est une manière très élégante d'exprimer des boucles complexes. Vous vous devez de lire la documentation du module itertools. Cela vous rendra la vie plus amusante.

37
Li-aung Yip

Il existe en fait une syntaxe simple pour cela. Il vous suffit d'avoir deux fors:

>>> [(x,y) for x in range(3) for y in range(2)]
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
22
dhg

C'est donc le produit cartésien de deux listes donc:

import itertools
for element in itertools.product(range(3),range(2)):
    print element

donne cette sortie:

(0, 0)
(0, 1)
(1, 0)
(1, 1)
(2, 0)
(2, 1)
6
0x90

Vous pouvez utiliser product à partir du module itertools.

itertools.product(range(3), range(2))
3
Praveen Gollakota

Je voudrais jeter un œil à numpy.meshgrid:

http://docs.scipy.org/doc/numpy-1.6.0/reference/generated/numpy.meshgrid.html

qui vous donnera les valeurs de grille X et Y à chaque position dans un maillage/grille. Ensuite, vous pourriez faire quelque chose comme:

import numpy as np
X,Y = np.meshgrid(xrange(3),xrange(2))
Zip(X.ravel(),Y.ravel()) 
#[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1)]

ou

Zip(X.ravel(order='F'),Y.ravel(order='F')) 
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
3
JoshAdel

ndindex() de Numpy fonctionne pour l'exemple que vous avez donné, mais il ne sert pas tous les cas d'utilisation. Contrairement à range() intégré à Python, qui permet à la fois un start, stop et step arbitraire, np.ndindex() de numpy accepte uniquement un stop. (Le start est présumé être (0,0,...), Et le step est (1,1,...).)

Voici une implémentation qui agit davantage comme la fonction intégrée range(). Autrement dit, il autorise les arguments arbitraires start/stop/step, mais il fonctionne sur tuples au lieu de simples entiers.

import sys
from itertools import product, starmap

# Python 2/3 compatibility
if sys.version_info.major < 3:
    from itertools import izip
else:
    izip = Zip
    xrange = range

def ndrange(start, stop=None, step=None):
    if stop is None:
        stop = start
        start = (0,)*len(stop)

    if step is None:
        step = (1,)*len(stop)

    assert len(start) == len(stop) == len(step)

    for index in product(*starmap(xrange, izip(start, stop, step))):
        yield index

Exemple:

In [7]: for index in ndrange((1,2,3), (10,20,30), step=(5,10,15)):
   ...:     print(index)
   ...:
(1, 2, 3)
(1, 2, 18)
(1, 12, 3)
(1, 12, 18)
(6, 2, 3)
(6, 2, 18)
(6, 12, 3)
(6, 12, 18)
1
Stuart Berg