Any better way to slicing numpy array in parametric way in numpy? - python

I'd like to do slice operation on numpy array in parametric way in function so I could get expected array element for my computation. I know how to slide the array by index, but I am more interested in slicing array element in parametric way, so no need to indicate the index. In my case, I have coefficient array c and power array p, I have also parameter num_order. Basically, num_order decide the index of slicing array. To do so, I have following attempt:
my attempt:
import numpy as np
c=[1,1/2, -1/6, 1/12]
p= [1,2,3,4]
x = np.array([1, 1, 2, 3, 5, 8, 13, 21])
def arr_pow(x, num_order):
output= []
for i in range(num_order):
mul = c[i] * np.power(x, p[i])
output.append(mul)
return output
so, if num_order=2, then I also slice first two term of c and p doing c_new = c[:-2], p_new=p[:-2], c_new=[1,1/2], p_new=[1,2] and so on. I am curious is there any better way to do slicing element in two or more array based on param num_order. Can anyone point me out any elegant way to make this happen in parameterized function? Any thoughts?
update:
instead of doing c_new=c[:-1], p_new=[:-1] if num_order=3, and c_new=c[:-2], p_new=p[:-2] if num_order=2, and so on, is there more elegant way (parametric fashion) to do this? Any way of doing this efficiently in python function? Thanks!

I'm not sure if this is the output you want (if you could please update your question to include the expected output that would be helpful):
import numpy as np
c = np.array([1, 1 / 2, -1 / 6, 1 / 12])
p = np.array([1, 2, 3, 4])
x = np.array([1, 1, 2, 3, 5, 8, 13, 21])
def arr_pow_numpy(x, num_order):
return c[:num_order, None] * np.power(x[None], p[:num_order, None])
def arr_pow(x, num_order):
output = []
for i in range(num_order):
mul = c[i] * np.power(x, p[i])
output.append(mul)
return np.asarray(output)
for num_order in range(1, len(p)):
assert np.array_equal(arr_pow(x, num_order), arr_pow_numpy(x, num_order)), f"{num_order}"
The idea here is to use NumPy broadcasting plus NumPy slicing to achieve the result you want without for loops and in a parametric way.

Use the following:
num_order = 2
np.array([c[i] * np.power(x, p[i]) for i in range(num_order)])
# Out:
# array([[ 1. , 1. , 2. , 3. , 5. , 8. , 13. , 21. ],
# [ 0.5, 0.5, 2. , 4.5, 12.5, 32. , 84.5, 220.5]])

Related

How to perform a vectorized function on a 2D numpy array?

vecs = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
def find_len(vector):
return (vector[0] ** 2 + vector[1] ** 2 + vector[2] ** 2) ** 0.5
vec_len = np.vectorize(find_len)
I want to apply find_len to every vector in the 2d array and create a new numpy array with the values returned. How can I do this?
try this
res= []
for i in range(vecs.shape[0]):
res.append(find_len(vecs[i]))
res=np.array(res)
results in
array([ 3.74165739, 8.77496439, 13.92838828])
you can also make this in one line:
res = np.array([find_len(x) for x in vecs[range(vecs.shape[0])]])
Are you just looking for this result:
array([ 3.74165739, 8.77496439, 13.92838828])
because you can achieve that without vectorize, just use:
(vecs**2).sum(axis=1)**0.5
This also has the advantage of not being specific to vectors of length 3.
Operations are already applied element-wise, so you can handle the squaring and square rooting normally. sum(axis=1) says to sum along the rows.

selecting random elements from each column of numpy array

I have an n row, m column numpy array, and would like to create a new k x m array by selecting k random elements from each column of the array. I wrote the following python function to do this, but would like to implement something more efficient and faster:
def sample_array_cols(MyMatrix, nelements):
vmat = []
TempMat = MyMatrix.T
for v in TempMat:
v = np.ndarray.tolist(v)
subv = random.sample(v, nelements)
vmat = vmat + [subv]
return(np.array(vmat).T)
One question is whether there's a way to loop over each column without transposing the array (and then transposing back). More importantly, is there some way to map the random sample onto each column that would be faster than having a for loop over all columns? I don't have that much experience with numpy objects, but I would guess that there should be something analogous to apply/mapply in R that would work?
One alternative is to randomly generate the indices first, and then use take_along_axis to map them to the original array:
arr = np.random.randn(1000, 5000) # arbitrary
k = 10 # arbitrary
n, m = arr.shape
idx = np.random.randint(0, n, (k, m))
new = np.take_along_axis(arr, idx, axis=0)
Output (shape):
in [215]: new.shape
out[215]: (10, 500) # (k x m)
To sample each column without replacement just like your original solution
import numpy as np
matrix = np.arange(4*3).reshape(4,3)
matrix
Output
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
k = 2
np.take_along_axis(matrix, np.random.rand(*matrix.shape).argsort(axis=0)[:k], axis=0)
Output
array([[ 9, 1, 2],
[ 3, 4, 11]])
I would
Pre-allocate the result array, and fill in columns, and
Use numpy index based indexing
def sample_array_cols(matrix, n_result):
(n,m) = matrix.shape
vmat = numpy.array([n_result, m], dtype= matrix.dtype)
for c in range(m):
random_indices = numpy.random.randint(0, n, n_result)
vmat[:,c] = matrix[random_indices, c]
return vmat
Not quite fully vectorized, but better than building up a list, and the code scans just like your description.

