Select N evenly spaced out elements in array, including first and last - python

I have an array of arbitrary length, and I want to select N elements of it, evenly spaced out (approximately, as N may be even, array length may be prime, etc) that includes the very first arr[0] element and the very last arr[len-1] element.
Example:
>>> arr = np.arange(17)
>>> arr
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
Then I want to make a function like the following to grab numElems evenly spaced out within the array, which must include the first and last element:
GetSpacedElements(numElems = 4)
>>> returns 0, 5, 11, 16
Does this make sense?
I've tried arr[0:len:numElems] (i.e. using the array start:stop:skip notation) and some slight variations, but I'm not getting what I'm looking for here:
>>> arr[0:len:numElems]
array([ 0, 4, 8, 12, 16])
or
>>> arr[0:len:numElems+1]
array([ 0, 5, 10, 15])
I don't care exactly what the middle elements are, as long as they're spaced evenly apart, off by an index of 1 let's say. But getting the right number of elements, including the index zero and last index, are critical.

To get a list of evenly spaced indices, use np.linspace:
idx = np.round(np.linspace(0, len(arr) - 1, numElems)).astype(int)
Next, index back into arr to get the corresponding values:
arr[idx]
Always use rounding before casting to integers. Internally, linspace calls astype when the dtype argument is provided. Therefore, this method is NOT equivalent to:
# this simply truncates the non-integer part
idx = np.linspace(0, len(array) - 1, numElems).astype(int)
idx = np.linspace(0, len(arr) - 1, numElems, dtype='int')

Your GetSpacedElements() function should also take in the array to avoid unfortunate side effects elsewhere in code. That said, the function would need to look like this:
import numpy as np
def GetSpacedElements(array, numElems = 4):
out = array[np.round(np.linspace(0, len(array)-1, numElems)).astype(int)]
return out
arr = np.arange(17)
print(array)
spacedArray = GetSpacedElements(arr, 4)
print (spacedArray)

If you want to know more about finding indices that match values you seek, also have a look at numpy.argmin and numpy.where. Implementing the former:
import numpy as np
test = np.arange(17)
def nearest_index(array, value):
return (np.abs(np.asarray(array) - value)).argmin()
def evenly_spaced_indices(array, steps):
return [nearest_index(array, value) for value in np.linspace(np.min(array), np.max(array), steps)]
print(evenly_spaced_indices(test,4))
You should keep in mind that this is an unnecessary amount of function calls for the initial question you asked as switftly demonstrated by coldspeed. np.round intuitively rounds to the closest matching integer serving as index, implementing a similar process but optimised in C++. If you are interested in the indices too, you could have your function simply return both:
import numpy as np
def GetSpacedElements(array, numElems=4, returnIndices=False):
indices = np.round(np.linspace(0, len(arr) - 1, numElems)).astype(int)
values = array[indices]
return (values, indices) if returnIndices else (values)
arr = np.arange(17) + 42
print(arr)
print(GetSpacedElements(arr, 4)) # values only
print(GetSpacedElements(arr, 4, returnIndices=True)[0]) # values only
print(GetSpacedElements(arr, 4, returnIndices=True)[1]) # indices only

To get N evenly spaced elements from list 'x':
x[::int(np.ceil( len(x) / N ))]

Related

How can I build a complementary array in numpy

I have an array of numbers corresponding to indices of another array.
index_array = np.array([2, 3, 5])
What I want to do is to create another array with the numbers 0, 1, 4, 6, 7, 8, 9. What I have thought is:
index_list = []
for i in range(10):
if i not in index_array:
index_list.append(i)
This works but I don't know if there is a more efficient way to do it or even a built-in function for it.
Probably the simplest solution is just to remove unwanted indices from the set:
n = 10
index_array = [2, 3, 5]
complement = np.delete(np.arange(n), index_array)
You can use numpy.setdiff1d to efficiently collect the unique value from a "universal array" that aren't in your index array. Passing assume_unique=True provides a small speed up.
When assume_unique is True, the result will be sorted so long as the input is sorted.
import numpy as np
# "Universal set" to take complement with respect to.
universe = np.arange(10)
a = np.array([2,3,5])
complement = np.setdiff1d(universe, a, assume_unique=True)
print(complement)
Results in
[0 1 4 6 7 8 9]

