How do I shift col of numpy matrix to last col? - python

Say I have a numpy matrix as such:
[[1, 3, 4, 7, 8]
[5, 6, 8, 2, 6]
[2, 9, 3, 3, 6]
[7, 1, 9, 3, 5]]
I want to shift column 2 of the matrix to the last column:
[[1, 4, 7, 8, 3]
[5, 8, 2, 6, 6]
[2, 3, 3, 6, 9]
[7, 9, 3, 5, 1]]
How exactly do I do this?

Use numpy.roll:
arr[:, 1:] = np.roll(arr[:, 1:], -1, 1)
Output:
array([[1, 4, 7, 8, 3],
[5, 8, 2, 6, 6],
[2, 3, 3, 6, 9],
[7, 9, 3, 5, 1]])
How:
np.roll takes three arguments: a, shift and axis:
np.roll(a = arr[:, 1:], shift = -1, axis = 1)
This means that, take arr[:, 1:](all rows, all columns from 1), and shift it one unit to the left (-1. to the right would be +1), along the axis 1 (i.e. columnar shift, axis 0 would be row shift).
np.roll, as name states, is a circular shift. One unit shift will make last column to be the first, and so on.

Create a list of columns, then use that to index the array. Here, new_column_order uses a range to get all columns before col, another range to get all columns after col, then puts col at the end. Each range object is unpacked via * into the new column list.
x = np.array([[1, 3, 4, 7, 8],
[5, 6, 8, 2, 6],
[2, 9, 3, 3, 6],
[7, 1, 9, 3, 5]])
col = 1 # 2nd column
new_column_order = [*range(col), *range(col + 1, x.shape[-1]), col]
x_new = x[:, new_column_order]
print(x_new)
Output:
[[1 4 7 8 3]
[5 8 2 6 6]
[2 3 3 6 9]
[7 9 3 5 1]]

Related

How to generate values from a diagonal to fill matrix

I have the following diagonal matrix
a = array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
And the desired out come is the following
array([[1, 3, 4, 5],
[3, 2, 5, 6],
[4, 5, 3, 7],
[5, 6, 7, 4]])
Each element is the sum of the corresponding diagonals.
Thanks a lot
Try:
>>> np.diag(a) + np.diag(a)[:, None] - a
array([[1, 3, 4, 5],
[3, 2, 5, 6],
[4, 5, 3, 7],
[5, 6, 7, 4]])
Addendum
What if a is a DataFrame?
Then: np.diag(a) + np.diag(a)[:, None] - a is also a DataFrame (with same index and columns as a).
What if a is a numpy array, but I want a DataFrame result?
Then use: pd.DataFrame(...) instead.
You can use:
# get diagonal
diag = np.diag(a)
# outer sum
out = diag+diag[:,None]
# or
# out = np.outer(diag, diag)
# reset diagonal
np.fill_diagonal(out, diag)
print(out)
output:
[[1 3 4 5]
[3 2 5 6]
[4 5 3 7]
[5 6 7 4]]

numpy.roll horizontally on a 2D ndarray with different values

Doing np.roll(a, 1, axis = 1) on:
a = np.array([
[6, 3, 9, 2, 3],
[1, 7, 8, 1, 2],
[5, 4, 2, 2, 4],
[3, 9, 7, 6, 5],
])
results in the correct:
array([
[3, 6, 3, 9, 2],
[2, 1, 7, 8, 1],
[4, 5, 4, 2, 2],
[5, 3, 9, 7, 6]
])
The documentation says:
If a tuple, then axis must be a tuple of the same size, and each of the given axes is shifted by the corresponding number.
Now I like to roll rows of a by different values, like [1,2,1,3] meaning, first row will be rolled by 1, second by 2, third by 1 and forth by 3. But np.roll(a, [1,2,1,3], axis=(1,1,1,1)) doesn't seem to do it. What would be the correct interpretation of the sentence in the docs?
By specifying a tuple in np.roll you can roll an array along various axes. For example, np.roll(a, (3,2), axis=(0,1)) will shift each element of a by 3 places along axis 0, and it will also shift each element by 2 places along axis 1. np.roll does not have an option to roll each row by a different amount. You can do it though for example as follows:
import numpy as np
a = np.array([
[6, 3, 9, 2, 3],
[1, 7, 8, 1, 2],
[5, 4, 2, 2, 4],
[3, 9, 7, 6, 5],
])
shifts = np.c_[[1,2,1,3]]
a[np.c_[:a.shape[0]], (np.r_[:a.shape[1]] - shifts) % a.shape[1]]
It gives:
array([[3, 6, 3, 9, 2],
[1, 2, 1, 7, 8],
[4, 5, 4, 2, 2],
[7, 6, 5, 3, 9]])

Convert one-dimensional array to two-dimensional array so that each element is a row in the result

