Iterate through a numpy ndarray, manage first and last elements - python

I have a numpy array
import numpy as np
arr = np.array([2, 3, 4, 7, 7, 4, 4, 5, 1, 1, 9, 9, 9, 4, 25, 26])
I would like to iterate over this list to produce pairs of "matching" elements. In the above array, 7 matches 7. You only compare the element "ahead" and the element "behind".
My problem: how do I deal with the first and last elements?
This is what I have to begin with:
for i in range(len(arr)):
if (arr[i] == arr[i+1]):
print( "Match at entry %d at array location (%d)" % (arr[i], i))
else:
pass
This outputs:
Match at entry 7 at array location (3)
Match at entry 7 at array location (4)
Match at entry 4 at array location (6)
Match at entry 1 at array location (9)
Match at entry 9 at array location (11)
Match at entry 9 at array location (12)
I feel the condition should be
if ((arr[i] == arr[i+1]) and (arr[i] == arr[i-1]))
but this throws an error.
How do I deal with the first and last elements?

You should avoid loops in NumPy.
Using slightly modified array with pairs at the start end:
>>> arr = np.array([2, 2, 3, 4, 7, 7, 4, 4, 5, 1, 1, 9, 9, 9, 4, 25, 26, 26])
This finds the first index of each pair.
>>> np.where(arr[:-1] == arr[1:])[0]
array([ 0, 4, 6, 9, 11, 12, 16])
Printing them out:
arr = np.array([2, 2, 3, 4, 7, 7, 4, 4, 5, 1, 1, 9, 9, 9, 4, 25, 26, 26])
matches = np.where(arr[:-1] == arr[1:])[0]
for index in matches:
for i in [index, index + 1]:
print("Match at entry %d at array location (%d)" % (arr[i], i))
prints:
Match at entry 2 at array location (0)
Match at entry 2 at array location (1)
Match at entry 7 at array location (4)
Match at entry 7 at array location (5)
Match at entry 4 at array location (6)
Match at entry 4 at array location (7)
Match at entry 1 at array location (9)
Match at entry 1 at array location (10)
Match at entry 9 at array location (11)
Match at entry 9 at array location (12)
Match at entry 9 at array location (12)
Match at entry 9 at array location (13)
Match at entry 26 at array location (16)
Match at entry 26 at array location (17)
The function np.where can be used in several ways. In our case we use the condition arr[:-1] == arr[1:]. This compares each element with the next in the array:
>>> arr[:-1] == arr[1:]
array([ True, False, False, False, True, False, True, False, False,
True, False, True, True, False, False, False, True], dtype=bool)
Now applying np.where to this condition gives a tuple with matching indices.
>>> cond = arr[:-1] == arr[1:]
>>> np.where(cond)
(array([ 0, 4, 6, 9, 11, 12, 16]),)
Since we have a 1D array, we get a tuple with one element. For a 2D array we would have gotten a tuple with two elements, holding the indices along the first and second dimension. We take these indices out of the tuple:
>>> np.where(cond)[0]
array([ 0, 4, 6, 9, 11, 12, 16])

Related

Python - Problem multiplying the inverse of a matrix with a vector - Structural Engineering (FEM)

I´m developing a code to analyse portal frames with the finite element method.
However I´m encountering a problem, when I try to multiply the inverse of specic positions of a matrix with an array, Python is returning: shapes (9,9) and (9,1,1) not aligned: 9 (dim 1) != 1 (dim 1).
I have a 15x15 matrix and an array with 15 rows, but I just need to multiply positions [3:11].
Any guess where the problem might be?
The code is too big to be displayed in SO, so I just wrighted the 15x15 matrix, kportico, and the array, f. I´ve allready checked the matrix and the array and they are correct.
k = [k1global, k2global, k3global, k4global]
kportico = np.zeros([15, 15])
for i, m in enumerate(k):
kportico[i*3:i*3+6, i*3:i*3+6] += m
f = np.array([[f1global[0]],
[f1global[1]],
[f1global[2]],
[f1global[3]+f2globalx[0]+f2globaly[0]],
[f1global[4]+f2globalx[1]+f2globaly[1]],
[f1global[5]+f2globalx[2]+f2globaly[2]],
[f2globalx[3]+f2globaly[3]+f3globalx[0]+f3globaly[0]],
[f2globalx[4]+f2globaly[4]+f3globalx[1]+f3globaly[1]],
[f2globalx[5]+f2globaly[5]+f3globalx[2]+f3globaly[2]],
[f4global[3]+f3globalx[3]+f3globaly[3]],
[f4global[4]+f3globalx[4]+f3globaly[4]],
[f4global[5]+f3globalx[5]+f3globaly[5]],
[f4global[0]],
[f4global[1]],
[f4global[2]]])
u = np.dot(np.linalg.pinv(kportico[np.ix_([3, 4, 5, 6, 7, 8, 9, 10, 11], [3, 4, 5, 6, 7, 8, 9, 10, 11])]), f[np.ix_([3, 4, 5, 6, 7, 8, 9, 10, 11])])