Iterating through two arrays with a correlation and getting the sum Numpy Python

I am trying to modify the code down below so that it adds up the indexes and the Numbers values that correlate with it.
So since the first element in indexes is 3, it takes the first 3 elements within Numbers which is 1, 5, 6 sum of all these integers is equal to 12.
For the second value in the next 5 elements is being computed 7,4,3,6,7 which is equal to 27.
I am trying to achieve Expected Output but am getting the Current Output what can I change in the code to achieve the Expected Output without using a for loop.
Numbers = np.array([1, 5, 6,7,4,3,6,7,11,3,4,6,2,20])
indexes = np.array([3 , 5, 5])
np.add.reduceat(Numbers, indexes)
Current Output:
array([11, 3, 62])
Expected Output
array([12, 27, 26])
np.add.reduceat(Numbers, [0,3,3,8,8,13])[::2]
Pair indices by cumulative sum and then provide it to reduceat
pair_indexes = np.insert(
indexes.cumsum(), 0, 0
).repeat([1] + [2] * (len(indexes) - 1) + [1])
np.add.reduceat(Numbers, pair_indexes)[::2]
Adjust your "indexes" variable to represent bins rather than increments.
Numbers = np.array([1, 5, 6,7,4,3,6,7,11,3,4,6,2,20])
indexes = np.array([0, 3 , 8, 13])
answ = np.add.reduceat(Numbers, indexes)[:-1]

What is wrong with my sorting algorithm?