Insert calculated values between consecutive values in array

Let's say I have a simple array, like this one:
import numpy as np
a = np.array([1,2,3])
Which returns me, obviously:
array([1, 2, 3])
I'm trying to add calculated values between consecutive values in this array. The calculation should return me n equally spaced values between it's bounds.
To express myself in numbers, let's say I want to add 1 value between each pair of consecutive values, so the function should return me a array like this:
array([1, 1.5, 2, 2.5, 3])
Another example, now with 2 values between each pair:
array([1, 1.33, 1.66, 2, 2.33, 2.66, 3])
I know the logic and I can create myself a function which will do the work, but I feel numpy has specific functions that would make my code so much cleaner!
If your array is
import numpy as np
n = 2
a = np.array([1,2,5])
new_size = a.size + (a.size - 1) * n
x = np.linspace(a.min(), a.max(), new_size)
xp = np.linspace(a.min(), a.max(), a.size)
fp = a
result = np.interp(x, xp, fp)
returns: array([1. , 1.33333333, 1.66666667, 2. , 2.66666667, 3.33333333, 4. ])
If your array is always evenly spaced, you can just use
new_size = a.size + (a.size - 1) * n
result = np.linspace(a.min(), a.max(), new_size)
Using linspace should do the trick:
a = np.array([1,2,3])
n = 1
temps = []
for i in range(1, len(a)):
temps.append(np.linspace(a[i-1], a[i], num=n+1, endpoint=False))
# Add last final ending point
temps.append(np.array([a[-1]]))
new_a = np.concatenate(temps)
print(new_a)
Try with np.arange:
a = np.array([1,2,3])
n = 2
print(np.arange(a.min(), a.max(), 1 / (n + 1)))
Output:
[1. 1.33333333 1.66666667 2. 2.33333333 2.66666667]

Iterating and making decision through a NumPy array

I've been trying to iterate through a NumPy array. I'm trying to check if every element of the array is greater than or equal to 0.1. If an element is greater than or equal to 0.1 the code should append a list.
The array elements should have numbers which where processed in another function. Example:
[[-0.68454815]
[-0.6868374 ]
[-0.72553124]
[-0.72324855]
[-0.69258814]
[ 0.30578739]
[ 0.2679637 ]
[ 0.27038732]
[-0.62690676]
[ 0.372456 ]
[ 0.32854353]
[ 0.33191556]
[-0.6357395 ]
[ 0.3649385 ]
[ 0.31940787]
[ 0.32325424]
[-0.71096214]
[ 0.29032854]
[ 0.2589025 ]
[ 0.26576582]
[-0.71774566]
[ 0.28527439]
[ 0.25350313]
[ 0.26095643]
[-0.65131719]
[ 0.35093124]
[ 0.30984058]]
I'm using this to create a graph with the outputs of the counter_list to create a convergence .
I've checked several threads on here on how to do it and I've came up with something like this:
def looper(self):
rows = error.shape[0]
for x in range(0, rows):
counter_list = list();
if error(rows) >= 0.2:
counter += 1
counter_list.append(counter)
print("Amount:",counter_list)
However, I'm getting this error:
TypeError: 'numpy.ndarray' object is not callable
I know that the error TypeError: 'numpy.ndarray' object is not callable means that you tried to call a numpy array as a function, however I still don't know how to fix it.
If this list is one dimensional, you can try just using numpys slicing.
return data[data >= 0.1]
returning a np.array where every item >= 0.1 in the data np array is kept.
Easier way to check that condition:
import numpy as np
def check(a):
if np.all(a >= 0.1):
print("All of them do!")
return True
else:
print("Someone doesnt :'(")
return False
a = np.array([1, 2, 3, 4, 5, 0.02])
b = np.array([1, 2, 3, 4, 5, 6])
check(a)
check(b)
Output:
Someone doesnt :'(
All of them do!
EDIT
To know the number there is a similar easiness way to do it:
print(np.count_nonzero(a < 0.1)) # prints 1
print(np.count_nonzero(a < 0.1)) # prints 0
Then you can do something like:
e = np.arange(1, np.count_nonzero(a < 0.1) + 1) # e is [1]
This does work with multidimensional arrays. ie:
a = np.array([[1, 2, 0.04, 4, 5, 3],[1, 2, 3, 4, 5, 0.02]])
check(a)
print(np.count_nonzero(a < 0.1))
e = np.arange(1, np.count_nonzero(a < 0.1) + 1)
print(e)
Output:
Someone doesnt :'(
2
[1 2]
EDIT 2
To do a representation of the increasing errors (error meaning value less than 0.1) the most elegant way I can think of doing it is with a cumulative sum:
import matplotlib.pyplot as plt
a = np.array([1, 2, 0.03, 4, 5, 0.06, 7, 8])
err = np.cumsum(a < 0.1)
plt.plot(np.arange(1, err.shape[0] + 1), err)
plt.savefig('image.png')
plt.show()
Let me know if this is what you wanted!

