How to iterate through a section of a 2D list - python

I want to go through just a section of a 2D list, rather than the whole thing.
Here's essentially what it is I want to do:
Let's say the user inputs the coordinates [1,1] (so, row 1 column 1)
If I have the 2D list:
[[1,3,7],
[4,2,9],
[13,5,6]]
Then I want to iterate through all the elements adjacent to the element at [1,1]
Furthermore, if the element is at a corner or the edge of the 2D list, (so basically if the user enters [0,0], for example) then I want to just want to get back the elements at [0,0], [0,1], [1,0], and [1,1]
So essentially I just want elements adjacent to a specific to a certain point in the 2D array.
Here's what I've done so far:
I've made it so that it assigns 4 variables at the start of the code: starting_row, ending_row, starting_column, and ending_column. These variables are assigned values based off of which coordinates the user wants to input (if they the row is 0 or len(list) then the for loop runs accordingly. The same goes for the columns).
Then, I use a nested for loop to go through every element
for row in range(row_start, row_end+1):
for column in range(column_start, column_end+1):
print(lst[row,column])
Only thing is, it doesn't seem to work correctly and often outputs the whole entire 2D list when enter a list size of more than 3x3 elements (all the lists will be square lists)

You can slice the list of lists according to the given row and column. For the lower bounds, use max with 0 to avoid slicing with a negative index, but not so for the upper bounds since it is okay for the stopping index of a slice to be out of the range of a list:
def get_adjacent_items(matrix, row, col):
output = []
for r in matrix[max(row - 1, 0): row + 2]:
for i in r[max(col - 1, 0): col + 2]:
output.append(i)
return output
or, with a list comprehension:
def get_adjacent_items(matrix, row, col):
return [i for r in matrix[max(row - 1, 0): row + 2] for i in r[max(col - 1, 0): col + 2]]
so that given:
m = [[1, 3, 7],
[4, 2, 9],
[13, 5, 6]]
get_adjacent_items(m, 0, 0) returns: [1, 3, 4, 2]
get_adjacent_items(m, 1, 1) return: [1, 3, 7, 4, 2, 9, 13, 5, 6]
get_adjacent_items(m, 2, 1) returns: [4, 2, 9, 13, 5, 6]
get_adjacent_items(m, 2, 2) returns: [2, 9, 5, 6]

Related

Add repeated elements of array indexed by another array

I have a relatively simple problem that I cannot solve without using loops. It is difficult for me to figure out the correct title for this problem.
Lets say we have two numpy arrays:
array_1 = np.array([[0, 1, 2],
[3, 3, 3],
[3, 3, 4],
[3, 6, 2]])
array_2 = np.array([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3],
[4, 4, 4],
[5, 5, 5],
[6, 6, 6]])
array_1 represents indices of the rows in array_2 that we want to sum. So for example, 4th row in result array should contain summed all rows in array_2 that have same row indices as all 3s in array_1.
It is much easier to understand it in the code:
result = np.empty(array_2.shape)
for i in range(array_1.shape[0]):
for j in range(array_1.shape[1]):
index = array_1[i, j]
result[index] = result[index] + array_2[i]
Result should be:
[[ 0 0 0]
[ 0 0 0]
[ 3 3 3]
[10 10 10]
[ 2 2 2]
[ 0 0 0]
[ 3 3 3]]
I tried to use np.einsum but I need to use both elements in array as indices and also its rows as indices so I'm not sure if np.einsum is the best path here.
This is the problem I have in graphics. array_1 represent indices of vertices for triangles and array_2 represents normals where index of a row corresponds to the index of the vertex
Any time you're adding something from a repeated index, normal ufuncs like np.add don't work out of the box because they only process a repeated fancy index once. Instead, you have to use the unbuffered version, which is np.add.at.
Here, you have a pair of indices: the row in array_1 is the row index into array_2, and the element of array_1 is the row index into the output.
First, construct the indices explicitly as fancy indices. This will make it much simpler to use them:
output_row = array_1.ravel()
input_row = np.repeat(np.arange(array_1.shape[0]), array_1.shape[1]).ravel()
You can apply input_row directly to array_2, but you need add.at to use output_row:
output = np.zeros_like(array_2)
np.add.at(output, output_row, array_2[input_row])
You really only use the first four rows of array_2, so it could be truncated to
array_2 = array2[:array_1.shape[0]]
In that case, you would want to initialize the output as:
output = np.zeros_like(array_2, shape=(output_row.max() + 1, array2.shape[1]))