I am a beginner programmer and I've been trying to create my own sorting algorithm in Python, I don't understand why it outputs only some of the numbers that were present in the input. I put debug prints everywhere to understand the problem but still got nothing. The code should find and move to the final array the biggest number of the input array, and do that until the input array it's empty, but it seems to stop at some point. There was a person with a similar problem but the solution did not apply to me as well. This is the code:
array = [3, 6, 25, 4, 5, 24, 7, 15, 5, 2, 0, 8, 1] #just random numbers
output = []
while(len(array) > 0):
maximum = 0
for x in array:
maximum = max(maximum, x)
output.append(maximum)
tempArray = []
for x in array:
temp = array.pop()
if(temp < maximum):
tempArray.append(temp)
array = tempArray
print(output)
The problem is here:
for x in array:
temp = array.pop()
You're modifying the same list that you're iterating over. That's going to cause trouble.
Consider what happens when 5 is the maximum (and there are two 5s in the input.) One 5 gets added to output, the rest of the 5s are never added to tempArray.
To diagnose, put some debug prints in the loop, such as print(output, array) at the end of the outer loop. And maybe more in the inner loop. After seeing the problem (removing two things from array each inner iteration, this works.
array = [3, 6, 25, 4, 5, 24, 7, 15, 5, 2, 0, 8, 1] #just random numbers
output = []
while(array):
maximum = 0
for x in array:
maximum = max(maximum, x)
output.append(maximum)
tempArray = []
for x in array:
if(x < maximum):
tempArray.append(x)
array = tempArray
print(output)
There are, of course, easier and better ways to delete the max from array, and only delete one copy of max instead of all.

How to get values in list at incremental indexes in Python?

I'm looking at getting values in a list with an increment.
l = [0,1,2,3,4,5,6,7]
and I want something like:
[0,4,6,7]
At the moment I am using l[0::2] but I would like sampling to be sparse at the beginning and increase towards the end of the list.
The reason I want this is because the list represents the points along a line from the center of a circle to a point on its circumference. At the moment I iterate every 10 points along the lines and draw a circle with a small radius on each. Therefore, my circles close to the center tend to overlap and I have gaps as I get close to the circle edge. I hope this provides a bit of context.
Thank you !
This can be more complicated than it sounds... You need a list of indices starting at zero and ending at the final element position in your list, presumably with no duplication (i.e. you don't want to get the same points twice). A generic way to do this would be to define the number of points you want first and then use a generator (scaled_series) that produces the required number of indices based on a function. We need a second generator (unique_ints) to ensure we get integer indices and no duplication.
def scaled_series(length, end, func):
""" Generate a scaled series based on y = func(i), for an increasing
function func, starting at 0, of the specified length, and ending at end
"""
scale = float(end) / (func(float(length)) - func(1.0))
intercept = -scale * func(1.0)
print 'scale', scale, 'intercept', intercept
for i in range(1, length + 1):
yield scale * func(float(i)) + intercept
def unique_ints(iter):
last_n = None
for n in iter:
if last_n is None or round(n) != round(last_n):
yield int(round(n))
last_n = n
L = [0, 1, 2, 3, 4, 5, 6, 7]
print [L[i] for i in unique_ints(scaled_series(4, 7, lambda x: 1 - 1 / (2 * x)))]
In this case, the function is 1 - 1/2x, which gives the series you want [0, 4, 6, 7]. You can play with the length (4) and the function to get the kind of spacing between the circles you are looking for.
I am not sure what exact algorithm you want to use, but if it is non-constant, as your example appears to be, then you should consider creating a generator function to yield values:
https://wiki.python.org/moin/Generators
Depending on what your desire here is, you may want to consider a built in interpolator like scipy: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy.interpolate.interp1d
Basically, given your question, you can't do it with the basic slice operator. Without more information this is the best answer I can give you :-)
Use the slice function to create a range of indices. You can then extend your sliced list with other slices.
k = [0,1,2,3,4,5,6,7]
r = slice(0,len(k)//2,4)
t = slice(r.stop,None,1)
j = k[r]
j.extend(k[t])
print(j) #outputs: [0,4,5,6,7]
What I would do is just use list comprehension to retrieve the values. It is not possible to do it just by indexing. This is what I came up with:
l = [0, 1, 2, 3, 4, 5, 6, 7]
m = [l[0]] + [l[1+sum(range(3, s-1, -1))] for s in [x for x in range(3, 0, -1)]]
and here is a breakdown of the code into loops:
# Start the list with the first value of l (the loop does not include it)
m = [l[0]]
# Descend from 3 to 1 ([3, 2, 1])
for s in range(3, 0, -1):
# append 1 + sum of [3], [3, 2] and [3, 2, 1]
m.append(l[ 1 + sum(range(3, s-1, -1)) ])
Both will give you the same answer:
>>> m
[0, 4, 6, 7]
I made this graphic that would I hope will help you to understand the process:

numpy.where() clause error

Briefly... here is the problem:
import numpy as np
a = np.array([ 0, 1, 2, 3, 4, 5, 6, 100, 8, 9])
np.where(a==100, -1, a[a])
What I expect to get is: 0, 1, 2, 3, 4, 5, 6, -1, 8, 9
Instead I'm getting: index 100 out of bounds 0<=index<10
I admit that the index is invalid but is shouldn't eval a[100] but -1 instead... as far as I understand numpy.where() command structure.
What I'm doing wrong in this example?
Just to clarify what I actually trying to do here is more detailed code:
It is a lookup table array remapping procedure:
import numpy as np
# gamma-ed look-up table array
lut = np.power(np.linspace(0, 1, 32), 1/2.44)*255.0
def gamma(x):
ln = (len(lut)-1)
idx = np.uint8(x*ln)
frac = x*ln - idx
return np.where( frac == 0.0,
lut[idx],
lut[idx]+(lut[idx+1]-lut[idx])*frac)
# some linear values array to remap
lin = np.linspace(0, 1, 64)
# final look-up remap
gamma_lin = gamma(lin)
Expressions that you put as function arguments are evaluated before they are passed to the function (Documentation link). Thus you are getting an index error from the expression a[a] even before np.where is called.
Use the following:
np.where(a==100, -1, a)
As stated by the documentation:
numpy.where(condition[, x, y])
Return elements, either from x or y, depending on condition.
If only condition is given, return condition.nonzero().
Here, a==100 is your condition, -1 the value that should be taken when the condition is met (True), a the values to fall back on.
The reason why you're getting an IndexError is your a[a]: you're indexing the array a by itself, which is then equivalent to a[[0,1,2,3,4,5,6,100,8,9]]: that fails because a has less than 100 elements...
Another approach is:
a_copy = a.copy()
a_copy[a==100] = -1
(replace a_copy by a if you want to change it in place)
When you write a[a] you try to take index 0,1,2...100... from a which is why you get the index out of bounds error. You should instead write np.where(a==100, -1, a) - I think that will produce the result you are looking for.

Categories