Skip to content

Commit fb29be6

Browse files
committed
added Product class (from larray_editor) to make product for Sequences
similar to itertools.product but can be indexed and has a length
1 parent 3ed417c commit fb29be6

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

larray/util/misc.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,70 @@ def __iter__(self):
833833

834834
def __repr__(self):
835835
return 'SequenceZip({})'.format(self.sequences)
836+
837+
838+
# TODO: remove Product from larray_editor.utils (it is almost identical)
839+
class Product(object):
840+
"""
841+
Represents the `cartesian product` of several sequences.
842+
843+
This is very similar to itertools.product but only accepts sequences and acts as a sequence (it can be
844+
indexed and has a len).
845+
846+
Parameters
847+
----------
848+
sequences : Iterable of Sequence
849+
Sequences on which to apply the cartesian product.
850+
851+
Examples
852+
--------
853+
>>> p = Product([['a', 'b', 'c'], [1, 2]])
854+
>>> for i in range(len(p)):
855+
... print(p[i])
856+
('a', 1)
857+
('a', 2)
858+
('b', 1)
859+
('b', 2)
860+
('c', 1)
861+
('c', 2)
862+
>>> p[1:4]
863+
[('a', 2), ('b', 1), ('b', 2)]
864+
>>> p[-3:]
865+
[('b', 2), ('c', 1), ('c', 2)]
866+
>>> list(p)
867+
[('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1), ('c', 2)]
868+
"""
869+
def __init__(self, sequences):
870+
self.sequences = sequences
871+
assert len(sequences)
872+
shape = [len(a) for a in self.sequences]
873+
self._div_mod = [(int(np.prod(shape[i + 1:])), shape[i])
874+
for i in range(len(shape))]
875+
self._length = np.prod(shape)
876+
877+
def __len__(self):
878+
return self._length
879+
880+
def __getitem__(self, key):
881+
if isinstance(key, (int, np.integer)):
882+
if key >= self._length:
883+
raise IndexError("index %d out of range for Product of length %d" % (key, self._length))
884+
# this is similar to np.unravel_index but a tad faster for scalars
885+
return tuple(array[key // div % mod]
886+
for array, (div, mod) in zip(self.sequences, self._div_mod))
887+
else:
888+
assert isinstance(key, slice), "key (%s) has invalid type (%s)" % (key, type(key))
889+
start, stop, step = key.indices(self._length)
890+
div_mod = self._div_mod
891+
arrays = self.sequences
892+
# XXX: we probably want to return another Product object with an updated start/stop to stay
893+
# lazy in that case too.
894+
return [tuple(array[idx // div % mod]
895+
for array, (div, mod) in zip(arrays, div_mod))
896+
for idx in range(start, stop, step)]
897+
898+
def __iter__(self):
899+
return product(*self.sequences)
900+
901+
def __repr__(self):
902+
return 'Product({})'.format(self.sequences)

0 commit comments

Comments
 (0)