How to ravel numpy array in 's' path? - python

Given a 2D np.array:
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
How do I ravel it in an s-path such that I get
>>> sravel(arr)
array([1, 2, 3, 6, 5, 4, 7, 8, 9])
Additonally, I would like the option of going down the 0-axis first as well, i.e.
>>> sravel(arr, [0,1])
array([1, 4, 7, 8, 5, 2, 3, 6, 9])
here the second argument of the parenthesis indicates the order of axis.

I don't think there is any direct way to do that, but it's not hard to get that result:
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr2 = arr.copy()
arr2[1::2] = np.flip(arr[1::2], 1)
print(arr2.ravel())
# [1 2 3 6 5 4 7 8 9]
arr3 = arr.T.copy()
arr3[1::2] = np.flip(arr.T[1::2], 1)
print(arr3.ravel())
# [1 4 7 8 5 2 3 6 9]
EDIT: As pointed out by scleronomic, the second case can also be done by means of an F-contiguous array:
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# The array is copied with F order so ravel does not require another copy later
arr3 = arr.copy(order='F')
arr3[:, 1::2] = np.flip(arr3[:, 1::2], 0)
print(arr3.ravel(order='F'))
# [1 4 7 8 5 2 3 6 9]

Related

How to extend array in numpy? [duplicate]

This question already has an answer here:
Appending matrix A with matrix B
(1 answer)
Closed 2 years ago.
I have a array:
a = np.array([[1, 2, 3],
[4, 5, 6])
b = np.array([[7, 8],
[9, 10]])
I want to extend array with expected result:
c = np.array([[1, 2, 3, 7, 8],
[4, 5, 6, 9, 10])
Thanks!
You can use .hstack:
import numpy as np
a = np.array([[1, 2, 3],
[4, 5, 6]])
b = np.array([[7, 8],
[9, 10]])
c = np.hstack((a, b))
print(c)
Output:
[[ 1 2 3 7 8]
[ 4 5 6 9 10]]

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

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]]

Replace duplicate elements in a numpy array based on a range

I have an 1d numpy array arr as below:
arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
For the duplicate elements, I wish to randomly select any one of the indices containing the same element and replace it with a value missing in between 0 and arr.shape[0].
e.g. In the given array, 7 is present in indices 1, 4 and 9. Hence, I wish to randomly select an index between 1, 4 and 9 and set its value by randomly selecting some element like 8, which is absent in the array. In the end, arr should contain arr.shape[0] unique elements lying between 0 and arr.shape[0] - 1 (both inclusive)
How can I do this efficiently using Numpy (possibly without needing to use any explicit loop)?
Here's one based on np.isin -
def create_uniques(arr):
# Get unique ones and the respective counts
unq,c = np.unique(arr,return_counts=1)
# Get mask of matches from the arr against the ones that have
# respective counts > 1, i.e. the ones with duplicates
m = np.isin(arr,unq[c>1])
# Get the ones that are absent in original array and shuffle it
newvals = np.setdiff1d(np.arange(len(arr)),arr[~m])
np.random.shuffle(newvals)
# Assign the shuffled values into the duplicate places to get final o/p
arr[m] = newvals
return ar
Sample runs -
In [53]: arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
In [54]: create_uniques(arr)
Out[54]: array([9, 7, 0, 1, 6, 4, 8, 2, 3, 5])
In [55]: arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
In [56]: create_uniques(arr)
Out[56]: array([9, 4, 0, 5, 6, 2, 7, 1, 3, 8])
In [57]: arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
In [58]: create_uniques(arr)
Out[58]: array([9, 4, 0, 1, 7, 2, 6, 8, 3, 5])
Extending Divakar's answer (and I have basically no experience in python so this is likely a very roundabout and unpython way of doing it but):
import numpy as np
def create_uniques(arr):
np.random.seed()
indices = []
for i, x in enumerate(arr):
indices.append([arr[i], [j for j, y in enumerate(arr) if y == arr[i]]])
indices[i].append(np.random.choice(indices[i][1]))
indices[i][1].remove(indices[i][2])
sidx = arr.argsort()
b = arr[sidx]
new_vals = np.setdiff1d(np.arange(len(arr)),arr)
arr[sidx[1:][b[:-1] == b[1:]]] = new_vals
for i,x in enumerate(arr):
if x == indices[i][0] and i != indices[i][2]:
arr[i] = arr[indices[i][2]]
arr[indices[i][2]] = x
return arr
Sample:
arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
print(arr)
print(create_uniques(arr))
arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
print(create_uniques(arr))
arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
print(create_uniques(arr))
arr = np.array([9, 7, 0, 4, 7, 4, 2, 2, 3, 7])
print(create_uniques(arr))
Outputs:
[9 7 0 4 7 4 2 2 3 7]
[9 7 0 4 6 5 2 1 3 8]
[9 8 0 4 6 5 1 2 3 7]
[9 8 0 4 6 5 2 1 3 7]
[9 7 0 5 6 4 2 1 3 8]

Forming a matrix by summing 2 arrays element-array wise using NumPy

Let's say I have the following NumPy arrays:
i = array([2, 4, 5])
j = array([0, 1, 2])
I would like to have a very efficient method (built-in if possible) to sum those vectors and have an output that looks like this:
[[2 4 5]
[3 5 6]
[4 6 7]]
So basically each column is the array j to which the k th element of i has been added (k = 0, 1, 2 in this case)
Use numpy.add.outer.
>>> import numpy as np
>>> i = np.array([2, 4, 5])
>>> j = np.array([0, 1, 2])
>>>
>>> np.add.outer(j, i)
array([[2, 4, 5],
[3, 5, 6],
[4, 6, 7]])
Or with broadcasting:
In [272]: i[:,None]+j
Out[272]:
array([[2, 3, 4],
[4, 5, 6],
[5, 6, 7]])
i[:,None] makes a (3,1) array, which broadcasts with a (3,) (or (1,3)) to make a (3,3).

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