How to unpack a tuple for looping without being dimension specific - python

I'd like to do something like this:
if dim==2:
a,b=grid_shape
for i in range(a):
for j in range(b):
A[i,j] = ...things...
where dim is simply the number of elements in my tuple grid_shape. A is a numpy array of dimension dim.
Is there a way to do it without being dimension specific?
Without having to write ugly code like
if dim==2:
a,b=grid_shape
for i in range(a):
for j in range(b):
A[i,j] = ...things...
if dim==3:
a,b,c=grid_shape
for i in range(a):
for j in range(b):
for k in range(c):
A[i,j,k] = ...things...

Using itertools, you can do it like this:
for index in itertools.product(*(range(x) for x in grid_shape)):
A[index] = ...things...
This relies on a couple of tricks. First, itertools.product() is a function which generates tuples from iterables.
for i in range(a):
for j in range(b):
index = i,j
do_something_with(index)
can be reduced to
for index in itertools.product(range(a),range(b)):
do_something_with(index)
This works for any number of arguments to itertools.product(), so you can effectively create nested loops of arbitrary depth.
The other trick is to convert your grid shape into the arguments for itertools.product:
(range(x) for x in grid_shape)
is equivalent to
(range(grid_shape[0]),range(grid_shape[1]),...)
That is, it is a tuple of ranges for each grid_shape dimension. Using * then expands this into the arguments.
itertools.product(*(range(x1),range(x2),...))
is equivalent to
itertools.product(range(x1),range(x2),...)
Also, since A[i,j,k] is equivalent to A[(i,j,k)], we can just use A[index] directly.
As DSM points out, since you are using numpy, you can reduce
itertools.product(*(for range(x) for x in grid_shape))
to
numpy.ndindex(grid_shape)
So the final loop becomes
for index in numpy.ndindex(grid_shape):
A[index] = ...things...

You can catch the rest of the tuple by putting a star in front of the last variable and make a an array by putting parentheses around it.
>>> tupl = ((1, 2), 3, 4, 5, 6)
>>> a, *b = tupl
>>> a
(1, 2)
>>> b
[3, 4, 5, 6]
>>>
And then you can loop through b. So it would look something like
a,*b=grid_shape
for i in a:
for j in range(i):
for k in b:
for l in range(k):
A[j, l] = ...things...

Related

Generate Unique Pairs From Two Lists Without Itertools

