如何使用 Python、Numpy 和 Shapely 生成落在多边形内的随机和非重复点?
How to generate random and non duplicate points that fall within a polygon using Python, Numpy, and Shapely?
我一直在使用 Python、Shapely、Numpy 和 Geopandas 生成点密度图。点密度地图点根据种族有不同的颜色,因此可以了解整体城市人口统计数据。
我一直在使用一些类似于函数 found in this answer 的代码来生成落在多边形内的点。该函数采用一个多边形和一个整数(不同种族的人数)并运行一个 while 循环以创建落在该多边形内的随机点。它为此使用 Numpy uniform
。
这是地理数据框的样子
这是我 运行 创建点的代码:
pts_per_person = 5
epsg = 4326
seed = 10
list_of_point_categories = []
for field in ['white_pop','black_pop','hispanic_pop', 'asian_pop', 'amerindian_pop', 'other_race_pop', 'two_or_more_races_pop']:
ps = gpd.GeoDataFrame(gen_points_in_gdf_polys(geometry = gdf['geometry'], values=gdf[field],
points_per_value = pts_per_person, seed=seed))
ps['ethnicity'] = field
ps['year'] = i
list_of_point_categories.append(ps)
all_points=gpd.GeoDataFrame(pd.concat(list_of_point_categories))
all_points=all_points.reset_index(drop=True)
函数如下:
def gen_random_points_poly(poly, num_points, seed = None):
"""
Returns a list of N randomly generated points within a polygon.
"""
min_x, min_y, max_x, max_y = poly.bounds
points = []
i=0
while len(points) < num_points:
s=RandomState(seed+i) if seed else RandomState(seed)
random_point = Point([s.uniform(min_x, max_x), s.uniform(min_y, max_y)])
if random_point.within(poly):
points.append(random_point)
i+=1
return points
def gen_points_in_gdf_polys(geometry, values, points_per_value = None, seed = None):
"""
Take a GeoSeries of Polygons along with a Series of values and returns randomly generated points within
these polygons. Optionally takes a "points_per_value" integer which indicates the number of points that
should be generated for each 1 value.
"""
if points_per_value:
new_values = (values/points_per_value).astype(int)
else:
new_values = values
new_values = new_values[new_values>0]
#print(new_values.size)
if(new_values.size > 0):
g = gpd.GeoDataFrame(data = {'vals':new_values}, geometry = geometry)
a = g.apply(lambda row: tuple(gen_random_points_poly(row['geometry'], row['vals'], seed)),1)
b = gpd.GeoSeries(a.apply(pd.Series).stack(), crs = geometry.crs)
b.name='geometry'
return b
但我发现我最终得到了每个种族的多个重复点。纬度和经度值完全相同。
重复的点相互堆叠。我不得不将 s.uniform
行更改为 random_point = Point([s.uniform(min_x, max_x) + round(random.uniform(.0001, .001),10), s.uniform(min_y, max_y) + round(random.uniform(.0001, .001),10)])
以使其真正随机。这具有更随机地分散点数的预期效果,没有重复。
但是这感觉有点不对劲,就像我没有正确使用 .uniform
一样。这是在多边形内创建随机点的正确方法吗?
问题似乎出在 gen_random_points_poly()
函数中 while
循环的第一行。这一行写着:
s=RandomState(seed+i) if seed else RandomState(seed)
这会在循环的每次迭代中初始化随机数生成器。初始化取决于 seed
的值(固定的)和 i
的值(在循环的每次迭代中递增)。实际上,如果多次调用 gen_random_points_poly()
函数,那么每次生成的点序列都将完全相同。这就是为什么不同种族产生的点数集合是完全一样的。
如果您想获得可重现的结果,您可以在 gen_random_points_poly()
函数之外创建一次 RandomState
对象。或者,您可以在每次调用此函数时提供不同的种子。在后一种情况下,在 while
循环之前只创建一次 RandomState
对象比在每次迭代中重复创建它更有效。
我一直在使用 Python、Shapely、Numpy 和 Geopandas 生成点密度图。点密度地图点根据种族有不同的颜色,因此可以了解整体城市人口统计数据。
我一直在使用一些类似于函数 found in this answer 的代码来生成落在多边形内的点。该函数采用一个多边形和一个整数(不同种族的人数)并运行一个 while 循环以创建落在该多边形内的随机点。它为此使用 Numpy uniform
。
这是地理数据框的样子
这是我 运行 创建点的代码:
pts_per_person = 5
epsg = 4326
seed = 10
list_of_point_categories = []
for field in ['white_pop','black_pop','hispanic_pop', 'asian_pop', 'amerindian_pop', 'other_race_pop', 'two_or_more_races_pop']:
ps = gpd.GeoDataFrame(gen_points_in_gdf_polys(geometry = gdf['geometry'], values=gdf[field],
points_per_value = pts_per_person, seed=seed))
ps['ethnicity'] = field
ps['year'] = i
list_of_point_categories.append(ps)
all_points=gpd.GeoDataFrame(pd.concat(list_of_point_categories))
all_points=all_points.reset_index(drop=True)
函数如下:
def gen_random_points_poly(poly, num_points, seed = None):
"""
Returns a list of N randomly generated points within a polygon.
"""
min_x, min_y, max_x, max_y = poly.bounds
points = []
i=0
while len(points) < num_points:
s=RandomState(seed+i) if seed else RandomState(seed)
random_point = Point([s.uniform(min_x, max_x), s.uniform(min_y, max_y)])
if random_point.within(poly):
points.append(random_point)
i+=1
return points
def gen_points_in_gdf_polys(geometry, values, points_per_value = None, seed = None):
"""
Take a GeoSeries of Polygons along with a Series of values and returns randomly generated points within
these polygons. Optionally takes a "points_per_value" integer which indicates the number of points that
should be generated for each 1 value.
"""
if points_per_value:
new_values = (values/points_per_value).astype(int)
else:
new_values = values
new_values = new_values[new_values>0]
#print(new_values.size)
if(new_values.size > 0):
g = gpd.GeoDataFrame(data = {'vals':new_values}, geometry = geometry)
a = g.apply(lambda row: tuple(gen_random_points_poly(row['geometry'], row['vals'], seed)),1)
b = gpd.GeoSeries(a.apply(pd.Series).stack(), crs = geometry.crs)
b.name='geometry'
return b
但我发现我最终得到了每个种族的多个重复点。纬度和经度值完全相同。
重复的点相互堆叠。我不得不将 s.uniform
行更改为 random_point = Point([s.uniform(min_x, max_x) + round(random.uniform(.0001, .001),10), s.uniform(min_y, max_y) + round(random.uniform(.0001, .001),10)])
以使其真正随机。这具有更随机地分散点数的预期效果,没有重复。
但是这感觉有点不对劲,就像我没有正确使用 .uniform
一样。这是在多边形内创建随机点的正确方法吗?
问题似乎出在 gen_random_points_poly()
函数中 while
循环的第一行。这一行写着:
s=RandomState(seed+i) if seed else RandomState(seed)
这会在循环的每次迭代中初始化随机数生成器。初始化取决于 seed
的值(固定的)和 i
的值(在循环的每次迭代中递增)。实际上,如果多次调用 gen_random_points_poly()
函数,那么每次生成的点序列都将完全相同。这就是为什么不同种族产生的点数集合是完全一样的。
如果您想获得可重现的结果,您可以在 gen_random_points_poly()
函数之外创建一次 RandomState
对象。或者,您可以在每次调用此函数时提供不同的种子。在后一种情况下,在 while
循环之前只创建一次 RandomState
对象比在每次迭代中重复创建它更有效。