Function that acts on all elements of numpy array? - python

I wonder if you can define a function to act on all elements of a 1-D numpy array simultaneously, so that you don't have to loop over the array. Similar to the way you can, for example, square all elements of an array without looping. An example of what I'm after is to replace this code:
A = np.array([ [1,4,2], [5,1,8], [2,9,5], [3,6,6] ])
B = []
for i in A:
B.append( i[0] + i[1] - i[2] )
B = array(B)
print B
Output:
>>> array([3, -2, 6, 3])
With something like:
A = np.array([ [1,4,2], [5,1,8], [2,9,5], [3,6,6] ])
def F(Z):
return Z[0] + Z[1] - Z[2]
print F(A)
So that the output is something like:
>>> array( [ [3] , [-2], [6], [3] ] )
I know the 2nd code won't produce what I'm after, but I'm just trying to give an idea of what I'm talking about. Thanks!
EDIT:
I used the function above just as a simple example. The real function I'd like to use is something like this:
from numpy import linalg as LA
def F(Z):
#Z is an array of matrices
return LA.eigh(Z)[0]
So I have an array of 3x3 matrices, and I'd like an output array of their eigenvalues. And I'm wondering if it's possible to do this in some numpythonic way, so as not to have to loop over the array.

Try:
np.apply_along_axis(F, 1, A)

Related

Why does it print array([ ]) when in a list when using an array to iterate and pull elements from?

Looking to see if I could use comprehensions or array operators, instead of for loops.
import numpy as np
a=[[1,2],[3,4]]
b=np.array(a)
c=[[x*z for x in z] for z in b[0:1]]
print(c)
OUTPUT = [[array([1, 2]), array([2, 4])]]
I want a list or array = [2,12]
I can convert list to 1D array after.
Where it is first element * second element for each row in array.
I want it to work on a general case for any 2 dimensional array.
Look at the action - step by step:
In [170]: b.shape
Out[170]: (2, 2)
In [171]: b[0:1]
Out[171]: array([[1, 2]]) # (1,2) array
In [172]: [z for z in b[0:1]]
Out[172]: [array([1, 2])] # iteration on 1st, size 1 dimension
In [173]: [[x for x in z] for z in b[0:1]]
Out[173]: [[1, 2]]
In [174]: [[x*z for x in z] for z in b[0:1]]
Out[174]: [[array([1, 2]), array([2, 4])]]
So you are doing [1*np.array([1,2]), 2*np.array([1,2])]
With the b[0:1] slicing you aren't even touching the 2nd row of b.
But a simpler list comprehension does:
In [175]: [i*j for i,j in b] # this iterates on the rows of b
Out[175]: [2, 12]
or
In [176]: b[:,0]*b[:,1]
Out[176]: array([ 2, 12])
The easiest way is to use the prod function in numpy.
from numpy import prod
a = [[1,2],[3,4]]
b = [prod(x) for x in a]
print(b)
Output:
[2,12]
To answer the question in parts.
Why does it print array([ ])... ?
The numpy array you use is a class with a __repr__ or a __str__ method, which decides what you see, when you have it as an argument in a print statement.
In numpys array case, something along the lines of:
def __repr__(self):
return self.__class__.__name__ + "(" + repr(self.array) + ")"
2 ... when in a list using an array...
The list or dict calls the child elements __repr__ methods in its __repr__ method.
...and pull elements from?
in your inner x*z you are multiplying x, a number (1 or 2) with z a 1x2 array (b[0:1] = array([[1, 2]])). Which has an array as a result, which is the array of itself, multiplied by each of its elements as a scalar ([[1*1, 1*2]], [2*1, 2*2]])
Some other solution for your problem (but probably the already mentioned prod while for sure be much faster ;)):
import numpy as np
a=[[1,2],[3,4]]
b=np.array(a)
c=[[z1*z2] for z1, z2 in b]
print(c)
You can just multiply the first column with the second one:
c = b[:, 0] * b[:, 1]
Or you can use np.multiply:
c = np.multiply.reduce(b[:, :2], axis=1)

For loops and matrices, replacing rows

I am trying to create a function first_rpt which will take as input matrix/array M and output an array which changes each row in the matrix to the values in row number 0 (first row).
If I have matrix a=np.array([[1,1,1],[2,2,2],[3,3,3]]) I would want the function to change that to a=[1,1,1],[1,1,1],[1,1,1]
def first_rpt(M):
new_array=M
M=np.array(M)
for i in len(M):
M[i]=M[0]
return new_array
This code brings back error "int" object is not iterable. Changing len(M) to range(len(M)) just outputs the same matrix inputted.
What am I doing wrong?
U9-Forward's answer is ideal, but since you insist on iterating over the array...
Just remove new_array and iterate over a range.
def first_rpt(M):
for i in xrange(len(M)): # "xrange" in case the array is very big
M[i] = M[0]
return M
In your code you assigned new_array = M, but then you reassigned M, which meant new_array was left pointing to the old M. You could also fix the problem by moving new_array = M after reassigning M, but there's no point.
Full test code
import numpy as np
def first_rpt(M):
for i in xrange(len(M)):
M[i] = M[0]
return M
a = np.array([[1,1,1], [2,2,2], [3,3,3]])
print first_rpt(a)
Why not just use:
>>> import numpy as np
>>> a=np.array([[1,1,1],[2,2,2],[3,3,3]])
>>> def first_rpt(M):
M[1:]=M[0]
return M
>>> first_rpt(a)
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
>>>
def first_rpt(M):
for i in range(1, len(M)):
M[i]=M[0]
return
In this task you don't need a "new_array"