How to plot pairwise distances of two-dimensional vectors?

I have a set of data in python likes:
x y angle
If I want to calculate the distance between two points with all possible value and plot the distances with the difference between two angles.
x, y, a = np.loadtxt('w51e2-pa-2pk.log', unpack=True)
n = 0
f=(((x[n])-x[n+1:])**2+((y[n])-y[n+1:])**2)**0.5
d = a[n]-a[n+1:]
plt.scatter(f,d)
There are 255 points in my data.
f is the distance and d is the difference between two angles.
My question is can I set n = [1,2,3,.....255] and do the calculation again to get the f and d of all possible pairs?
You can obtain the pairwise distances through broadcasting by considering it as an outer operation on the array of 2-dimensional vectors as follows:
vecs = np.stack((x, y)).T
np.linalg.norm(vecs[np.newaxis, :] - vecs[:, np.newaxis], axis=2)
For example,
In [1]: import numpy as np
...: x = np.array([1, 2, 3])
...: y = np.array([3, 4, 6])
...: vecs = np.stack((x, y)).T
...: np.linalg.norm(vecs[np.newaxis, :] - vecs[:, np.newaxis], axis=2)
...:
Out[1]:
array([[ 0. , 1.41421356, 3.60555128],
[ 1.41421356, 0. , 2.23606798],
[ 3.60555128, 2.23606798, 0. ]])
Here, the (i, j)'th entry is the distance between the i'th and j'th vectors.
The case of the pairwise differences between angles is similar, but simpler, as you only have one dimension to deal with:
In [2]: a = np.array([10, 12, 15])
...: a[np.newaxis, :] - a[: , np.newaxis]
...:
Out[2]:
array([[ 0, 2, 5],
[-2, 0, 3],
[-5, -3, 0]])
Moreover, plt.scatter does not care that the results are given as matrices, and putting everything together using the notation of the question, you can obtain the plot of angles by distances by doing something like
vecs = np.stack((x, y)).T
f = np.linalg.norm(vecs[np.newaxis, :] - vecs[:, np.newaxis], axis=2)
d = angle[np.newaxis, :] - angle[: , np.newaxis]
plt.scatter(f, d)
You have to use a for loop and range() to iterate over n, e.g. like like this:
n = len(x)
for i in range(n):
# do something with the current index
# e.g. print the points
print x[i]
print y[i]
But note that if you use i+1 inside the last iteration, this will already be outside of your list.
Also in your calculation there are errors. (x[n])-x[n+1:] does not work because x[n] is a single value in your list while x[n+1:] is a list starting from n+1'th element. You can not subtract a list from an int or whatever it is.
Maybe you will have to even use two nested loops to do what you want. I guess that you want to calculate the distance between each point so a two dimensional array may be the data structure you want.
If you are interested in all combinations of the points in x and y I suggest to use itertools, which will give you all possible combinations. Then you can do it like follows:
import itertools
f = [((x[i]-x[j])**2 + (y[i]-y[j])**2)**0.5 for i,j in itertools.product(255,255) if i!=j]
# and similar for the angles
But maybe there is even an easier way...

Categories