Sort matrix columns based on the values in the first row

Currently trying to do some beginner matrix handling exercises, but are unsure on how to sort a nxn matrix's column by the columns first index. etc.
It should be a method that could work on any size matrix, as it will not be the same size matrix every time.
Anyone who has any good suggestions?
The implementation here can be very simple depending on how the data, ie. the matrix, is represented. If it is given as a list of column-lists, it just needs a sort. For the given example:
>>> m = [[2, 3, 7], [-1, -2, 5.2], [0, 1, 4], [2, 4, 5]]
>>> y = sorted(m, key=lambda x: x[0])
>>> y
[[-1, -2, 5.2], [0, 1, 4], [2, 3, 7], [2, 4, 5]]
Other representations might need a more complex approach. For example, if the matrix is given as a list of rows:
>>> m = [[2, -1, 0, 2], [3, -2, 1, 4], [7, 5.2, 4, 5]]
>>> order = sorted(range(len(m[0])), key=lambda x: m[0][x])
>>> order
[1, 2, 0, 3]
>>> y = [[row[x] for x in order] for row in m]
>>> y
[[-1, 0, 2, 2], [-2, 1, 3, 4], [5.2, 4, 7, 5]]
The idea here is that first, we will get the order the elements are going to be in based on the first row. We can do that by sorting range(4), so [0, 1, 2, 3] with the sorting key (the value used for sorting) being the i-th value of the first row.
The result is that we get [1, 2, 0, 3] which says: Column index 1 is first, then index 2, 0 and finally 3.
Now we want to create a new matrix where every row follows that order which we can do with a list comprehension over the original matrix, where for each row, we create a new list that has the elements of the row according to the order we determined before.
Note that this approach creates new lists for the whole matrix - if you're dealing with large matrices, you probably want to use the appropriate primitives from numpy and swap the elements around in place.
If matrix is your input, you can do:
result = list(zip(*sorted(zip(*matrix))))
So working from inside out, this expression does:
zip: to iterate the transposed of the matrix (rows become columns and vice versa)
sorted: sorts the transposed matrix. No need to provide a custom key, the sorting will be by the first element (row, which is a column in the original matrix). If there is a tie, by second element (row), ...etc.
zip: to iterate the transposed of the transposed matrix, i.e. transposing it back to its original shape
list to turn the iterable to a list (a matrix)

How to change the places of numbers in list, python

