Compare adjacent values in numpy array - python

I have a Numpy one-dimensional array of data, something like this
a = [1.9, 2.3, 2.1, 2.5, 2.7, 3.0, 3.3, 3.2, 3.1]
I want to create a new array, where the values are composed of the greater of the adjacent values. For the above example, the output would be:
b = [2.3, 2.3, 2.5, 2.7, 3.0, 3.3, 3.3, 3.2]
I can do this by looping through the input array, comparing the neighbouring values, eg:
import numpy as np
a = np.array([1.9, 2.3, 2.1, 2.5, 2.7, 3.0, 3.3, 3.2, 3.1])
b = np.zeros(len(a)-1)
for i in range(len(a)-1):
if (a[i] > a[i+1]):
b[i] = a[i]
else:
b[i] = a[i+1]
but I'd like to do this in a more elegant "pythonic" vectorised fashion. I've searched and read about np.zip, np.where, np.diff etc but haven't yet found a way to do this (or more likely, I haven't understood what is possible). Any suggestions ?

You want element-wise maximum of a[1:] and a[:-1]:
>>> a
array([ 1.9, 2.3, 2.1, 2.5, 2.7, 3. , 3.3, 3.2, 3.1])
>>> a[1:]
array([ 2.3, 2.1, 2.5, 2.7, 3. , 3.3, 3.2, 3.1])
>>> a[:-1]
array([ 1.9, 2.3, 2.1, 2.5, 2.7, 3. , 3.3, 3.2])
>>> np.maximum(a[1:], a[:-1])
array([ 2.3, 2.3, 2.5, 2.7, 3. , 3.3, 3.3, 3.2])

Related

Storing multiple arrays in a np.zeros or np.ones

I'm trying to initialize a dummy array of length n using np.zeros(n) with dtype=object. I want to use this dummy array to store n copies of another array of length m.
I'm trying to avoid for loop to set values at each index.
I tried using the below code but keep getting error -
temp = np.zeros(10, dtype=object)
arr = np.array([1.1,1.2,1.3,1.4,1.5])
res = temp * arr
The desired result should be -
np.array([[1.1,1.2,1.3,1.4,1.5], [1.1,1.2,1.3,1.4,1.5], ... 10 copies])
I keep getting the error -
operands could not be broadcast together with shapes (10,) (5,)
I understand that this error arises since the compiler thinks I'm trying to multiply those arrays.
So how do I achieve the task?
np.tile() is a built-in function that repeats a given array reps times. It looks like this is exactly what you need, i.e.:
res = np.tile(arr, 2)
>>> arr = np.array([1.1,1.2,1.3,1.4,1.5])
>>> arr
array([1.1, 1.2, 1.3, 1.4, 1.5])
>>> np.array([arr]*10)
array([[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5],
[1.1, 1.2, 1.3, 1.4, 1.5]])

Python. Convert string to dictionary containing numpy arrays

