21

I already googled a bit and didn't find any good answers.

The thing is, I have a 2d numpy array and I'd like to replace some of its values at random positions.

I found some answers using numpy.random.choice to create a mask for the array. Unfortunately this does not create a view on the original array so I can not replace its values.

So here is an example of what I'd like to do.

Imagine I have 2d array with float values.

[[ 1., 2., 3.],
 [ 4., 5., 6.],
 [ 7., 8., 9.]]

And then I'd like to replace an arbitrary amount of elements. It would be nice if I could tune with a parameter how many elements are going to be replaced. A possible result could look like this:

[[ 3.234, 2., 3.],
 [ 4., 5., 6.],
 [ 7., 8., 2.234]]

I couldn't think of nice way to accomplish this. Help is appreciated.

6 Answers 6

19

Just mask your input array with a random one of the same shape.

import numpy as np

# input array
x = np.array([[ 1., 2., 3.], [ 4., 5., 6.], [ 7., 8., 9.]])

# random boolean mask for which values will be changed
mask = np.random.randint(0,2,size=x.shape).astype(np.bool)

# random matrix the same shape of your data
r = np.random.rand(*x.shape)*np.max(x)

# use your mask to replace values in your input array
x[mask] = r[mask]

Produces something like this:

[[ 1.          2.          3.        ]
 [ 4.          5.          8.54749399]
 [ 7.57749917  8.          4.22590641]]
Sign up to request clarification or add additional context in comments.

2 Comments

I like this one the best. It's quick but kinda not dirty.
To vary the expected number of elements to be replaced use mask = np.random.choice([0, 1], size=x.shape, p=((1 - replace_rate), replace_rate)).astype(np.bool) instead of the randint above, which is equivalent to using np.random.choice as above with replace_rate = 0.5.
5

It's easy to choose indices at random when the array is one-dimensional, so I'd recommend reshaping the array to 1D, changing random elements, then reshaping back to the original shape.

For example:

import numpy as np

def replaceRandom(arr, num):
    temp = np.asarray(arr)   # Cast to numpy array
    shape = temp.shape       # Store original shape
    temp = temp.flatten()    # Flatten to 1D
    inds = np.random.choice(temp.size, size=num)   # Get random indices
    temp[inds] = np.random.normal(size=num)        # Fill with something
    temp = temp.reshape(shape)                     # Restore original shape
    return temp

So this does something like:

>>> test = np.arange(24, dtype=np.float).reshape(2,3,4)
>>> print replaceRandom(test, 10)
[[[  0.          -0.95708819   2.           3.        ]
  [ -0.35466096   0.18493436   1.06883205   7.        ]
  [  8.           9.          10.          11.        ]]
 [[ -1.88613449  13.          14.          15.        ]
  [  0.57115795  -1.25526377  18.          -1.96359786]
  [ 20.          21.           2.29878207  23.        ]]]

Here I've replaced elements choosing from a normal distribution --- but obviously you can replace the call to np.random.normal with whatever you want.

Comments

3

You can create bernoulli random variables using scipy, and the parameter p will control what percent of values in your array you end up replacing. Then replace values in your original array based on whether the bernoulli random variable takes on a value of 0 or 1.

from scipy.stats import bernoulli as bn
import numpy as np

array = np.array([[ 1., 2., 3.],[ 4., 5., 6.],[ 7., 8., 9.]])
np.random.seed(123)
flag = bn.rvs(p=0.5,size=(3,3))
random_numbers = np.random.randn(3,3)
array[flag==0] = random_numbers[flag==0]

Comments

2

Not really optimized, but a starting point to help you figuring out a way of doing it:

import numpy as np

a = np.array( [[ 1., 2., 3.], [ 4., 5., 6.], [ 7., 8., 9.]])

def replace(ar,nbr):
  x,y = ar.shape
  s = x*y 
  mask = [1]*nbr + [0]*(s-nbr)
  np.random.shuffle(mask)
  mask = np.array(mask) == 1
  ar.reshape( (s) )[mask] = [ np.random.random() for _ in range(nbr) ]

Comments

1

You could always randomly generated n integers to index a flattened view (1D version) of your array, and set those indexed values equal to n random values:

In [1]: import numpy as np
In [2]: x = np.arange(1, 10).reshape(3, 3).astype(float)
In [3]: m = np.product(x.shape)
In [4]: n = 3
In [5]: x.ravel()[np.random.randint(0, m, size=n)] = np.random.rand(n)
In [6]: x
Out[6]: 
array([[ 0.28548823,  0.28819589,  3.        ],
       [ 4.        ,  5.        ,  6.        ],
       [ 7.        ,  8.        ,  0.28772056]])

You could scale the randomly generated values by some factor if you want values greater than 1; for example np.random.rand(n) * m would yield values between 0 and np.product(x.shape).

Note that numpy.ravel operates inplace by default.

Comments

0

Unfortunately there is no Numpy function to select random indices. Instead you can create a random Boolean array containing the number of True elements you want to select:

np.random.permutation(np.array([True]*n + [False]*(A.size-n)))

and reshape it to match the shape of your original array, e.g. to set 3 elements of a 4x4 array to 0:

import numpy as np 
A = np.ones(shape=(4,4)) # the original array to update
n = 3 # desired number of indices

mask = np.random.permutation(np.array([True]*n + [False]*(A.size-n))).reshape(A.shape)
A[mask] = 0 # update random elements

print(A)


[[1. 1. 1. 0.]
 [1. 1. 1. 1.]
 [0. 1. 0. 1.]
 [1. 1. 1. 1.]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.