Numpy Equivalent of Logic Operation on Multidimensional Arrays in MATLAB - python

I have the following logic operation coded in MATLAB, where [A, B, C, and D] are all 5x3x16 doubles and [a, b, c, and d] are all 240x1 doubles. I am trying to implement the same logic operation in python using numpy.
D = zeros(size(A));
for i = 1:numel(D)
flag = ...
(a == A(i)) & ...
(b == B(i)) & ...
(c == C(i));
D(i) = d(flag);
end
d is a column vector that is already populated with data. a, b, and c are also populated column vectors of equal size. Meshgrid was used to construct A, B, and C into a LxMxN grid of the unique values within a, b, and c. Now I want to use d to populate a LxMxN D with the appropriate values using the boolean expression.
I have tried:
D= np.zeros(np.shape(N))
for i in range(len(D)):
for j in range(len(D[0])):
for k in range(len(D[0][0])):
flag = np.logical_and(
(a == A[i][j][k]),
(b == B[i][j][k]),
(c == C[i][j][k])
)
D[i][j][k] = d[flag];

The syntax will be a little messier, but you can use the np.logical_* functions to do this.

Related

A function works fine with one number, but not an array. What am I missing?

Pardon my ignorance, but I'm very new to coding in python. I have a pretty simple function; it just needs to make a calculation based on b's relative location to a and c:
a = 6
b = 3
c = 2
def function(a, b, c):
if ((a >= b) & (b >= c)):
return b - c
elif ((a <= b) & (b >= c)):
return a - c
else:
return 0
t = function(a, b, c)
print(t)
When I run it with simple numbers like above, it gives me the right answer no matter what I make b. (In this case 1)
But When I run it with a,b, and c as Numpy Arrays, it only returns b - c across the entire "t" array.
It's not too much different, but here's what I'm using for the array version:
def function(a, b, c):
if ((a >= b) & (b >= c)).any():
return b - c
elif ((a <= b) & (b >= c)).any():
return a - c
else:
return 0
t = function(a, b, c[i>1])
print(t)
(The [i>1] is there because there is a variable amount of array input, and another function will be used for when [i = 0])
I've also tried this:
t = np.where(((prev2 >= Head_ELV) & (Head_ELV >= Bottom_ELV)).any, Head_ELV - Bottom_ELV, 0)
but ran into the same result.
Would a while-loop work better?
I don't think you need looping here as the problem can be solved using array operations. You could try the below, assuming the arrays are of the same length.
# import numpy to be able to work with arrays
import numpy as np
def function(a, b, c):
# declare array t with only zeros
t = np.zeros_like(a)
# declare filters
mask_1 = (a >= b) * (b >= c)
mask_2 = (a <= b) * (b >= c)
# modifying t based on the filters above
t[mask_1] = (b - c)[mask_1]
t[mask_2] = (a - c)[mask_2]
return t
# example 2d arrays
a = np.array([[1800,5], [5,5]])
b = np.array([[3416,2], [3,4]])
c = np.array([[1714,2], [3,4]])
# run function
function(a, b, c)

How to iterate over tuples using jax.lax.scan