Reversing array without reverse function but loop isn't working

So I am trying to reverse an array from a .txt file without using the reverse function. here is what I have.
numbers = read() #creates numbers array out of the txt file
numbersrev = numbers #blank array for reverse
numLength = len(numbers) #measures the length of the array
print(numbers)
print("Array length of numbers: ", numLength)
i = numLength
i = i-1 #since the array starts at 0 instead of 1
k = 0
for k in range(8):
numbersrev[k] = numbers[i]
print ("The ", i," element of numbers is the ", k," element of numbersrev")
i -= 1
k += 1
print(numbersrev)
This is what I get after debugging on vscode:
[2, 4, 9, 11, 8, 3, 2, 5, 10]
Array length of numbers: 9
The 8 element of numbers is the 0 element of numbersrev
The 7 element of numbers is the 1 element of numbersrev
The 6 element of numbers is the 2 element of numbersrev
The 5 element of numbers is the 3 element of numbersrev
The 4 element of numbers is the 4 element of numbersrev
The 3 element of numbers is the 5 element of numbersrev
The 2 element of numbers is the 6 element of numbersrev
The 1 element of numbers is the 7 element of numbersrev
[10, 5, 2, 3, 8, 3, 2, 5, 10]
The top array is the original and the bottom array is the supposed reversal array
I cannot for the life of me find out why it stops changing the numbersrev array halfway through. Anybody know what the cause could be?
Okay, a few things...
First for loops increment their variables automatically in python.
so:
for k in range(8):
...
i -= 1
k += 1
should be:
for k in range(8):
...
i -= 1
No need to manually increment k.
Next lists are NOT arrays.
Lists in python are very different from arrays in a language like C.
Lists are mutable, and are passed by reference by default.
so when you try to make an empty array:
numbersrev = numbers #blank array for reverse
you are actually referencing the same 'list' from both numbers AND numbersrev
What you should have done is numbersrev = []
Then in your for loop, simply append to numbersrev rather than assign.
for k in range(numLength):
numbersrev.append(numbers[i])
print ("The ", i," element of numbers is the ", k," element of numbersrev")
i -= 1
Lastly
you could/should reference the length of numbers rather than a hardcoded value in your for loop, but how you have it will still work (assuming you ONLY ever get 8 numbers)
for k in range(numLength):
...
All together
numbers = read() #creates numbers array out of the txt file
numbersrev = [] #blank array for reverse
numLength = len(numbers) #measures the length of the array
print(numbers)
print("Array length of numbers: ", numLength)
i = numLength
i = i-1 #since the array starts at 0 instead of 1
for k in range(numLength):
numbersrev.append(numbers[i])
print ("The ", i," element of numbers is the ", k," element of numbersrev")
i -= 1
print(numbersrev)
numbersrev = numbers sets numbersrev to point to the same list as numbers, meaning when you modify numbers or numbersrev you're modifying the other at the same time. To make an actual copy of the object, you instead need to call numbersrev = numbers.copy(). Also, #sahasrara62's comment is correct, you need to call for k in range(numLength) instead of for k in range(8)
What you've done here is assign the reversed array as the normal array with this line:
numbersrev = numbers #blank array for reverse
What you're actually doing with that loop is this:
numbers[0] = numbers[9] # [10, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers[1] = numbers[8] # [10, 9, 3, 4, 5, 6, 7, 8, 9, 10]
numbers[2] = numbers[7] # [10, 9, 8, 4, 5, 6, 7, 8, 9, 10]
numbers[3] = numbers[6] # [10, 9, 8, 7, 5, 6, 7, 8, 9, 10]
numbers[4] = numbers[5] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
numbers[5] = numbers[4] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
numbers[6] = numbers[3] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
numbers[7] = numbers[2] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
numbers[8] = numbers[1] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
numbers[9] = numbers[0] # [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]
...
If you assign the variable like this:
numLength = len(numbers) #measures the length of the array
numbersrev = [0]*numLength #blank array for reverse
You'll get the correct answer since the reverse list is no longer pointing to the normal list.
the problem you are facing is because of this line
numbersrev = numbers #blank array for reverse
In this line you are not creating an empty array, you are saving the reference of numbers array in a new variable named numbersrev. This means that when you make an operation in array numbersrev you are changing also the values of numbers. To avoid this kind of problems you have two options:
Make a copy of the array with slices
In this way you copy the values of the array, not the reference of the array. This means the changes you make to the new array will not change the original array.
numbersrev = numbers[:]
Create an empty array and use append instead of assignation
This change is a little different from what you did, but basically instead of creating a copy of the array, you create a new array which will be filled in the for loop, something like:
numbers.rev = []
...
for k in range(8):
numbersrev.append(numbers[i])
So with the first option and also changing some things in the k index we have a code like this:
numbers = [2, 4, 9, 11, 8, 3, 2, 5, 10] # you can change for read
numbersrev = numbers[:] #copy array elements
numLength = len(numbers) #array length
print(numbers)
print("Array length of numbers: ", numLength)
i = numLength - 1
# you don't need to initialize k because it's initialized and incremented with range in the loop
for k in range(numLength):
numbersrev[k] = numbers[i]
print ("The ", i," element of numbers is the ", k," element of numbersrev")
i -= 1
print(numbersrev)
Hope this would help you solve the problem, just like a note you could solve this problem in many ways: slicing (numbers[::-1]), list comprehension and some others. All the ways are valid so just in case you want to explore more.

