3

Problem: Populate a 10 x 10 array of zeros randomly with 10 1's, 20 2's, 30 3's.

I don't actually have to use an array, rather I just need coordinates for the positions where the values would be. It's just easier to think of in terms of an array.

I have written several solutions for this, but they all seem to be non-straight forward and non-pythonic. I am hoping someone can give me some insight. My method has been using a linear array of 0--99, choosing randomly (np.random.choice) 10 values, removing them from the array, then choosing 20 random values. After that, I convert the linear positions into (y,x) coordinates.

import numpy as np

dim = 10
grid = np.arange(dim**2)

n1 = 10
n2 = 20
n3 = 30

def populate(grid, n, dim):
    pos = np.random.choice(grid, size=n, replace=False)
    yx = np.zeros((n,2))
    for i in xrange(n):
        delPos = np.where(grid==pos[i])
        grid = np.delete(grid, delPos)
        yx[i,:] = [np.floor(pos[i]/dim), pos[i]%dim]
    return(yx, grid)

pos1, grid = populate(grid, n1, dim)
pos2, grid = populate(grid, n2, dim)
pos3, grid = populate(grid, n3, dim)

Extra Suppose when I populate the 1's, I want them all on one half of the "array." I can do it using my method (sampling from grid[dim**2/2:]), but I haven't figured out how to do the same with the other suggestions.

3 Answers 3

3

You can create a list of all coordinates, shuffle that list and take the first 60 of those (10 + 20 + 30):

>>> import random
>>> coordinates = [(i, j) for i in xrange(10) for j in xrange(10)]
>>> random.shuffle(coordinates)
>>> coordinates[:60]
[(9, 5), (6, 9), (1, 5), ..., (0, 2), (5, 9), (2, 6)]

You can then use the first 10 to insert the 10 values, the next 20 for the 20 values and the remaining for the 30 values.

Sign up to request clarification or add additional context in comments.

Comments

3

To generate the array, you can use numpy.random.choice.

np.random.choice([0, 1, 2, 3], size=(10,10), p=[.4, .1, .2, .3])

Then you can convert to coordinates. Note that numpy.random.choice generates a random sample using probabilities p, and thus you are not guaranteed to get the exact proportions in p.

Extra

If you want to have all the 1s on a particular side of the array, you can generate two random arrays and then hstack them. The trick is to slightly modify the probabilities of each number on each side.

In [1]: import numpy as np
In [2]: rem = .1/3 # amount to de- / increase the probability for non-1s
In [3]: A = np.random.choice([0, 1, 2, 3], size=(5, 10),
                              p=[.4-rem, .2, .2-rem, .3-rem])
In [4]: B = np.random.choice([0, 2, 3], size=(5, 10), p=[.4+rem, .2+rem, .3+rem])
In [5]: M = np.hstack( (A, B) )
In [6]: M
Out[1]: 
array([[1, 1, 3, 0, 3, 0, 0, 1, 1, 0, 2, 2, 0, 2, 0, 2, 3, 3, 2, 0],
       [0, 3, 3, 3, 3, 0, 1, 3, 1, 3, 0, 2, 3, 0, 0, 0, 3, 3, 2, 3],
       [1, 0, 0, 0, 1, 0, 3, 1, 2, 2, 0, 3, 0, 3, 3, 0, 0, 3, 0, 0],
       [3, 2, 3, 0, 3, 0, 1, 2, 3, 2, 0, 0, 0, 0, 3, 2, 0, 0, 0, 3],
       [3, 3, 0, 3, 3, 3, 1, 3, 0, 3, 0, 2, 0, 2, 0, 0, 0, 3, 3, 3]])

Here, because I'm putting all the 1s on the left, I double the probability of 1 and decrease the probability of each number equally. The same logic applies when creating the other side.

5 Comments

Wow, even though I've used the p argument before, it never occurred to me to use it in this application. Your solution is simply and eloquent, thanks you!
I think p[0] should be 0.4
@mititan8: I've added an Extra to my question, and I am trying to figure out how to modify your answer such that it works in that instance. Do you have any suggestions?
Actually, this approach won't give you EXACTLY 10 1's, 20 2's, 30 3's - it will maintain these probabilities only on average. To check, count occurrences in the array you are getting. If you need exact number, you should use Simeon's approach
@ngimel: good point, I've updated my answer to make this readily apparent.
0

Not sure if this is anymore "pythonic", but here's something I came up with using part of Simeon's answer.

import random

dim = 10
n1 = 10
n2 = 20
n3 = 30

coords = [[i,j] for i in xrange(dim) for j in xrange(dim)]

def setCoords(coords, n):
    pos = []
    for i in xrange(n):
        random.shuffle(coords)
        pos.append(coords.pop())
    return(coords, pos)

coordsTmp, pos1 = setCoords(coords[dim**2/2:], n1)
coords = coords[:dim**2/2] + coordsTmp
coords, pos2 = setCoords(coords, n2)
coords, pos3 = setCoords(coords, n3)

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.