I have list, ex. [1, 3, 0, 2, 6, 0, 7]
And if there is “0” I have to put it at the end of the list, without changing the places of other numbers.
Should return - [1,3,2,6,7,0,0]
Thanks in advance! :)
Just sort by zeroness:
>>> a = [1, 3, 0, 2, 6, 0, 7]
>>> a.sort(key=bool, reverse=True)
>>> a
[1, 3, 2, 6, 7, 0, 0]
my_list = [1, 3, 0, 2, 6, 0, 7]
count = my_list.count(0)
my_list = [value for value in my_list if value != 0]
my_list.extend([0]*count)
print(my_list)
output:
[1, 3, 2, 6, 7, 0, 0]
Here is another way of doing it that is different than the answers so far. This will work whether it is 0 or another number that you need the same operation for (as long as you replace the 0 with the number you want in both the filter functions).
lst = [1, 3, 0, 2, 6, 0, 7]
[*filter((0).__ne__, lst)] + [*filter((0).__eq__, lst)]
Output:
[1, 3, 2, 6, 7, 0, 0]
Definitely an easier way, but here is what I came up with:
l = [1, 3, 0, 2, 6, 0, 7]
le = len(l)
l = [x for x in l if x !=0]
for i in range(0,le-len(l)):
l.append(0)
This is a basic programming exercice so lets approach it with the most basic Python constructs. A simple algorithm would be to track an index where the first zero is detected and swap non-zero numbers with that position which we then move forward. This will carry the zeroes across the list until all the zeroes are bunched up at then end.
numbers = [1, 3, 0, 2, 6, 0, 7]
firstZero = -1 # track position of first zero
for index,number in enumerate(numbers): # go through numbers in list
if firstZero < 0:
if number == 0: # do nothing until a zero is found
firstZero = index # start tracking first zero position
elif number != 0:
numbers[firstZero] = number # swap non-zero numbers
numbers[index] = 0 # with zero position
firstZero += 1 # and advance first zero position
print(numbers)
# [1, 3, 2, 6, 7, 0, 0]
Tracing the progress of the loop, you can see the movement of the firstZero position relative to the index and the carrying of the zeroes through the list:
# firstZero index number numbers
# -1 0 1 [1, 3, 0, 2, 6, 0, 7] # do nothing
# Z ^
# -1 1 3 [1, 3, 0, 2, 6, 0, 7] # do nothing
# Z ...^
# 2 2 0 [1, 3, 0 2, 6, 0, 7] # record position of 1st zero
# ..........Z^
# 3 3 2 [1, 3, 2<->0, 6, 0, 7] # swap non-zero (2),
# ...Z^ # advance position of 1st zero
# 4 4 6 [1, 3, 2, 6<->0, 0, 7] # swap non-zero (6),
# ...Z^ # advance position of 1st zero
# 4 5 0 [1, 3, 2, 6, 0, 0, 7] # ignore subsequent zeroes
# Z ..^ # do nothing
# 5 6 0 [1, 3, 2, 6, 7<- 0,->0] # swap non-zero (7),
# ...Z ..^ # advance position of 1st zero
On the other hand, if you're into more meta trickery, you can leverage the stability of the Python sort and its ability to sort on a calculated key to group the numbers between non-zeroes and zeroes:
numbers.sort(numbers,key=lambda n:not n)
The key parameter uses a function to obtain a key to sort on (as opposed to the numbers themselves). That function here is not n which, when applied to an integer, will return True if it is zero and False if it is not. This will match sorting keys with numbers in the following way:
False, False, True, False, False, True, False
[1, 3, 0, 2, 6, 0, 7]
Sorting boolean values will place False values before True and the stability of the Python sort will keep the relative order of items for identical key values:
False, False, False, False, False True, True
[1, 3, 2, 6, 7, 0, 0]
The calculated key values only exist during the sort process, the result is only the number list sorted accordingly.
Although using the sort function is a nice trick, in therms of complexity, it will perform in O(n log n) time. The basic algorithm being more specialized is able to do the same work in a single pass through the data which will perform in O(n) time.
Another way to group the zeroes at the end of the list in O(n) time is to build a new list by assembling a list of the non-zero items with a list of zeros of the appropriate length:
nonZero = [*filter(bool,numbers)]
numbers = nonZero + [0]*(len(numbers)-len(nonZero))
Then nonZero list is built using a list comprehension with unpacking of the result from the filter iterator. The second part is a repetition of a list with zero for the number of times needed to reach the original length.

Conditional iloc statement. "Time series analysis"

In Python, I have a data set with several rows and columns. If the value of one column=X I am interested in the value of the row above it and the column to the right so plus [1,1] from the previous location of value x.
What's the best way to approach this? If cell =X, is there a dynamic way I can get the location and then add X amount of rows and columns to it "since I am interested in the value of that cell"?
Visual Example of Question
Say our array is defined as:
import numpy as np
arr = np.array([[9, 8, 4, 5, 2],
[0, 3, 5, 6, 7],
[6, 6, 2, 0, 6],
[2, 0, 2, 5, 8],
[2, 6, 9, 7, 9]])
A straight forward solution is to just loop through the column of interest.
colIndex = 2
X = 2
rDelta = -1 #row change, up is negative!
cDelta = 1 #col change, right is positive
numberRows = np.shape(arr)[1]
output = []
#This loops through all the values in the specified column and looks for
#matches, if a match is found it is added to an output list
#Note that we only look at values that would result in valid output
# for example we will not look at the first row if
# we would need to index into the row above for the output
for i in range(max(-rDelta,0),min(numberRows,numberRows-rDelta)):
if(arr[i,colIndex]==X):
#replace this with your processing
output.append(arr[i+rDelta,colIndex+cDelta])
After running this code I get that output as
[6, 0]
which are the values up and to the right of the 2's in the second column.

