user90465
user90465

Reputation: 187

Trying to generate random x,y coordinates within a ring in python

I am trying to generate random x and y coordinates within a ring, which has an outer radius of 3.5 and an inner radius of 2. Therefor the following must be true for x and y:

x**2 + y**2 < 12.25 and x**2 + y**2 > 4

I wrote the following function:

def meteorites():
    circle = False
    while circle == False:        
        r = np.array([uniform(-6., 6.), uniform(-6., 6.)])
        # we will regenerate random numbers untill the coordinates
        # are within the ring x^2+y^2 < 3,5^2 and x^2+y^2 > 2^2
        if (r[0]**2+r[1]**2 < 12.25) and (r[0]**2+r[1]**2 > 4.):
            circle = True

       else :
            circle = False

    return r[0], r[1]

x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
    x[i] = meteorites()[0]
    y[i] = meteorites()[1]
plt.scatter(x,y)
plt.show()  

When I plot the resulting coordinates I get a square from -3.5 to 3.5. I can't seem to find the problem. I'm also not sure if it's a coding error, or some dum math problem. Since you guys are usually good at both, can you see what I'm doing wrong here?

Upvotes: 3

Views: 6447

Answers (7)

Nimrod Morag
Nimrod Morag

Reputation: 984

as @Martijn Pieters suggested, simply draw the polar coordinates uniformly in the range you require.

theta = uniform(0,2*np.pi)
r = uniform(2.,3.5)
x = r*np.cos(theta)
y = r*np.sin(theta)

EDIT: There will be equal probability for every point in the ring to occur.

But practically there will be less pixels for a given theta the closer r is to the lower limit. So "meteorites" with smaller r will occur with larger probability.

I believe this effect is negligeble.

EDIT 2: MBo's answer is better. Code:

theta = uniform(0, 2 * np.pi)
r = np.sqrt(uniform(2.0 ** 2, 3.5 ** 2)) # draw from sqrt distribution
x = r * np.cos(theta)
y = r * np.sin(theta)

Upvotes: 2

Sandipan Dey
Sandipan Dey

Reputation: 23129

Your implementation will also work if you correct one line: insstead of calling meteorites() twice, call just once.

x = np.zeros(1000)
y = np.zeros(1000)
for i in range(1000):
    x[i], y[i] = meteorites()
plt.scatter(x,y)
plt.show()  

enter image description here

Upvotes: 3

Martijn Pieters
Martijn Pieters

Reputation: 1125258

Take a random angle and a random distance between the two constraints; you'll need to produce a uniform distribution in a circle:

from math import sin, cos, radians, pi, sqrt

def meteorites():
    angle = uniform(0, 2 * pi)  # in radians
    distance = sqrt(uniform(4, 12.25))
    return distance * cos(angle), distance * sin(angle)

Upvotes: 6

MBo
MBo

Reputation: 80325

To get uniform distribution of random point in the ring, one should take relative areas of thin circular regions into account. How it works for the circle enter image description here

For your case generate uniform distribution of SquaredR in range of squared inner and outer radii. Pseudocode:

 Fi  = RandomUniform(0, 2 * Pi)
 SquaredR  = RandomUniform(inner*inner, outer*outer)
 R = Sqrt(SquaredR)
 x,y = R * Cos(Fi), R * Sin(Fi)

Upvotes: 8

Edward van Kuik
Edward van Kuik

Reputation: 1387

I would also rather run through a loop that picks a random angle and a random distance within your ring range. Then calculate the coords from that.

But in your code the first problem is see is that should write:

x[i],y[i] = meteorites()

instead of

x[i] = meteorites()[0] 
y[i] = meteorites()[1]

In your example, you're called meteorites() twice resulting in the x and y two different meteorites.

Upvotes: 2

Blckknght
Blckknght

Reputation: 104852

You're getting random points that don't fall on your ring because these two lines don't do what you want:

x[i] = meteorites()[0]
y[i] = meteorites()[1]

These assign an x value from one point on the ring to x[i], and the y value from a different point on the ring to y[i]. You get coordinates from different points because you're calling meteorites() twice.

Instead, you probably want to call the function once, and then assign to each coordinate, or do an assignment with iterable-unpacking where both targets are on the left side of the equals sign:

x[i], y[i] = meteorites()

Upvotes: 5

keepitwiel
keepitwiel

Reputation: 142

You could try the following to generate 1000 samples using numpy:

import numpy 
n = 1000
phi = numpy.random.uniform(0, 2*numpy.pi, n)
r = numpy.random.uniform(2, 3.5, n)

Then x, y coordinates can be constructed as follows using the transformation from radial to cartesian coordinates:

x = r * numpy.cos(phi)
y = r * numpy.sin(phi)

This shows the power of numpy, as x and y are now arrays without needing to iterate over n.

Upvotes: 1

Related Questions