I have a string which has this structure:
{0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2]), 1: [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5]), 2: [array([6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}
It is in the form of a python dictionary containing numpy arrays.
How can I turn this string into a python dictionary containing numpy arrays?
Thanks for the replies already. I can't simply just change the string because the string is an output of a python file that I want to pipe to another one. I could go and change the format of this output, but I was rather hoping to avoid that.
In an interactive ipython session is created an alias:
In [7]: array = np.array
and edited a copy-n-paste of your sample. I had to add a couple of closing ]:
In [8]: data = {0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2])], 1
...: : [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5])], 2: [array(
...: [6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}
Now a simple execute works:
In [9]: data
Out[9]:
{0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2])],
1: [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5])],
2: [array([6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}
or with a string (corrected):
In [10]: astr="{0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2])], 1
...: : [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5])], 2: [array
...: ([6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}"
In [11]: astr
Out[11]: '{0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2])], 1: [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5])], 2: [array([6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}'
I can do an exec or eval:
In [15]: eval(astr)
Out[15]:
{0: [array([5.1, 3.5, 1.4, 0.2]), array([4.9, 3. , 1.4, 0.2])],
1: [array([7. , 3.2, 4.7, 1.4]), array([6.4, 3.2, 4.5, 1.5])],
2: [array([6.3, 3.3, 6. , 2.5]), array([7.1, 3. , 5.9, 2.1])]}
Use of eval is often discouraged because it can be hacked. But the safer ast_eval only works with dict, list and tuples, not np.array. One way or other you have to edit the string so it is a valid Python/numpy expression.
I changed how the string was being being produced so that it was in the form of a dictionary containing python lists instead of numpy arrays.
With this I could just use ast.literal_eval() to deseriealize it.

is there a parameter to set the precision for numpy.linspace?

I am trying to check if a numpy array contains a specific value:
>>> x = np.linspace(-5,5,101)
>>> x
array([-5. , -4.9, -4.8, -4.7, -4.6, -4.5, -4.4, -4.3, -4.2, -4.1, -4. ,
-3.9, -3.8, -3.7, -3.6, -3.5, -3.4, -3.3, -3.2, -3.1, -3. , -2.9,
-2.8, -2.7, -2.6, -2.5, -2.4, -2.3, -2.2, -2.1, -2. , -1.9, -1.8,
-1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1. , -0.9, -0.8, -0.7,
-0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0. , 0.1, 0.2, 0.3, 0.4,
0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2, 1.3, 1.4, 1.5,
1.6, 1.7, 1.8, 1.9, 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6,
2.7, 2.8, 2.9, 3. , 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7,
3.8, 3.9, 4. , 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8,
4.9, 5. ])
>>> -5. in x
True
>>> a = 0.2
>>> a
0.2
>>> a in x
False
I assigned a constant to variable a. It seems that the precision of a is not compatible with the elements in the numpy array generated by np.linspace().
I've searched the docs, but didn't find anything about this.
This is not a question of the precision of np.linspace, but rather of the type of the elements in the generated array.
np.linspace generates elements which, conceptually, equally divide the input range between them. However, these elements are then stored as floating point numbers with limited precision, which makes the generation process itself appear to lack precision.
By passing the dtype argument to np.linspace, you can specify the precision of the floating point type used to store its result, which can increase the apparent precision of the generation process.
Nevertheless, you should not use the equality operator to compare floating point numbers. Instead, use np.isclose in conjunction with np.ndarray.any, or some equivalent:
>>> floats_64 = np.linspace(-5, 5, 101, dtype='float64')
>>> floats_128 = np.linspace(-5, 5, 101, dtype='float128')
>>> print(0.2 in floats_64)
False
>>> print(floats_64[52])
0.20000000000000018
>>> print(np.isclose(0.2, floats_64).any()) # check if any element in floats_64 is close to 0.2
True
>>> print(0.2 in floats_128)
False
>>> print(floats_128[52])
0.20000000000000017764
>>> print(np.isclose(0.2, floats_128).any()) # check if any element in floats_128 is close to 0.2
True

How to print a value to a new array if it within a bound of previous value in that array in Python/Numpy

If I have an array:
StartArray=np.array([1, 2, 3, 1.4, 1.2, 0.6, 1.8, 1.5, 1.9, 2.2, 3, 4 ,2.3])
I would like to loop through this array starting with StartArray[0] and only keep values that are within +/- .5 of the last kept value to yield:
EndArray=[1, 1.4, 1.2, 1.5, 1.9, 2.2, 2.3]
This is what I have tried so far and the results don't make sense
StartArray=np.array([1, 2, 3, 1.4, 1.2, 0.6, 1.8, 1.5, 1.9, 2.2, 3, 4 ,2.3])
EndArray=np.empty_like(StartArray)
EndArray[0]=StartArray[0]
for i in range(len(StartArray)-1):
if EndArray[i]+.5>StartArray[i+1]>EndArray[i]-.5:
EndArray[i+1]=StartArray[i+1]
Out:
array([ 1. , 0.22559146, 0.13015365, 5.24910493, 0.63804761,
0.6 , 1.73143364, 1.5 , 1.9 , 2.2 ,
6.82525036, 0.61641556, 6.82325036])
List is the good structure for this job:
StartArray=np.array([1, 2, 3, 1.4, 1.2, 0.6, 1.8, 1.5, 1.9, 2.2, 3, 4 ,2.3])
ref=StartArray[0]
End=[]
for x in StartArray:
if abs(x- ref)<.5:
End.append(x)
ref=x
print(np.array(End))
[ 1. 1.4 1.2 1.5 1.9 2.2 2.3]
There are multiple problems with your approach. First, you're initializing EndArray to be the same size as StartArray, but that's not what you want your desired output to be. Instead, initialize EndArray to be an empty list and append values as your loop through StartArray. Secondly, you want the output values to be within 0.5 of the last kept value, so you need to keep track of this.
Adapting your code:
StartArray=np.array([1, 2, 3, 1.4, 1.2, 0.6, 1.8, 1.5, 1.9, 2.2, 3, 4 ,2.3])
EndArray=[]
last_kept = StartArray[0]
EndArray.append(last_kept)
for i in range(len(StartArray)-1):
if np.abs(StartArray[i+1] - last_kept) < 0.5:
last_kept = StartArray[i+1]
EndArray.append(last_kept)
# convert back to numpy array
EndArray = np.array(EndArray)

Merge lists of different size with order

Is there a good way to merge lists like that:
L1 = [1.1, 1.2, 1.3]
L2 = [2.1, 2.2, 2.3, 2.4]
L3 = [3.1, 3.2]
Result:
[1.1, 2.1, 3.1, 1.2, 2.2, 3.2, 1.3, 2.3, 2.4]
There should be no "None" elements in result.
Edit
Since it was marked as duplicate:
I do not need a result like this:
[(1.1, 2.1, 3.1), (1.2, 2.2, 3.2), (1.3, 2.3, None), (None, 2.4, None)]
I do not need any "None" elements. And the result should be one list.
With izip_longest from itertools:
>>> from itertools import izip_longest
>>> L1 = [1.1, 1.2, 1.3]
>>> L2 = [2.1, 2.2, 2.3, 2.4]
>>> L3 = [3.1, 3.2]
>>> [x for sub in izip_longest(L1,L2,L3) for x in sub if x is not None]
[1.1, 2.1, 3.1, 1.2, 2.2, 3.2, 1.3, 2.3, 2.4]
Answer to the comment:
What if the lists have None in them?
None is the default fillvalue:
>>> list(izip_longest(L1,L2,L3))
[(1.1, 2.1, 3.1), (1.2, 2.2, 3.2), (1.3, 2.3, None), (None, 2.4, None)]
If the lists can have None in them, use a fillvalue that cannot appear in the lists. For example:
>>> list(izip_longest(L1,L2,L3,fillvalue='my_awesome_fillval'))
[(1.1, 2.1, 3.1), (1.2, 2.2, 3.2), (1.3, 2.3, 'my_awesome_fillval'), ('my_awesome_fillval', 2.4, 'my_awesome_fillval')]
To merge the lists
L1 = [1.1, 1.2, 1.3]
L2 = [2.1, 2.2, 2.3, 2.4]
L3 = [3.1, 3.2]
you can use the following one-liner
>>> [x for y in map(None,L1,L2,L3) for x in y if x is not None]
[1.1, 2.1, 3.1, 1.2, 2.2, 3.2, 1.3, 2.3, 2.4]

Categories