I am looking to translate a bit of code from a NumPy version listed here, to a JAX compatible version. The NumPy code iteratively calculates the value of a matrix, E from the values of other matrices, A, B, D, as well as the value of E from the previous iteration: E_jm1.
Both the NumPy and JAX version work in their listed forms and produce identical results. How can I get the JAX version to work when passing A, B, D as a tuple instead of as a concatenated array? I have a specific use case where a tuple would be more useful.
I found a question asking something similar, but it just confirmed that this should be possible. There are no examples in the documentation or elsewhere that I could find.
Original NumPy version
import numpy as np
import jax
import jax.numpy as jnp
def BAND_J(A, B, D, E_jm1):
'''
output: E(N x N)
input: A(N x N), B(N x N), D(N x N), E_jm1(N x N)
𝐄ⱼ = -[𝐁 + 𝐀𝐄ⱼ₋₁]⁻¹ 𝐃
'''
B_inv = np.linalg.inv(B + np.dot( A, E_jm1 ))
E = -np.dot(B_inv, D)
return E
key = jax.random.PRNGKey(0)
N = 2
NJ = 4
# initialize matrices with random values
A, B, D = [ jax.random.normal(key, shape=(N,N,NJ)),
jax.random.normal(key, shape=(N,N,NJ)),
jax.random.normal(key, shape=(N,N,NJ)) ]
A_np, B_np, D_np = [np.asarray(A), np.asarray(B), np.asarray(D)]
# initialize E_0
E_0 = jax.random.normal(key+2, shape=(N,N))
E_np = np.empty((N,N,NJ))
E_np[:,:,0] = np.asarray(E_0)
# iteratively calculate E from A, B, D, and 𝐄ⱼ₋₁
for j in range(1,NJ):
E_jm1 = E_np[:,:,j-1]
E_np[:,:,j] = BAND_J(A_np[:,:,j], B_np[:,:,j], D_np[:,:,j], E_jm1)
JAX scan version
def BAND_J(E, ABD):
'''
output: E(N x N)
input: A(N x N), B(N x N), D(N x N), E_jm1(N x N)
'''
A, B, D = ABD
B_inv = jnp.linalg.inv(B + jnp.dot( A, E ))
E = -jnp.dot(B_inv, D)
return E, E # ("carryover", "accumulated")
abd = jnp.asarray([(A[:,:,j], B[:,:,j], D[:,:,j]) for j in range(NJ)])
# abd = tuple([(A[:,:,j], B[:,:,j], D[:,:,j]) for j in range(NJ)]) # this produces error
# ValueError: too many values to unpack (expected 3)
_, E = lax.scan(BAND_J, E_0, abd)
for j in range(1, NJ):
print(np.isclose(E[j-1], E_np[:,:,j]))
The short answer is "you can't". By design, jax.scan can scan over axes of arrays, not entries of arbitrary Python collections.
So if you want to use scan, you'll have to stack your entires into an array.
That said, since your tuple only has three elements, a good alternative would be to skip the scan and simply JIT-compile the for loop approach. JAX tracing will effectively unroll the loop and optimize the flattened sequence of operations. While this can lead to long compile times for large loops, since your application is only 3 iterations it shouldn't be problematic.

How to test all possible values ​for all variables to get the maximum result for the function

I have three variables called a, b and c, each of these can assume a different value defined in a range. I'd like to create a function that tests every possible variable value and gives me their best combination for the output 'f'.
a = list(range(1, 10, 2))
b = list(range(5, 8, 1))
c = list(range(1, 3, 1))
def all_combinations (a, b, c):
#something
f = a + (b * a) - (c*(a ^ b))
return BEST a, b, c for my f
it's possible to do it ? what is the best way to do it?
You can use itertools.product() to get all the possible combinations of a, b, and c.
Then calculate your formula for each unique combination of a b c, keep track of the result, and if the result is better than the previous best, save the current values of a b c.
import itertools
def all_combinations (alist, blist, clist):
best_a = 0
best_b = 0
best_c = 0
best_f = 0
for a,b,c in itertools.product(alist, blist, clist):
f = a + (b * a) - (c*(a ^ b))
if f > best_f: # use your own definition of "better"
best_a = a
best_b = b
best_c = c
best_f = f
return best_a, best_b, best_c
First of all, you said I have three variables called a, b and c, each of these can assume a different value defined in a range. Note that the variables in your code are actually equal to three lists of integers, not three integers.
The naive algorithm to test all possible combinations is 3 nested for loops. Here I assume that by "best" you mean "maximum value":
def all_combinations (list1, list2, list3):
best_f, best_a, best_b, best_c = None, None, None, None
for a in list1:
for b in list2:
for c in list3:
f = a + (b * a) - (c*(a ^ b))
# here you have to define what f being "better" than best_f means:
if not f or f > best_f:
best_f = f
best_a = a
best_b = b
best_c = c
return best_a, best_b, best_c
If you're sure those are the only values you want to test, then the following will work. Otherwise you might want to look into scipy.optimize.
from itertools import product
import numpy as np
parameters = list(product(a, b, c))
results = [my_fun(*x) for x in parameters]
print(parameters[np.argmax(results)])
obviously replace np.argmax with np.argmin if you want to minimize the function

