multiple for statements in any() python - python

So, I have four lists. Two hold x coordinates (foodX and newPosXArray) and the other two hold y coordinates (foodX and newPosYArray). Both the food and the newPos arrays are of different dimensions because I have multiple "food sources", and multiple objects searching for the food.
I want to write an if statement that does something when the objects get to within a certain distance of the food.
My attempt with any()
if any(np.sqrt((newPosXArray[u]-foodX[t])**2 + (newPosYArray[u]-foodY[t])**2) <= 0.2 for t in zip(food[0], food[1]) for u in zip(newPosXArray, newPosYArray)):
#dosomething
I am getting an error TypeError: list indices must be integers, not tuple
Edit:
maybe I am misunderstanding zip(). I was assuming that it condenses this
if any(np.sqrt((newPosXArray[u]-foodX[t])**2 + (newPosYArray[u]-foodY[t])**2) <= 0.2 for t in foodX for t in foodY for u in newPosXArray for u in newPosYArray):
Typical Values of what I am working with
foodX = [5,5,-5,-5]
foodY = [5,-5,5,-5]
In [65]: newPosXArray
Out[65]:
[-0.012880860643600167,
-0.566815786730638,
0.7905336304903405,
0.09006991095474826,
0.26518652615441063,
0.3161232055076695,
0.681255361368023,
-0.6849985596071202,
0.7140832628874829,
0.4958515031060564]
In [66]: newPosYArray
Out[66]:
[-0.41112817779983235,
-0.08554837651693648,
0.8743935617169996,
-0.9384733737088207,
0.02423386678116546,
-0.3735855691077572,
-0.5251118585489394,
0.3950871276165102,
0.9892320167752822,
-0.7342372054958279]
of course, none of these values will return true in the if statement because none are within a 0.2 radius of any of the food coordinates

Just to be clear nested loops and zip do not do the same thing:
>>> [(i, j) for i in range(3) for j in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Whereas:
>>> list(zip(range(3), range(3)))
[(0, 0), (1, 1), (2, 2)]
itertools.product does the same as next for loops:
>>> import itertools
>>> list(itertools.product(range(3), range(3)))
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
You don't index into the array when you iterate over an array, e.g:
>>> food = [1,2,3,4]
>>> [f+1 for f in food] # Note you did [food[f] for f in food]
[2,3,4,5]
So fixing your second example looks like:
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for t_x in foodX for t_y in foodY for u_x in newPosXArray for u_y in newPosYArray):
But this iterates foodY for every foodX so assume you do want these to be zipped which would be written:
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for t_x, t_y in zip(foodX, foodY) for u_x, u_y in zip(newPosXArray, newPosYArray)):
Or using itertools.product (which personally I don't find any easier to follow in this instance:
import itertools
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for (t_x, t_y), (u_x, u_y) in itertools.product(zip(foodX, foodY), zip(newPosXArray, newPosYArray))):

You try to get food[][z] but z is tuple (from zip method ), perhaps you meant to do
food[0][z[0]]

Of course, Pythonic way is to just use complex numbers instead of those 2D data separated along the wrong axis. But if you really need to use that data format, I guess
any(math.hypot(X-x,Y-y)<.2 for X,Y in zip(foodX,foodY) for x,y in zip(posX,posY))
will do what you want.

The problem is that u and t are tuples because zip places x and y side by side in tuple format. You need to say food[0][t[1]]andfood[1][t[0]] where you intend to use the list values as coordinates.
From what it sees, you are trying to give it an x,(x,y) coordinate instead of an x,y coordinate.
This holds where you are using u as a list index as well.
if any(np.sqrt((u[0] - t[0]) ** 2 + (u[1] - t[1]) ** 2) <= 0.2 for t in zip(foodX,foodY) for u in zip(newPosXArray,newPosYArray))
From values given it evaluates to False until the 0.2 value gets to 5, which sounds weird given how low your initial value was, but that's how the distance formula works.
Alternatively for readability you could change u to new and t to current.

Related

Multiple index loop in Python

I need to make several nested loops like this, but with a generic number of looping indices:
for ii in range(0,num):
for iii in range(0,num):
for iiii in range(0,num):
for iiiii in range(0,num):
Is it there any compact or practical way to do this? Thanks in advance.
Use itertools.product to generate tuples of the desired indices. For example:
import itertools
for indices in intertools.product(range(num), repeat=depth):
print(indices)
This will generate tuples of length depth of the values.
Here's a small example:
>>> for indices in itertools.product(range(3), repeat=2):
... print(indices)
...
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(2, 2)
>>>
In this example, the number of values present in each tuple is 2, given by repeat=2.