How does numpy's argpartition work on the documentation's example?

I am trying to understand numpy's argpartition function. I have made the documentation's example as basic as possible.
import numpy as np
x = np.array([3, 4, 2, 1])
print("x: ", x)
a=np.argpartition(x, 3)
print("a: ", a)
print("x[a]:", x[a])
This is the output...
('x: ', array([3, 4, 2, 1]))
('a: ', array([2, 3, 0, 1]))
('x[a]:', array([2, 1, 3, 4]))
In the line a=np.argpartition(x, 3) isn't the kth element the last element (the number 1)? If it is number 1, when x is sorted shouldn't 1 become the first element (element 0)?
In x[a], why is 2 the first element "in front" of 1?
What fundamental thing am I missing?
The more complete answer to what argpartition does is in the documentation of partition, and that one says:
Creates a copy of the array with its elements rearranged in such a way
that the value of the element in k-th position is in the position it
would be in a sorted array. All elements smaller than the k-th element
are moved before this element and all equal or greater are moved
behind it. The ordering of the elements in the two partitions is
undefined.
So, for the input array 3, 4, 2, 1, the sorted array would be 1, 2, 3, 4.
The result of np.partition([3, 4, 2, 1], 3) will have the correct value (i.e. same as sorted array) in the 3rd (i.e. last) element. The correct value for the 3rd element is 4.
Let me show this for all values of k to make it clear:
np.partition([3, 4, 2, 1], 0) - [1, 4, 2, 3]
np.partition([3, 4, 2, 1], 1) - [1, 2, 4, 3]
np.partition([3, 4, 2, 1], 2) - [1, 2, 3, 4]
np.partition([3, 4, 2, 1], 3) - [2, 1, 3, 4]
In other words: the k-th element of the result is the same as the k-th element of the sorted array. All elements before k are smaller than or equal to that element. All elements after it are greater than or equal to it.
The same happens with argpartition, except argpartition returns indices which can then be used for form the same result.
Similar to #Imtinan, I struggled with this. I found it useful to break up the function into the arg and the partition.
Take the following array:
array = np.array([9, 2, 7, 4, 6, 3, 8, 1, 5])
the corresponding indices are: [0,1,2,3,4,5,6,7,8] where 8th index = 5 and 0th = 9
if we do np.partition(array, k=5), the code is going to take the 5th element (not index) and then place it into a new array. It is then going to put those elements < 5th element before it and that > 5th element after, like this:
pseudo output: [lower value elements, 5th element, higher value elements]
if we compute this we get:
array([3, 5, 1, 4, 2, 6, 8, 7, 9])
This makes sense as the 5th element in the original array = 6, [1,2,3,4,5] are all lower than 6 and [7,8,9] are higher than 6. Note that the elements are not ordered.
The arg part of the np.argpartition() then goes one step further and swaps the elements out for their respective indices in the original array. So if we did:
np.argpartition(array, 5) we will get:
array([5, 8, 7, 3, 1, 4, 6, 2, 0])
from above, the original array had this structure [index=value]
[0=9, 1=2, 2=7, 3=4, 4=6, 5=3, 6=8, 7=1, 8=5]
you can map the value of the index to the output and you with satisfy the condition:
argpartition() = partition(), like this:
[index form] array([5, 8, 7, 3, 1, 4, 6, 2, 0]) becomes
[3, 5, 1, 4, 2, 6, 8, 7, 9]
which is the same as the output of np.partition(array),
array([3, 5, 1, 4, 2, 6, 8, 7, 9])
Hopefully, this makes sense, it was the only way I could get my head around the arg part of the function.
i remember having a hard time figuring it out too, maybe the documentation is written badly but this is what it means
When you do a=np.argpartition(x, 3) then x is sorted in such a way that only the element at the k'th index will be sorted (in our case k=3)
So when you run this code basically you are asking what would the value of the 3rd index be in a sorted array. Hence the output is ('x[a]:', array([2, 1, 3, 4]))where only element 3 is sorted.
As the document suggests all numbers smaller than the kth element are before it (in no particular order) hence you get 2 before 1, since its no particular order.
i hope this clarifies it, if you are still confused then feel free to comment :)

Categories

Resources