Python - Create Array from List?!

import numpy as np
means = [[2, 2], [8, 3], [3, 6]]
cov = [[1, 0], [0, 1]]
N = 20
X0 = np.random.multivariate_normal(means[0], cov, N)
X1 = np.random.multivariate_normal(means[1], cov, N)
X2 = np.random.multivariate_normal(means[2], cov, N)
X = np.concatenate((X0, X1, X2), axis = 0)
Y = X[np.random.choice(X.shape[0], 3, replace=False)]
A = [X[np.random.choice(X.shape[0], 3, replace=False)]]
B = A[-1]
print(Y), print(type(Y))
print(A), print(type(A))
print(B), print(type(B))
>>>
[[3.58758421 6.83484817]
[9.10469916 4.23009063]
[7.24996633 4.0524614 ]]
<class 'numpy.ndarray'>
[array([[3.22836848, 7.06719777],
[2.33102712, 0.96966102],
[2.06576315, 4.84061538]])]
<class 'list'>
[[3.22836848 7.06719777]
[2.33102712 0.96966102]
[2.06576315 4.84061538]]
<class 'numpy.ndarray'>
Can you help me explain
What does X[np.random.choice(X.shape[0], 3, replace=False)] mean?
Is np.random.choice() supposed to return a new array?
Why Y and A return different results?
Is B supposed to return the last element in the list?
Thank you!
You can find the docs for scipy and numpy here as referenced in the comments.
Y is a numpy.ndarray object, and A is a list object. This is due to the [brackets] you have when you create A. The first and only element in A (the list) is Y (the array).
B does return the last element in the list. The last element in the list is the array object.
I would recommend reading this documentation on numpy.random.choice to find out exactly how the function works. In this instance, it essentially chooses 3 random indices from the numpy array X.
Y = X[np.random.choice(X.shape[0], 3, replace=False)]
This line can be thought of like this: Choose 3 random values from X, and create a new numpy array containing those values, and call it Y.
A = [X[np.random.choice(X.shape[0], 3, replace=False)]]
Then, define a regular python list. This is a list with only one element. That one element is a numpy array of 3 random values from X. The key concept is that A only has one element. However, that one element happens to be an array, which itself has 3 elements.
B = A[-1]
Finally, you are right that this returns the last element of A, and calls it B. From above, we know that A only has one element, an array of 3 elements. Therefore, that array is the last element of the list A.
The major takeaway is that python allows you to have lists of lists, lists of numpy arrays, etc.

Python: obtain multidimensional matrix as results from a function

I have a multi-inputs function, say:
def bla(a, b):
f = a + b
return f
When I do something like
import numpy as np
bla(np.asarray([0.2,0.4]), np.asarray([2,4]))
The result is:
array([ 2.2, 4.4])
However, I want bla to be applied to each possible pair of my inputs(bla(0.2, 2), bla(0.2, 4), bla(0.4, 2), bla(0.4, 4)) and obtain the final result as a 2-D matrix. In this example, I want the result to be:
array([[2.2, 4.2],
[2.4, 4.4]
])
How can I do this?
My original problem is that I have a function with three variables and one output, then I want to call the function by entering vectors for each variable so that I obtain a 3-D matrix as a result
Provided your function bla can accept arrays instead of scalars, you could use
meshgrid to prepare the inputs so that bla(A, B) returns the desired output:
import numpy as np
def bla(a, b):
f = a + b
return f
A, B = np.meshgrid([0.2,0.4], [2,4], sparse=True)
bla(A, B)
yields
array([[ 2.2, 2.4],
[ 4.2, 4.4]])
Not sure if you want to do this without modifying bla(), but for your example at least that's where the change must be:
def bla(a, b):
return np.asarray(a+n for n in b)
This operates on array a with each element of b, and builds an array with the results. Your example looks a little random (how did you get those .3 fractional parts?), but I'm guessing this is what you're trying to get at.

pythonic way to apply function to object multiple times

I want to repeatedly sum over varying dimensions of a numpy ndarray eg.
#what I've got
sumOverDims = [6 4 2 1]
ndarray = any n-dimensional numpy array
#what I want to do
ndarray.sum(6).sum(4).sum(2).sum(1)
how can I do this without an ugly loop?
Numpy's sum accepts a tuple for the axis argument:
ndarray.sum(axis=(1,2,4,6))
In general, a thing like
X.f(e0).f(e1).f(e2).…
can be rephrased as
reduce(lambda a, b: a.f(b), [ e0, e1, e2, … ], X)
or (if you dislike lambdas):
def f_(a, b): return a.f(b)
reduce(f_, [ e0, e1, e2, … ], X)
But I'm a bit in doubt if this really makes it more readable and effectively clearer (and thus more Pythonic) than using an iterative loop:
result = X
for e in [ e0, e1, e2, … ]:
result = result.f(e)
return result
I guess it boils down to a matter of taste and what you are more used to.
You could use reduce. An example with only two dimensions:
>>> A = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> reduce(numpy.sum, [1], A)
array([ 6, 15, 24])
>>> reduce(numpy.sum, [1, 0], A)
45
The argument to reduce is numpy's sum function, a list with the dimensions to sum over, and the numpy array A as the initial element to reduce. The function gets as parameter the (partially summed) numpy array and the dimension to sum next.

Categories