Trouble creating dataframe using pandas: ValueError/data type not understood

I'm a bit new to pandas/numpy and have had trouble with this issue.
I have a group of 10 lists, each of which have 58 elements in them (strings). When I try to join them into a dataframe
df = pd.Dataframe(a, b, c, d, e, f, g, h, i, j)
I get the error "ValueError: Shape of passed values is (1, 58), indices imply (58, 58)"
I started trying different combinations of the lists to check which list was causing the problem (I checked types and len etc.) and then I started getting the error "data type not understood".
I've tried checking similar posts but nothing has been working for me so far. Does anyone have any suggestions for how I can tackle this issue?
Use this:
df = pd.Dataframe([a, b, c, d, e, f, g, h, i, j])
The difference
# You had
df = pd.Dataframe( a, b, c, d, e, f, g, h, i, j )
# ^ \________________________/
# | |
# data argument |
# stuff pandas thought was something else
# New code
df = pd.Dataframe([a, b, c, d, e, f, g, h, i, j])
# \____________________________/
# |
# data argument
The first argument represents the data. According to python, you were passing 10 arguments, only the first, a, was getting interpreted as the data argument. The way I've told you to do it, all those elements are passed within a list [] and it is that list that pandas will take as the data argument, which is what I think you want.

Most efficient way to filter for lowest values in queryset

Given:
some_list = SomeClass.objects.filter(something=something).order_by('-X','-Y')
In English: some_list is a filtered list of SomeClass objects, ordered by two values, first X, then Y.
If I then want to limit the number of entries in the list to some value Z, I can do this:
final_list = some_list[:Z]
But what if I want to limit the number of entries to some value, but randomize the "cut-off entries" before I do? I'm having a hard time describing, so an example:
SC X Y
A 2 3
B 1 2
C 1 1
D 1 1
E 1 1
F 1 0
G 0 3
If Z=3 then, using my method, final_list = (A,B,C). What I want is final_list to include A and B because they are clearly above the others(A has greater X than any others, and B is tied for second most X but has greater Y), but seeing as the cut-off would be at C which is X=1,Y=1, and there are two OTHER objects "tied" with C, the third slot could be C, D, or E.
I could pull some_list apart by hand and examine values and start putting them into final_list until I hit Z, but I was hoping there was a better way I don't know of. As in, less lines of code, less processing power, etc.
So for this example, I would expect one of these outputs, at random:
final_list = (A, B, C)
final_list = (A, B, D)
final_list = (A, B, E)
If Z = 4, I would expect one of these outputs:
final_list = (A, B, C, D)
final_list = (A, B, C, E)
final_list = (A, B, D, C)
final_list = (A, B, D, E)
final_list = (A, B, E, C)
final_list = (A, B, E, D)
Hopefully that is clear.
You could do something simplistic like, annotating your query with the average values of X and Y, and then, picking the values that are above the average. For example, something like:
some_list = SomeClass.objects.filter(
something=something
).annotate(
average_x=Avg("X"), average_y=Avg("Y")
).order_by(
'-X','-Y'
)
def passes_threshold(x, y, avgx, avgy):
'''Simplistic function to check if a combination of X & Y is better than
another. Update to better suit your context.
'''
return (x + y) > (avgx + avgy)
# You could use filter instead, if you want all values from the list matching,
# but if you want just until the first few that match,
required_list = itertools.takewhile(passes_threshold, some_list)

Categories

Resources