Generate all the possible values using info from random binary tuples (Python)

Here's part of the code I'm working on using Python:
import random
pairs = [
(0, 1),
(1, 2),
(2, 3),
(3, 0),
]
alphasori = [(random.choice([1, -1]) * random.uniform(5, 15), pairs[n]) for n in range(4)]
binum = np.random.randint(2, size=4).tolist()
d = dict(zip([0,1,2,3], binum))
alpbi = [(i, tuple(d[j] for j in c)) for i, c in alphasori]
print(alpbi)
And this is a sample output (we can call this list as alpbi):
[(-6.16111614207135, (1, 1)), (-9.39824028732309, (1, 1)), (12.1294338553467, (1, 0)), (8.192565262190904, (0, 1))]
I'm now trying to calculate the linear combination (call that S) of the random numbers (the first terms) inside each tuple in alpbi, (-6.16111614207135,-9.39824028732309...), which is followed by the following rules:
if the inner tuple is (1,1) or (0,0), then the random number is multiplied by (-1)
if the inner tuple is (0,1) or (1,0), then keep the original number.
From 1&2, We're calculating the linear combinations S of those random numbers.
For example, for the random sample generated above, we have
S = (-1)(-6.16111614207135)+ (-1)(-9.39824028732309) +12.1294338553467+8.192565262190904 = 35.8813
Here's the code I have to figure out a single case:
S = 0
for i in range (len(alpbi)):
if alpbi[i][1][0] == alpbi[i][1][1]:
S += (-1)*alpbi[i][0]
else:
S += alpbi[i][0]
print(S)
However, given that '1's and '0's in the inner tuple are random binary numbers, how can I calculate all the possible values of S? (There're 16 combinations, and 8 distinct values in total, I'm wondering is there a way I can write a function to return all the possible values of S at the same time? (like a list containing all of them))
Thanks a lot for reading my question, I really appreciate the help:)
The answer should be the following:
from itertools import product
def all_combinations(numbers):
linear_combinations = product([-1, 1], repeat=len(numbers))
result = [sum([a * b for a, b in zip(numbers, factors)]) for factors in linear_combinations]
return result
alpbi = [(-6.16111614207135, (1, 1)), (-9.39824028732309, (1, 1)), (12.1294338553467, (1, 0)), (8.192565262190904, (0, 1))]
numbers = [item[0] for item in alpbi]
combinations = all_combinations(numbers)
However, there are indeed 16 combinations. I assume that when you say there are just 8 distinct value, you mean ignoring the negative pair to the positive one?
In that case you can just filter all negative numbers:
combinations = [num for num in combinations if num >= 0]

numpy / python - indexing by arrays with duplicates

I'm trying to make a 3D histogram. Initially h = zeros((6,6,8)).
I'll explain my problem with an example. Suppose I have 3 lists of coordinates for h, each list for one dimension:
x = array([2,1,0,1,2,2])
y = array([1,3,0,3,2,1])
z = array([6,2,0,2,5,6]) (the coordinates (x[0],y[0],z[0]) and (x[6],y[6],z[6]) are duplicates, and (x[1],y[1],z[1]) and (x[3],y[3],z[3]) also are)
and also a list of corresponding quantities to accumulate into h:
q = array([1,2,5,9,8,7])
I tried and h[x,y,z] += q does not work because only q[5] = 7 is added to h[2,1,6] and q[0] = 1 is not.
How can I work around this? Thank you.
IIUC, you want np.add.at. To quote the docs: "For addition ufunc, this method is equivalent to a[indices] += b, except that results are accumulated for elements that are indexed more than once."
For example:
>>> np.add.at(h, [x,y,z], q)
>>> for i, val in np.ndenumerate(h):
... if val: print(i, val)
...
((0, 0, 0), 5.0)
((1, 3, 2), 11.0)
((2, 1, 6), 8.0)
((2, 2, 5), 8.0)

generating binary numbers of size n as tuples : itertools.product(*[(0, 1)] * n)