I want to know how to convert this: array([0, 1, 2, 3, 4, 5]) to this:
array([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3],
[4, 4, 4],
[5, 5, 5]])
In short, given a flat array, repeat each element inside the array n times, so that each element creates a sub-array of n of the same element, and concatenate these sub-arrays into one, so that each row contains an element from the original array repeated n times.
I can do this:
def repeat(lst, n):
return [[e]*n for e in lst]
>repeat(range(10), 4)
[[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
[5, 5, 5, 5],
[6, 6, 6, 6],
[7, 7, 7, 7],
[8, 8, 8, 8],
[9, 9, 9, 9]]
How to do this in NumPy?
You can use numpy's repeat like this:
np.repeat(range(10), 4).reshape(10,4)
which gives:
[[0 0 0 0]
[1 1 1 1]
[2 2 2 2]
[3 3 3 3]
[4 4 4 4]
[5 5 5 5]
[6 6 6 6]
[7 7 7 7]
[8 8 8 8]
[9 9 9 9]]
You can use tile that handles dimensions:
a = np.array([0, 1, 2, 3, 4, 5])
N = 4
np.tile(a[:,None], (1, N))
# or
np.tile(a, (N, 1)).T
or broadcast_to:
np.broadcast_to(a, (N, a.shape[0])).T
# or
np.broadcast_to(a[:,None], (a.shape[0], N))
Or multiply by an array of ones:
a[:,None]*np.ones(N, dtype=a.dtype)
output:
array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
[5, 5, 5, 5]])

How can I get the index of an element of a diagonal in a matrix?

To explain further, I will give an example. I have a 8x8 grid made up of random numbers,
m = [
[1 ,5, 2, 8, 6, 9, 6, 8]
[2, 2, 2, 2, 8, 2, 2, 1]
[9, 5, 9, 6, 8, 2, 7, 2]
[2, 8, 8 ,6 ,4 ,1 ,8 ,1]
[2, 5, 5, 5, 4, 4, 7, 9]
[3, 9, 8, 8, 9, 4, 1, 1]
[8, 9, 2, 4, 2, 8, 4, 3]
[4, 4, 7, 8, 7, 5, 3, 6]]
I have written code that gives me the list of the diagonal given an x and y value. For example, if an x of 2 and a y of 3 is given, the diagonal [2,5,8,5,9,8,3] will be returned. This is the code:
def main():
m = [[1 ,5, 2, 8, 6, 9, 6, 8],[2, 2, 2, 2, 8, 2, 2, 1],[9, 5, 9, 6, 8, 2, 7, 2],[2, 8, 8 ,6 ,4 ,1 ,8 ,1],[2, 5, 5, 5, 4, 4, 7, 9],[3, 9, 8, 8, 9, 4, 1, 1],[8, 9, 2, 4, 2, 8, 4, 3],[4, 4, 7, 8, 7, 5, 3, 6]]
x = 2
y = 3
for i in m:
print(i)
print(diagonal(m,x,y))
def diagonal(m, x, y):
#x
row = max((y - x, 0))
#y
col = max((x - y, 0))
while row < len(m) and col < len(m[row]):
yield m[row][col]
row += 1
col += 1
main()
My question is, how could I get the index of the given element in the diagonal list. In the example, the coordinates are x=2 and y=3(which is the number 8), and the resulting diagonal is [2,5,8,5,9,8,3], so the index of the element is 2. Also I cannot use numpy fyi.
First, the case where x
if x<y:
row = y-x
idx = y-row
This simplifies to idx=x, and by symetry
idx = min(x,y)
You can grab the index of a element in a list by using list.index(element).
For example:
diagonal = [2,5,8,5,9,8,3]
theIndex = diagonal.index(8)
print(theIndex)
I hope this helps. Good luck!
I would suggest you change your function (or make a variant) to return a tuple with the coordinates and numbers instead of just the numbers (similar to what enumerate() does. It will be easier to map this to numbers and find coordinates of numbers afterward
In other words, if you:
yield (row,col,m[row][col])
you will be able to obtain just the numbers with :
numbers = [ num for row,col,num in diagonal(m,2,3) ]
but you will also be able to manipulate the coordinates when you need to

Tile rows of a 2D numpy array based on values in separate numpy vector

I have a source array:
a = array([[1, 1, 2, 2],
[3, 4, 5, 6],
[7, 7, 7, 8]])
And a vector that indicates how many times I want to tile each row of the array:
count = array([3, 1, 2])
I want to get:
results =array([[1, 1, 2, 2],
[1, 1, 2, 2],
[1, 1, 2, 2],
[3, 4, 5, 6],
[7, 7, 7, 8],
[7, 7, 7, 8]]
Is there a vectorized/numpy way to achieve this?
Currently I'm using an iterative loop approach and it's horribly slow when len(a) and/or count contains high values.
numpy.repeat() is what you are after:
Code:
np.repeat(a, count, axis=0)
Test Code:
import numpy as np
a = np.array([[1, 1, 2, 2],
[3, 4, 5, 6],
[7, 7, 7, 8]])
count = np.array([3, 1, 2])
print(np.repeat(a, count, axis=0))
Results:
[[1 1 2 2]
[1 1 2 2]
[1 1 2 2]
[3 4 5 6]
[7 7 7 8]
[7 7 7 8]]

Categories

Resources