Can't delete element in ndarray

I am trying to delete the last element in an array, if the element does not meet certain conditions. The code I am using is:
# Set the distibution parameter to 2
a = 2
# Set the size to 100
s = 100
# Create Zipf's Law distribution using a and s
x = np.random.zipf(a,s)
# Reorder list by number frequency
xb = np.unique(x, return_counts=True)
print("X",x)
print("XB",xb)
for i in reversed(xb):
if xb[-1] > xb[-2]*1.5:
xb = np.delete(xb,-1)
print("XB mod",xb)
print()
I get the following output for the python print("X",x) and ````python print("XB", xb):
XB (array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28,
29,
31, 33, 56, 225]), array([57, 17, 4, 4, 2, 1, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1],
dtype=int64))
However, when I try to run the deletion portion of the code, I get the following error:
Traceback (most recent call last): File "test2.py", line 22, in
if xb[-1] > xb[-2]*1.5: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Any idea how to fix it, so that I can delete the last element in the XB array, if it doesn't meet the condition?
xb is a tuple consisting of a pair of np.ndarray objects.
How do I delete the last element in the XB array, if it doesn't meet the condition
If you want to delete the last pair of zipped values (e.g. 225 and 1 for your data) based on your condition where you compare the last two numbers of the first row of data (e.g. 225 > 56 * 1.5 for your data):
if xb[0][-1] > xb[0][-2] * 1.5:
xb = tuple(x[:-1] for x in xb)
>>> xb
(array([ 1, 2, ..., 31, 33, 56]),
array([57, 17, ..., 1, 1, 1]))
Short answer:
Use all:
for i in reversed(xb):
if all(xb[-1] > xb[-2]*1.5): # use all here
xb = np.delete(xb,-1)
Equivalent: if (xb[-1] > xb[-2]*1.5).all():
Long answer:
You have:
xb
(array([ 1, 2, 3, 4, 5, 7, 9, 10, 13, 21, 22, 24, 30]),
array([62, 16, 2, 4, 6, 3, 1, 1, 1, 1, 1, 1, 1]))
that is a list of numpy arrays.
Next, xb[-1] > xb[-2]*1.5 returns:
array([ True, True, False, False, False, False, False, False, False,
False, False, False, False])
If you do not use all OR any, this condition will raise the errror
the problem is in if xb[-1] > xb[-2]*1.5
xb is not a scalar but a vector (1d array).
So what does it means v1 > v2? all items? at least one item?
Take for example [2,3] > [1,4], all will return False because 3 > 4, is False, any on the other hand, will return True because there is at least one that is true (2 > 1).
Like the error say it is ambiguous.
So, if for example you want that all the items will pass the condition you have to use:
if np.all(xb[-1] > xb[-2]*1.5): ...