I just found this instruction
itertools.product(*[(0, 1)] * n)
posted by PAG.
Can someone explain how it works?
I am trying to find a way of doing permutations without repetition of n tuples in 3 bags
and I only can use itertools if I want. Thanks
[(0, 1)] is a list of a single tuple of the numbers 0 and 1.
[(0, 1)] * n duplicates the tuple inside of the list, so we get
[(0, 1), (0, 1), ..., (0, 1), (0, 1)]
Then, if we look at the itertools.product function, we want to pass in each of those tuples as single arguments. So, we use the *-operator to unpack our list into arguments to the itertools.product function. So, our function is equivalent to:
itertools.product((0, 1), (0, 1), ..., (0, 1), (0, 1))
which computes all permutations of the n 0s and 1s.
Note that itertools.product takes a repeat parameter, which should be used to do this sort of thing:
itertools.product((0, 1), repeat=n)
To do permutations, you can use the itertools.permutations function:
def pick_into_three_bags(n):
return itertools.permutations(range(n), 3)

Return a sequence of a variable length whose summation is equal to a given integer

In the form f(x,y,z) where x is a given integer sum, y is the minimum length of the sequence, and z is the maximum length of the sequence. But for now let's pretend we're dealing with a sequence of a fixed length, because it will take me a long time to write the question otherwise.
So our function is f(x,r) where x is a given integer sum and r is the length of a sequence in the list of possible sequences.
For x = 10, and r = 2, these are the possible combinations:
1 + 9
2 + 8
3 + 7
4 + 6
5 + 5
Let's store that in Python as a list of pairs:
[(1,9), (2,8), (3,7), (4,6), (5,5)]
So usage looks like:
>>> f(10,2)
[(1,9), (2,8), (3,7), (4,6), (5,5)]
Back to the original question, where a sequence is return for each length in the range (y,x). I the form f(x,y,z), defined earlier, and leaving out sequences of length 1 (where y-z == 0), this would look like:
>>> f(10,1,3)
[{1: [(1,9), (2,8), (3,7), (4,6), (5,5)],
2: [(1,1,8), (1,2,7), (1,3,6) ... (2,4,4) ...],
3: [(1,1,1,7) ...]}]
So the output is a list of dictionaries where the value is a list of pairs. Not exactly optimal.
So my questions are:
Is there a library that handles this already?
If not, can someone help me write both of the functions I mentioned? (fixed sequence length first)?
Because of the huge gaps in my knowledge of fairly trivial math, could you ignore my approach to integer storage and use whatever structure the makes the most sense?
Sorry about all of these arithmetic questions today. Thanks!
The itertools module will definately be helpful as we're dealing with premutations - however, this looks suspiciously like a homework task...
Edit: Looks like fun though, so I'll do an attempt.
Edit 2: This what you want?
from itertools import combinations_with_replacement
from pprint import pprint
f = lambda target_sum, length: [sequence for sequence in combinations_with_replacement(range(1, target_sum+1), length) if sum(sequence) == target_sum]
def f2(target_sum, min_length, max_length):
sequences = {}
for length in range(min_length, max_length + 1):
sequence = f(target_sum, length)
if len(sequence):
sequences[length] = sequence
return sequences
if __name__ == "__main__":
print("f(10,2):")
print(f(10,2))
print()
print("f(10,1,3)")
pprint(f2(10,1,3))
Output:
f(10,2):
[(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)]
f(10,1,3)
{1: [(10,)],
2: [(1, 9), (2, 8), (3, 7), (4, 6), (5, 5)],
3: [(1, 1, 8),
(1, 2, 7),
(1, 3, 6),
(1, 4, 5),
(2, 2, 6),
(2, 3, 5),
(2, 4, 4),
(3, 3, 4)]}
The problem is known as Integer Partitions, and has been widely studied.
Here you can find a paper comparing the performance of several algorithms (and proposing a particular one), but there are a lot of references all over the Net.
I just wrote a recursive generator function, you should figure out how to get a list out of it yourself...
def f(x,y):
if y == 1:
yield (x, )
elif y > 1:
for head in range(1, x-y+2):
for tail in f(x-head, y-1):
yield tuple([head] + list(tail))
def f2(x,y,z):
for u in range(y, z+1):
for v in f(x, u):
yield v
EDIT: I just see it is not exactly what you wanted, my version also generates duplicates where only the ordering differs. But you can simply filter them out by ordering all results and check for duplicate tuples.

Categories