I am looping over a list twice and want to catch all unique pairs, rather than all combinations- ie. order in the pair doesn't matter
listy=[0,1,2]
out=[]
for i in listy:
for j in listy:
out.append([i,j])
The output I'm getting is [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]
What I am looking for is [[0,0],[0,1],[0,2],[1,1],[1,2],[2,2]]
One possible solution is,
listy=[0,1,2]
out=[]
for i in listy:
for j in listy:
pair=set([i,j])
if pair not in out:
out.append(pair)
This produces [{0},{0,1},{0,2},{1},{1,2},{2}]
However this creates inefficiency in what is a heavy script (the lists are long) and I also do not want a list of sets. I want a list of lists.
Is there a better way to achieve this without using itertools (I want an implementation I can also apply to javascript without too much rethinking)
I don't know javascript at all, but if there is something analogous to list comprehension I would try this:
listy = [0,1,2]
pairs = [[i, j] for i in listy for j in listy if i <= j]
The content of pairs is exactly as you wanted:
[[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
Option 1 - "minor fix"
A trivial "fix" would be:
listy=[0,1,2]
out=set()
for i in listy:
for j in listy:
if (i, j) not in out and (j, i) not in out:
out.add((i,j))
The result is:
{(0, 1), (1, 2), (0, 0), (1, 1), (0, 2), (2, 2)}
However this is not an efficient implementation, because we have to check twice if an element is in the list.
Option 2 - More efficient implementation
You could achieve your goal using a trivial scan of the array:
listy=[0,1,2]
out = [(i, j) for i in range(len(listy)) for j in range(i, len(listy))]
NOTE: I use tuples for the pairs, you could easily change that into a list of lists using:
out = [[i, j] for i in range(len(listy)) for j in range(i, len(listy))]
The most straightforward translation of itertools.combinations_with_replacement(listy, 2) (which matches the behavior you desire) that directly generates the results you want from an input list is:
listy = [0,1,2]
out = []
for idx, i in enumerate(listy):
for j in listy[idx:]:
out.append([i, j])
Only change is the use of enumerate (to get the current index being iterated as you iterate) and slicing the listy used in the inner loop (so it starts at the same index as the current run of the outer loop).
This gets the exact result requested with minimal overhead (it does make shallow copies of the list, of decreasing size, once for each inner loop, but this is fairly quick in Python; unless the list is huge, it should be a pretty minimal cost). If you need to avoid slicing, you can make the inner loop an index-based loop with indexing (but in practice, the overhead of indexing is high enough that it'll often lose to the slicing):
listy = [0,1,2]
out = []
for idx, i in enumerate(listy):
for idxj in range(idx, len(listy)):
out.append([i, j[idxj]])

Python | Give list [1,2,3,4] -> return 1*2+2*3+3*4

So as i said in the title, i want to multipy each two neigbors in the list and sum them all - with a single code line.
I'm looking for the most elegant and efficient method, without using extra memory or such.
Here's what I do have now:
import numpy as np
G = lambda array: sum(np.multiply(array[:-1], array[1:])))
That works, but when i write array[:-1] and array[1:], doesn't it create two new arrays? If so, is there a way to do that with the same original array?
Or maybe you can come up with a nicer way to do that :)
The actual numpy way
array = np.array([1, 2, 3, 4])
array[1:] # array[:-1]
Out[]: 20
# is the dot-product operator, which is what that multiply-sum operation is called normally.
If you're wedded to the lambda, it would be:
G = lambda array: array[1:] # array[:-1]
Try the following:
lst = [1,2,3,4]
func = lambda lst: sum([lst[i]*lst[i+1] for i in range(len(lst)-1)])
func(lst)
If you're not constrained by the lambda you can:
def sum_with_neighbours(arr: list[int]) -> int:
sum_ = 0
for i in range(len(arr) - 1): # -1 because we want to go until second-last item
sum_ += arr[i] * arr[i + 1]
return sum_
itertools.pairwise may be the most pythonic choice:
>>> from itertools import pairwise
>>> [*pairwise(range(5))]
[(0, 1), (1, 2), (2, 3), (3, 4)]
>>> sum(i * j for i, j in pairwise(range(5)))
20

How to enumerate all elements of ND-array without hardcoding number of dimensions?

If I know the number of dimensions, say, 3, I an hardcode it with 3 nested loops:
for i in range(A.shape[0]):
for j in range(A.shape[1]):
for k in range(A.shape[2]):
A[i,j,k] = some_formula(i, j, k)
But what if I don't know the number of dimensions? Can I still enumerate array with knowing all indices on each loop?
If your function broadcasts, you can use numpy.fromfunction:
B = numpy.fromfunction(some_formula, A.shape, dtype=int)
If your function doesn't broadcast, you can use numpy.vectorize and numpy.fromfunction, but it'll be a lot less efficient than if your function broadcasted naturally:
B = numpy.fromfunction(numpy.vectorize(some_formula), A.shape, dtype=int)

python set matrix add operation

I have a input matrix looks like this
grid = [[1,1,2],[1,2,3],[3,2,4]]
I am using the following code to construct a matrix of sets.
m,n = len(grid), len(grid[0])
valuesets = [[set()]*n for _ in range(m)]
for j in range(n):
s = sum(grid[0][:j+1])
valuesets[0][j].add(s)
print valuesets[0][0]
The output gives me
set([1])
set([1, 2])
set([1, 2, 4])
I am wondering why valuesets[0][0] is being updated each time in the for loop but not stays the same value set([1]) like I expected? Thanks.
The reason is [set()] * n creates a list by copying the same set() instance.
If you want to get a list of distinct set objects, use [set() for _ in range(n)] instead.

Python: Loops for simultaneous operation, Two or possibly more?

This question closely relates to How do I run two python loops concurrently?
I'll put it in a clearer manner:
I get what the questioner asks in the above link, something like
for i in [1,2,3], j in [3,2,1]:
print i,j
cmp(i,j) #do_something(i,j)
But
L1: for i in [1,2,3] and j in [3,2,1]:
doesnt work
Q1.
but this was amusing what happened here:
for i in [1,2,3], j in [3,2,1]:
print i,j
[1, 2, 3] 0
False 0
Q2. How do I make something like L1 work?
Not Multithreading or parallelism really. (It's two concurrent tasks not a loop inside a loop) and then compare the result of the two.
Here the lists were numbers. My case is not numbers:
for i in f_iterate1() and j in f_iterate2():
UPDATE: abarnert below was right, I had j defined somewhere. So now it is:
>>> for i in [1,2,3], j in [3,2,1]:
print i,j
Traceback (most recent call last):
File "<pyshell#142>", line 1, in <module>
for i in [1,2,3], j in [3,2,1]:
NameError: name 'j' is not defined
And I am not looking to zip two iteration functions! But process them simultaneously in a for loop like situation. and the question still remains how can it be achieved in python.
UPDATE #2: Solved for same length lists
>>> def a(num):
for x in num:
yield x
>>> n1=[1,2,3,4]
>>> n2=[3,4,5,6]
>>> x1=a(n1)
>>> x2=a(n2)
>>> for i,j in zip(x1,x2):
print i,j
1 3
2 4
3 5
4 6
>>>
[Solved]
Q3. What if n3=[3,4,5,6,7,8,78,34] which is greater than both n1,n2.
zip wont work here.something like izip_longest?
izip_longest works good enough.
It's hard to understand what you're asking, but I think you just want zip:
for i, j in zip([1,2,3], [3,2,1]):
print i, j
for i, j in zip(f_iterate1(), f_iterate2()):
print i, j
And so on…
This doesn't do anything concurrently as the term is normally used, it just does one thing at a time, but that one thing is "iterate over two sequences in lock-step".
Note that this extends in the obvious way to three or more lists:
for i, j, k in zip([1,2,3], [3,2,1], [13, 22, 31]):
print i, j, k
(If you don't even know how many lists you have, see the comments.)
In case you're wondering what's going on with this:
for i in [1,2,3], j in [3,2,1]:
print i,j
Try this:
print [1,2,3], j in [3,2,1]
If you've already defined j somewhere, it will print either [1, 2, 3] False or [1, 2, 3] True. Otherwise, you'll get a NameError. That's because you're just creating a tuple of two values, the first being the list [1,2,3], and the second being the result of the expression j in [3,2,1].
So:
j=0
for i in [1,2,3], j in [3,2 1]:
print i, j
… is equivalent to:
j=0
for i in ([1,2,3], False):
print i, 0
… which will print:
[1, 2, 3] 0
False 0
You want to use the zip() function:
for i, j in zip([1, 2, 3], [3, 2, 1]):
#
for i, j in zip(f_iterate1(), f_iterate2()):
#
zip() pairs up the elements of the input lists, letting you process them together.
If your inputs are large or are iterators, use future_builtins.zip(), or, if you don't care about forward compatibility with Python 3, use itertools.izip() instead; these yield pairs on demand instead of creating a whole output list in one go:
from future_builtins import zip
for i, j in zip(f_iterate1(), f_iterate2()):
Your generators fall in this scenario.
Last but not least, if your input lists have different lengths, zip() stops when the shortest list is exhausted. If you want to continue with the longest list instead, use itertools.izip_longest(); it'll use a fill value when the shorter input sequence(s) are exhausted:
>>> for i, j, k in izip_longest(range(3), range(3, 5), range(5, 10), fillvalue=42):
... print i, j, k
...
0 3 5
1 4 6
2 42 7
42 42 8
42 42 9
The default for fillvalue is None.
Your attempt:
for i in [1,2,3], j in [3,2,1]:
is really interpreted as:
for i in ([1,2,3], j in [3,2,1]):
where the latter part is interpreted as a tuple with two values, one a list, the other a boolean; after testing j in [3,2,1], is either True or False. You had j defined as 0 from a previous loop experiment and thus 0 in [3, 2, 1] is False.
For same-length arrays, you can use the index to refer to corresponding locations in respective lists, like so:
a = [1, 2, 3, 4, 5]
b = [2, 4, 6, 8, 10]
for i in range(len(a)):
print(a[i])
print(b[i])
This accesses same indices of both lists at the same time.

Categories