Numpy - Count Number of Values Until Condition Is Satisfied

If I have two numpy arrays of the same size.
ArrayOne = np.array([ 2, 5, 5, 6, 7, 10, 13])
ArrayTwo = np.array([ 8, 10, 12, 14, 16, 18, 24])
How can I count how many elements there are until the beginning of the array. Unless the condition ArrayOne >= ArrayTwo is satisfied. In which case how many elements until that condition. Then make an array out of the result.
So as an example for element [0] there are 0 elements in front. For element [1] there is 1 element in front, and ArrayOne >= ArrayTwo wasn't satisfied. At element [5] in ArrayOne is bigger than element[0] in ArrayTwo so there are four elements until element [1] in ArrayTwo Etc.
Giving the result
result = np.array([ 0, 1, 2, 3, 4, 4, 3])
Thanks in advance.
Basically, at index i you have the value
value = i -count(how often element i in array one was bigger than array two until index i)
Because I'm on mobile with damn autocorrect, I rename the two arrays to a and b.
def get_value(a, b, i):
max_value = a[i]
nb_smaller_elements = sum(1 for el in range(i) if b[el] < max_value)
return i - nb_smaller_elements
I think I got it. Using #Paul Panzer 's answer, I made a for loop that goes through the list.
def toggle(ArrayOne,ArrayTwo):
a = 0
sum = -1
linels = []
for i in range(len(ArrayOne)):
sum += 1
a = sum - np.searchsorted(ArrayTwo, ArrayOne[i])
linels.append(a)
return np.array(linels)
I get the result
linels = np.array([ 0, 1, 2, 3, 4, 4, 3])

Select rows in a Numpy 2D array with a boolean vector

I have a matrix and a boolean vector:
>>>from numpy import *
>>>a = arange(20).reshape(4,5)
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
>>>b = asarray( [1, 1, 0, 1] ).reshape(-1,1)
array([[1],
[1],
[0],
[1]])
Now I want to select all the corresponding rows in this matrix where the corresponding index in the vector is equal to zero.
>>>a[b==0]
array([10])
How can I make it so this returns this particular row?
[10, 11, 12, 13, 14]
The shape of b is somewhat strange, but if you can craft it as a nicer index it's a simple selection:
idx = b.reshape(a.shape[0])
print a[idx==0,:]
>>> [[10 11 12 13 14]]
You can read this as, "select all the rows where the index is 0, and for each row selected take all the columns". Your expected answer should really be a list-of-lists since you are asking for all of the rows that match a criteria.
Nine years later, I just wanted to add another answer to this question in the case where b is actually a boolean vector.
Square bracket indexing of a Numpy matrix with scalar indices give the corresponding rows, so for example a[2] gives the third row of a. Multiple rows can be selected (potentially with repeats) using a vector of indices.
Similarly, logical vectors that have the same length as the number of rows act as "masks", for example:
a = np.arange(20).reshape(4,5)
b = np.array( [True, True, False, True] )
a[b] # 3x5 matrix formed with the first, second, and last row of a
To answer the OP specifically, the only thing to do from there is to negate the vector b:
a[ np.logical_not(b) ]
Lastly, if b is defined as in the OP with ones and zeros and a column shape, one would simply do: np.logical_not( b.ravel().astype(bool) ).

Categories