How to generate random latitude and longitude using Python 3 random module? I already googled and read documentation and not found a way to do this.
The problem when using uniform distributions for both the latitude and the longitude
is that physically, the latitude is NOT uniformly distributed.
So if you plan to use these random points for something like some statiscal averaging computation,
or a physics Monte-Carlo simulation, the results will risk being incorrect.
And if you plot a graphical representation of the “uniform” random points, they will seem to cluster in the polar regions.
To picture that, consider on planet Earth the zone that lies between 89 and 90 degrees of latitude (North).
The length of a degree of latitude is 10,000/90 = 111 km. That zone is a circle of radius 111 km,
centered around the North Pole. Its area is about 3.14 * 111 * 111 ≈ 39,000 km2
On the other hand, consider the zone that lies between 0 and 1 degree of latitude.
This is a strip whose length is 40,000 km (the Equator) and whose width is 111 km,
so its area is 4.44 millions km2. Much larger than the polar zone.
A simple algorithm:
A possibility is to use Gaussian-distributed random variables, as provided by the Python library.
If we build a 3D vector whose 3 components have Gaussian distributions, the overall
probability distribution is like
exp(-x2) * exp(-y2) * exp(-z2)
but this is the same thing as exp(-(x2 + y2 + z2))
or exp(-r2), where r is the distance from the origin.
So these vectors have no privileged direction. Once normalized to unit length, they are uniformly
distributed on the unit sphere. They solve our problem with the latitude distribution.
The idea is implemented by the following Python code:
import math
import random
def randlatlon1():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
# get a random Gaussian 3D vector:
gx = random.gauss(0.0, 1.0)
gy = random.gauss(0.0, 1.0)
gz = random.gauss(0.0, 1.0)
# normalize to an equidistributed (x,y,z) point on the unit sphere:
norm2 = gx*gx + gy*gy + gz*gz
norm1 = 1.0 / math.sqrt(norm2)
x = gx * norm1
y = gy * norm1
z = gz * norm1
radLat = math.asin(z) # latitude in radians
radLon = math.atan2(y,x) # longitude in radians
return (round(cf*radLat, 5), round(cf*radLon, 5))
A sanity check:
Euclidean geometry provides a formula for the probability of a spherical zone
defined by minimal/maximal latitude and longitude. The corresponding Python code is like this:
def computeProbaG(minLat, maxLat, minLon, maxLon):
pi = math.pi
rcf = pi / 180.0 # degrees to radians Correction Factor
lonProba = (maxLon - minLon) / 360.0
minLatR = rcf * minLat
maxLatR = rcf * maxLat
latProba = (1.0/2.0) * (math.sin(maxLatR) - math.sin(minLatR))
return (lonProba * latProba)
And we can also compute an approximation of that same probability by random sampling, using
the random points provided by a function such as randlatlon1, and counting what
percentage of them happen to fall within the selected zone:
def computeProbaR(randlatlon, ranCount, minLat, maxLat, minLon, maxLon):
norm = 1.0 / ranCount
pairs = [randlatlon() for i in range(ranCount)]
acceptor = lambda p: ( (p[0] > minLat) and (p[0] < maxLat) and
(p[1] > minLon) and (p[1] < maxLon) )
selCount = sum(1 for p in filter(acceptor, pairs))
return (norm * selCount)
Equipped with these two functions, we can check for various geometric parameter sets
that the geometric and probabilistic results are in good agreement, with ranCount set to one million random points:
ranCount = 1000*1000
print (" ")
probaG1 = computeProbaG( 30, 60, 45, 90)
probaR1 = computeProbaR(randlatlon1, ranCount, 30, 60, 45, 90)
print ("probaG1 = %f" % probaG1)
print ("probaR1 = %f" % probaR1)
print (" ")
probaG2 = computeProbaG( 10, 55, -40, 160)
probaR2 = computeProbaR(randlatlon1, ranCount, 10, 55, -40, 160)
print ("probaG2 = %f" % probaG2)
print ("probaR2 = %f" % probaR2)
print (" ")
Execution output:
$ python3 georandom.py
probaG1 = 0.022877
probaR1 = 0.022852
probaG2 = 0.179307
probaR2 = 0.179644
$
So the two sort of numbers appears to agree reasonably here.
Addendum:
For the sake of completeness, we can add a second algorithm which is less intuitive but derives from a wider statistical principle.
To solve the problem of the latitude distribution, we can use the Inverse Transform Sampling theorem. In order to do so, we need some formula for the probability of the latitude to be less than an arbitrary prescribed value, φ.
The region of the unit 3D sphere whose latitude is less than a given φ is known as a spherical cap. Its area can be obtained thru elementary calculus, as described here for example.
The spherical cap area is given by formula: A = 2π * (1 + sin(φ))
The corresponding probability can be obtained by dividing this area by the overall area of the unit 3D sphere, that is 4π, corresponding to φ = φmax = π/2. Hence:
p = Proba{latitude < φ} = (1/2) * (1 + sin(φ))
Or, conversely:
φ = arcsin (2*p - 1)
From the Inverse Transform Sampling theorem, a fair sampling of the latitude (in radians) is obtained by replacing the probability p by a random variable uniformly distributed between 0 and 1. In Python, this gives:
lat = math.asin(2*random.uniform(0.0, 1.0) - 1.0)
As for the longitude, this is an independent random variable that is still uniformly distributed between -π and +π (in radians). So the overall Python sampler code is:
def randlatlon2r():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
u0 = random.uniform(0.0, 1.0)
u1 = random.uniform(0.0, 1.0)
radLat = math.asin(2*u0 - 1.0) # angle with Equator - from +pi/2 to -pi/2
radLon = (2*u1 - 1) * pi # longitude in radians - from -pi to +pi
return (round(radLat*cf,5), round(radLon*cf,5))
This code has been found to pass successfully the sanity check as described above.
Generate a random number between
Latitude: -85 to +85 (actually -85.05115 for some reason)
Longitude: -180 to +180
As #tandem wrote in his answer, the range for latitude is almost -90 to +90 (it is cut on maps) and for longitude it is -180 to +180. To generate random float numbers in this range use random.uniform function:
import random
# returns (lat, lon)
def randlatlon():
return (round(random.uniform( -90, 90), 5),
round(random.uniform(-180, 180), 5))
It is rounded to 5 digits after comma because that extra accuracy is unnecessary.
Related
I have estimates for data in units m2 at gridsquare resolution. I need to calculate the number of m2 in each latitude / longitude grid cell?
Cell sizes are much smaller near the poles than at the equator so this is important.
I would like a netcdf file or array of the number of square meters in each grid square.
In case anyone would like a netcdf of the number of square meters in each lat long grid cell.
This is probably not the cleanest solution, but will create a netcdf (earth_m2.nc) of m2 in each grid using xarray.
The gridsize() function is adapted from another stack overflow question.
We can then make a dummy array and create a earth field of m2s using the longitude distances at each location.
"""
This will create a global grid of the approximate size of each grid square.
"""
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
def gridsize(lat1):
#https://en.wikipedia.org/wiki/Haversine_formula
#https://stackoverflow.com/questions/639695/how-to-convert-latitude-or-longitude-to-meters/11172685#11172685
lon1=200
import math
lat2=lat1
lon2=lon1+1
R = 6378.137 # // Radius of earth in km
dLat = lat2 * np.pi / 180 - lat1 * np.pi / 180
dLon = lon2 * np.pi / 180 - lon1 * np.pi / 180
a = np.sin(dLat/2) * np.sin(dLat/2) + np.cos(lat1 * np.pi / 180) * np.cos(lat2 * np.pi / 180) * np.sin(dLon/2) * np.sin(dLon/2)
c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a))
d = R * c
return d * 1000 #; // meters
boxlo,boxla=np.array(np.meshgrid(np.arange(-179.5,179.5,1),np.arange(-89.5,89.5,1)))
sizes=np.ones(boxlo.shape)
grid=gridsize(boxla)
grid_nc=xr.DataArray(grid,coords={'lat':boxla[:,1],'lon':boxlo[1,:]},dims=['lat','lon'])
lat_size=110567 #in m
grid_nc['m2']=grid_nc*lat_size
grid_nc=grid_nc['m2']
grid_nc.to_netcdf('earth_m2.nc')
plt.pcolormesh(boxlo[1,:],boxla[:,1],grid_nc)
plt.colorbar()
plt.show()
By spherical trigonometry, the surface area of a triangle on a sphere of radius R with 1 vertex at the North pole (latitude: π/2) and 2 vertices at the same latitude -π/2 < x < π/2 separated (longitudinally) by d radians is
S(x, d) = (cos⁻¹((cos(b) - cos²(a))/sin²(a)) + 2cos⁻¹((cos(a) - cos(a)cos(b))/(sin(a)sin(b))) - π)R² where a = R(π/2 - x) and b = Rd
So, the surface area of a grid rectangle on a sphere of radius R between lines of longitude separated by d radians and latitudes x₁ > x₂ is
S(x₂, d) - S(x₁, d)
One option is to transform your cells into a coordinate reference system (CRS) that has units in, say, meters rather than degrees. Then the area calculation is simple.
I assume your coordinates are in WGS84.
For the target CRS there are choices especially if you know the locality of the points, but a common collection of global CRSs like this are Universal Transverse Mercator (UTM), or near the poles Universal Polar Stereographic
For example, for UTM, assuming a list of points of the form [lon, lat] where the last point is equal to the first:
import pyproj
from shapely.geometry import Polygon
from shapely.ops import transform
def utm_epsg(lon: float, lat: float) -> int:
"""
Return the UTM EPSG code for the given lon-lat.
"""
offset = int(round((183 + lon) / 6.0))
return 32600 + offset if lat > 0 else 32700 + offset
for lat in range(-79, 83):
for lon in range(-179, 179):
polygon = Polygon([
[lon, lat],
[lon+1, lat],
[lon+1, lat+1],
[lon, lat+1],
[lon, lat],
])
src_crs = pyproj.CRS.from_epsg(4326)
tgt_crs = pyproj.CRS.from_epsg(utm_epsg(polygon.centroid.x, polygon.centroid.y))
project = pyproj.Transformer.from_crs(src_crs, tgt_crs, always_xy=True).transform
utm_polygon = transform(project, polygon)
# aggregate into some result. Here just printed to stdout.
print(polygon.centroid, utm_polygon.area)
It's worth noting that UTM isn't defined south of 80°S and north of 84°N.
How would I calculate the area below an EarthSatellite so that I can plot the swath of land covered as the satellite passes over?
Is there anything in Skyfield that would facilitate that?
Edit: Just thought I'd clarify what I mean by area below the satellite. I need to plot the maximum area below the satellite possible to observe given that the Earth is a spheroid. I know how to plot the satellite path, but now I need to plot some lines to represent the area visible by that satellite as it flies over the earth.
Your edit made it clear what you want. The visible area from a satellite can be easily calculated (when the earth is seen as a sphere). A good source to get some background on the visible portion can be found here. To calculate the visible area when the earth is seen as an oblate spheroid will be a lot harder (and maybe even impossible). I think it's better to reform that part of the question and post it on Mathematics.
If you want to calculate the visible area when the earth is seen as a sphere we need to make some adjustments in Skyfield. With a satellite loaded using the TLE api you can easily get a sub point with the position on earth. The library is calling this the Geocentric position, but actually it's the Geodetic position (where the earth is seen as an oblate spheroid). To correct this we need to adjust subpoint of the Geocentric class to use the calculation for the Geocentric position and not the Geodetic position. Due to a bug and missing information in the reverse_terra function we also need to replace that function. And we need to be able to retrieve the earth radius. This results in the following:
from skyfield import api
from skyfield.positionlib import ICRF, Geocentric
from skyfield.constants import (AU_M, ERAD, DEG2RAD,
IERS_2010_INVERSE_EARTH_FLATTENING, tau)
from skyfield.units import Angle
from numpy import einsum, sqrt, arctan2, pi, cos, sin
def reverse_terra(xyz_au, gast, iterations=3):
"""Convert a geocentric (x,y,z) at time `t` to latitude and longitude.
Returns a tuple of latitude, longitude, and elevation whose units
are radians and meters. Based on Dr. T.S. Kelso's quite helpful
article "Orbital Coordinate Systems, Part III":
https://www.celestrak.com/columns/v02n03/
"""
x, y, z = xyz_au
R = sqrt(x*x + y*y)
lon = (arctan2(y, x) - 15 * DEG2RAD * gast - pi) % tau - pi
lat = arctan2(z, R)
a = ERAD / AU_M
f = 1.0 / IERS_2010_INVERSE_EARTH_FLATTENING
e2 = 2.0*f - f*f
i = 0
C = 1.0
while i < iterations:
i += 1
C = 1.0 / sqrt(1.0 - e2 * (sin(lat) ** 2.0))
lat = arctan2(z + a * C * e2 * sin(lat), R)
elevation_m = ((R / cos(lat)) - a * C) * AU_M
earth_R = (a*C)*AU_M
return lat, lon, elevation_m, earth_R
def subpoint(self, iterations):
"""Return the latitude an longitude directly beneath this position.
Returns a :class:`~skyfield.toposlib.Topos` whose ``longitude``
and ``latitude`` are those of the point on the Earth's surface
directly beneath this position (according to the center of the
earth), and whose ``elevation`` is the height of this position
above the Earth's center.
"""
if self.center != 399: # TODO: should an __init__() check this?
raise ValueError("you can only ask for the geographic subpoint"
" of a position measured from Earth's center")
t = self.t
xyz_au = einsum('ij...,j...->i...', t.M, self.position.au)
lat, lon, elevation_m, self.earth_R = reverse_terra(xyz_au, t.gast, iterations)
from skyfield.toposlib import Topos
return Topos(latitude=Angle(radians=lat),
longitude=Angle(radians=lon),
elevation_m=elevation_m)
def earth_radius(self):
return self.earth_R
def satellite_visiable_area(earth_radius, satellite_elevation):
"""Returns the visible area from a satellite in square meters.
Formula is in the form is 2piR^2h/R+h where:
R = earth radius
h = satellite elevation from center of earth
"""
return ((2 * pi * ( earth_radius ** 2 ) *
( earth_radius + satellite_elevation)) /
(earth_radius + earth_radius + satellite_elevation))
stations_url = 'http://celestrak.com/NORAD/elements/stations.txt'
satellites = api.load.tle(stations_url)
satellite = satellites['ISS (ZARYA)']
print(satellite)
ts = api.load.timescale()
t = ts.now()
geocentric = satellite.at(t)
geocentric.subpoint = subpoint.__get__(geocentric, Geocentric)
geocentric.earth_radius = earth_radius.__get__(geocentric, Geocentric)
geodetic_sub = geocentric.subpoint(3)
print('Geodetic latitude:', geodetic_sub.latitude)
print('Geodetic longitude:', geodetic_sub.longitude)
print('Geodetic elevation (m)', int(geodetic_sub.elevation.m))
print('Geodetic earth radius (m)', int(geocentric.earth_radius()))
geocentric_sub = geocentric.subpoint(0)
print('Geocentric latitude:', geocentric_sub.latitude)
print('Geocentric longitude:', geocentric_sub.longitude)
print('Geocentric elevation (m)', int(geocentric_sub.elevation.m))
print('Geocentric earth radius (m)', int(geocentric.earth_radius()))
print('Visible area (m^2)', satellite_visiable_area(geocentric.earth_radius(),
geocentric_sub.elevation.m))
I have an algorithm in which I need to work out the signed angle (-180 to 180) between edges in a graph. I've done some research and found plenty of specific answers but can't figure out how to relate them to my situation (e.g. this question which uses atan2, however the OP wanted only positive angles)
I've tried implementing a few different ways (using atan2 or arccos) but I'm struggling to relate the examples to my specific problem. I've tried treating the edges as vectors but got strange results.
Given a graph with points (A, B, C, D, E), and the average of those points (avg)... how do I find the signed angle between one of those points (e.g. A) and the other points (e.g. B, C, D, E), taking the angle from the current origin (A) to the 'avg' point as equal to 0 degrees. Example below...
...in this example, the anti-clockwise angle from (A, avg) to (A, B) would be positive something (between 0 and 180), and the angle from (A, avg) to (A, E) would be negative something (between 0 and -180).
Ideally I want a formula which I could also apply to defining any of the points as the origin, for example taking point C as the origin.. the 'zero angle' would be (C, avg) and the angle between (C, avg) and (C, A) would be negative (0 to -180) and the angle between (C, avg) and (C, E) would be positive (0 to 180).
I haven't studied math beyond high-school so I find it hard to decipher equations with symbols I don't understand.
UPDATE: Thought I'd clean this up to make it more obvious what the conclusion was.
I made two small changes to the accepted answer, resulting in the below snippet:
def angle(vertex, start, dest):
AhAB = math.atan2((dest.y - vertex.y), (dest.x - vertex.x))
AhAO = math.atan2((start.y - vertex.y), (start.x - vertex.x))
AB = AhAB - AhAO
# in between 0-math.pi = do nothing, more than math.pi = +(-2 * math.pi), less than zero = do nothing
AB = math.degrees(AB + (-2 * math.pi if AB > math.pi else (2 * math.pi if AB < 0 - math.pi else 0)))
return AB
...the final one-liner may be a bit much to grok after a few months of not working on this, so I turned it into it's own function, taking the result of AB = AhAB - AhAO as it's argument...
def calc(ab):
if ab > math.pi:
return ab + (-2 * math.pi)
else:
if ab < 0 - math.pi:
return ab + (2 * math.pi)
else:
return ab + 0
I thought this was a little clearer to read, though more lines.
The final function in full:
def angle(vertex, start, dest):
"""Calculates the signed angle between two edges with the same origin.
Origin is the 'vertex' argument, 'start' is the bounding point of the edge to calculate the angle from.
Positively signed result means anti-clockwise rotation about the vertex."""
def calc_radians(ab):
if ab > math.pi:
return ab + (-2 * math.pi)
else:
if ab < 0 - math.pi:
return ab + (2 * math.pi)
else:
return ab + 0
AhAB = math.atan2((dest.y - vertex.y), (dest.x - vertex.x))
AhAO = math.atan2((start.y - vertex.y), (start.x - vertex.x))
res = calc_radians(AhAB - AhAO)
return math.degrees(res)
Note: The function assumes the three arguments will all be instances of a typical Point class with x and y attributes.
Also, the example graph above has only positive values, but I am fairly sure that this works with graphs that involve negative values too.
I read your problem statement as follows: given 2 points A and B, and a center O, find the angle A to B as the angle, positive if anticlockwise, between the vectors A→O and A→B.
If my premises are correct, then you can
find the angle between A→B and a horizontal, rightward line passing in A,
find the angle between A→O and a horizontal, rightward line passing in A,
find the angle A to B as the difference of said angles,
normalize the result range so that it's between -π and +π.
What I've said can be visualized as follows
or in code (assuming a Point class with attributes x and y)
AhAB = math.atan2((B.y-A.y), (B.x-A.x)) # -π < AhAB ≤ +π
AhAO = math.atan2((O.y-A.y), (O.x-A.x)) # -π < AhA) ≤ +π
AB = AhAB - AhAO # -2π < AB ≤ +2π
AB = AB + ( 2*math.pi if AB < math.pi else (-2*math.pi if AB> math.pi else 0))
Addendum
Here it is a small code example, the position of the points is just similar to what you can see in the picture
In [18]: from math import atan2, pi
In [21]: class Point():
...: def __init__(self, x, y):
...: self.x, self.y = x, y
...: def __repr__(self):
...: return '(%s, %s)'%(self.x, self.y)
In [22]: A = Point(0.0, 0.0)
In [23]: B = Point(-2.0, 2.0)
In [24]: O = Point(0.0, -3.0)
In [25]: AhAB = atan2((B.y-A.y), (B.x-A.x)) ; print(3/4, AhAB/pi)
0.75 0.75
In [26]: AhAO = atan2((O.y-A.y), (O.x-A.x)) ; print(-1/2, AhAO/pi)
-0.5 -0.5
In [27]: AB = AhAB - AhAO ; print(5/4, AB/pi)
1.25 1.25
In [28]: AB = AB + ( 2*pi if AB < pi else (-2*pi if AB> pi else 0)) ; print(AB/pi)
-0.75
In [29]:
The last line normalize your result AB to be in the correct range -π < AB ≤ π, adding or subtracting 2π that doesn't change the meaning of the measured angle.
The definition of positive and negative angles is heavily depending on the reference system or reference point. Despite of its 'correct' definition, the basic calculation can be pretty much done based on the slope between two points and the resulting angle of incline which can be calculated by applying the inverse tan to the slope.
Applying the inverse tan in programming can be a bit annoying since many programming languages provide two different functions for this:
arctan or atan which is implemented in Python's math.atan() or numpy.atan()
arctan2 or atan2 which is delivered by math.atan2() or numpy.atan2()
Both of these functions, regardless of the implementation in the math module or numpy package, return the calculated angle in radians which is basically based on the number Pi instead of degrees which makes some further conversion necessary. This can either be done manually or by applying a function like numpy.rad2deg(). To get a basic idea of the data points and to get some eye-balled estimation for the calculated results, I suggest plotting the data point by using matplotlib.
Glueing all the before-mentioned considerations into code can look like this:
import pandas as pd
import matplotlib
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
# Define some sample data points
coords = {
'A': (1.5, 3.0),
'B': (3.0, 5.0),
'C': (5.5, 4.5),
'D': (5.8, 2.2),
'E': (2.8, 1.2)
}
# Extract data values from `coords` dict
values = np.array(list(coords.values()))
# Calculate the averaged point of all data points
avg = np.mean(values, axis=0)
# Plot sample data for better overview
for k, v in coords.items():
plt.plot(*v, marker='o', linestyle='')
plt.text(*v, k)
plt.plot(*avg, marker='o', linestyle='')
plt.text(*avg, 'avg')
plt.show()
# For further information about slope and angle of incline
# see Wikipedia (https://en.wikipedia.org/wiki/Slope).
# Calculating the angle from `avg` to each point. Please adopt
# to your own needs if needed for other pairs of points.
# Calculate the distance in x- and y-direction from each point to point `avg`
distances_x_y = (values - avg)
# Depending on your definition of the 'reference point' consider using
# distances_x_y = (avg - values)
# For further explanation on `atan` and `atan2` see
# https://stackoverflow.com/q/35749246/3991125 and
# https://en.wikipedia.org/wiki/Atan2 .
# Using a for loop instead of numpy's array/vectors is not very elegant,
# but easy to understand and therefore has potential for improvements.
# Calculate angle from point `avg` to each other point based on distances
angle_radians = np.array([np.arctan2(element[1], element[0]) for element in distances_x_y])
# since `.arctan2()` or `.arctan()` return the angle in radians,
# we need to convert to degrees
angle_degrees = np.rad2deg(angle_radians)
# print results
print(angle_degrees)
If you consider the coordinates x0=xavg-xA, y0=yavg-yA and x=xPoint-xA,y=yPoint-yA, the formula f(x,y) gives the signed angle that is positive as counter clockwise.
f(x,y)=pi()/2*((1+sign(x0))* (1-sign(y0^2))-(1+sign(x))* (1-sign(y^2)))
+pi()/4*((2+sign(x0))*sign(y0)-(2+sign(x))*sign(y))
+sign(x0*y0)*atan((abs(x0)-abs(y0))/(abs(x0)+abs(y0)))
-sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
Considering Gaussian shape we can find the volume of n dimensional volume of a sphere. My intention is to find the volume using Monte Carlo method.
Using the gaussian integral I have found the formula
What I understand that the ratio of the points inside the n-dimensional sphere to the total number of points will then be roughly the same as the ratio of the volume of the ball to that of the cube. I mean the mass density would never change , whatever dimension I use.
Therefore I assume I should follow the same technique what I used to find the value of pi using Monte Carlo method.
I don't understand how to follow the code which I evaluated to find the value of pi.
import random
TIMES_TO_REPEAT = 10**5
LENGTH = 10**5
def in_circle(x, y):
return x**2 + y**2 < LENGTH**2
inside_count = 0
for _ in range(TIMES_TO_REPEAT):
point = random.randint(0,LENGTH), random.randint(0,LENGTH)
if in_circle(*point):
inside_count += 1
pi = (inside_count / TIMES_TO_REPEAT) * 4
print(pi)
How can I apply the inequality condition in the code I have mentioned so the mass density would be same and I can find the value of volume in Higher dimension.?
import random
N = 10**5 # number of trials (ie, number of points to sample)
R = 10**5 # circle radius
def in_sphere(x, y, z):
return x**2 + y**2 + z**2 < R**2
c = 0
for _ in range(N):
p = random.randint(0,R), random.randint(0,R), random.randint(0,R)
if in_sphere(*p):
c += 1
pi = 6 * c / N
print(pi)
The basic idea: if a circle (radius R) is inscribed inside a square (then, its edge must be 2R), then the ratio (the area of circle over the area of square) will be π/4. So, if you pick N points at random inside the square, approximately N * π/4 of those points should fall inside the circle.
hope the annotated/revised code help you understand the MC logic
import random
N = 10**5 # number of trials (ie, number of points to sample)
R = 10**5 # circle radius
# whether p(x,y) is inside a circle
def in_circle(x, y):
return x**2 + y**2 < R**2
# use integer ops as much as possible for speed
c = 0
for i in range(N):
x, y = random.randint(0,R), random.randint(0,R)
if in_circle(x, y):
c += 1
pi = 4 * c / N
print(pi) # pi-> 3.14
I am working with some points in spherical coordinates. I need to generate new points as the error points for them and a kind of offset for the old points.
The new point should be in a specific distance from the old one which distributing by gaussian distribution. The angle of new point compared to old one is not important.I am trying to generate new points for r direction. no matter what are phi and theta (Spherical coordinates)
To generate the new point distributing by gaussian function, I tried the numpy.rand.normal(mean,std,..). But It is generating 1D random points over mean value and this mean value is a real number. In my case I need an approach to specify the position of the old point and I have one given standard deviation for this distance from the original points.
Honesty, I dont have a copy of my code. It is on the university's server. But let's assume I have an array of size 100*3 including the spherical (or cartesian) coordinates of some points on a surface of a cylinder. In spherical case, the first column presents the radius value, the second column is theta and third one shows the phi for the points. now I want to generate random points from them using gaussian distribution. there is a given standard deviation for the gaussian distribution. The only important thing is that the new points generated by gaussian distribution are limited in r value. No matter the position of points in term of theta and phi.
When I tried numpy.rand.normal(mean,std,..), this generate some random points over the mean value. It does not help me. I want new points over my old ones with the given STD.
any idea would be appreciated.
This is a code, similar to mine written By Ophion How to generate regular points on cylindrical surface
def make_cylinder(radius, length, nlength, alpha, nalpha, center, orientation):
#Create the length array
I = np.linspace(0, length, nlength)
#Create alpha array avoid duplication of endpoints
#Conditional should be changed to meet your requirements
if int(alpha) == 360:
A = np.linspace(0, alpha, num=nalpha, endpoint=False)/180*np.pi
else:
A = np.linspace(0, alpha, num=nalpha)/180*np.pi
#Calculate X and Y
X = radius * np.cos(A)
Y = radius * np.sin(A)
#Tile/repeat indices so all unique pairs are present
pz = np.tile(I, nalpha)
px = np.repeat(X, nlength)
py = np.repeat(Y, nlength)
points = np.vstack(( pz, px, py )).T
#Shift to center
shift = np.array(center) - np.mean(points, axis=0)
points += shift
#Orient tube to new vector
#Grabbed from an old unutbu answer
def rotation_matrix(axis,theta):
a = np.cos(theta/2)
b,c,d = -axis*np.sin(theta/2)
return np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
ovec = orientation / np.linalg.norm(orientation)
cylvec = np.array([1,0,0])
if np.allclose(cylvec, ovec):
return points
#Get orthogonal axis and rotation
oaxis = np.cross(ovec, cylvec)
rot = np.arccos(np.dot(ovec, cylvec))
R = rotation_matrix(oaxis, rot)
return points.dot(R)
now calling the function:
points = make_cylinder(3, 5, 5, 360, 10, [0,2,0], [1,0,0])
sigma = 0.5 # given STD
ossfet_points = numpy.random.normal(np.mean(point[:,0]), sigma, size=(n,3))
If I'm not mistaken, you want random points on a spherical manifold with a gaussian distribution of distances from the center. If so, then you have the latter problem solved by sampling gaussian values of the radius using numpy.rand.normal
To get random spherical points is a little bit more tricky, but here's some code to do it (and a description of the math behind it at Wolfram MathWorld):
import numpy as np
num_points = 500
U = np.random.random(num_points)
V = np.random.random(num_points)
import math as m
def spherical_to_cartesian(vec):
'''
Convert spherical polar coordinates to cartesian coordinates:
See the definition of spherical_cartesian_to_polar.
#param vec: A vector of the 3 polar coordinates (r, u, v)
#return: (x, y, z)
'''
(r, u, v) = vec
x = r * m.sin(u) * m.cos(v)
y = r * m.sin(u) * m.sin(v)
z = r * m.cos(u)
return [x, y, z]
radius = 1.
points = np.array([spherical_to_cartesian([radius, 2 * np.pi * u, np.arccos(2*v - 1)]) for u,v in zip(U,V)])
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax = Axes3D(fig)
ax.plot(points[:,0], points[:,1], points[:,2], 'o')
Which will give you points like this:
Now if you want them to have normally distributed radii, you just need to substitute your randomly generated values in the list comprehension which uses the variable radius like this:
radii = np.random.normal(10, 3, 100)
points = np.array([spherical_to_cartesian([r, 2 * np.pi * u, np.arccos(2*v - 1)]) for r,u,v in zip(radii, U,V)])
Is this more or less what you're looking for?