确定一个点在哪个多边形中,然后将该多边形的名称作为新列应用于大型 pandas 数据框
Determine in which polygon a point is, and then apply the name of that polygon as a new column to a large pandas dataframe
我有一个大型数据框,其中包含来自世界各地各种船只的位置数据。 imoNo
是船号。以下是数据框的示例:
这是重现它的代码:
# intialise data of lists.
ships_data = {'imoNo':[9321483, 9321483, 9321483, 9321483, 9321483],
'Timestamp':['2020-02-22 00:00:00', '2020-02-22 00:10:00', '2020-02-22 00:20:00', '2020-02-22 00:30:00', '2020-02-22 00:40:00'],
'Position Longitude':[127.814598, 127.805634, 127.805519, 127.808548, 127.812370],
'Position Latitude':[33.800232, 33.801899, 33.798885, 33.795799, 33.792931]}
# Create DataFrame
ships_df = pd.DataFrame(ships_data)
我需要做的是在数据框的末尾添加一列,该列将标识 sea_name
船只航行的位置。像下面这样的东西:
为了到达那里,我在 this link (IHO Sea Areas v3) 找到了一个 .shp 格式的数据集,如下所示:
所以,我的方法是遍历船舶数据集的每个(经度,纬度),检查其中的那对多边形,最后 return 匹配的海洋名称多边形。这是我的代码:
### Load libraries
import numpy as np
import pandas as pd
import geopandas as gp
import shapely.speedups
from shapely.geometry import Point, Polygon
shapely.speedups.enable()
### Check and map lon lat pair with sea name
def get_seaname(long,lat):
pnt = Point(long,lat)
for i,j in enumerate(iho_df.geometry):
if pnt.within(j):
return iho_df.NAME.iloc[i]
### Apply the above function to the dataframe
ships_df['sea_name'] = ships_df.apply(lambda x: get_seaname(x['Position Longitude'], x['Position Latitude']), axis=1)
但是,这是一个非常耗时的过程。我在我的 Mac 上本地测试了 ships_df 的前 1000 行,运行 花了大约 1 分钟。如果它线性增长,那么整个数据集我将需要大约 14 天 :-D.
任何优化上述功能的想法将不胜感激。
谢谢!
试试这个,
用apply在每个经纬度上做点(无法实现更快的方法,欢迎帮忙)
import numpy as np
import pandas as pd
import geopandas as gp
import shapely.speedups
from shapely.geometry import Point, Polygon
shapely.speedups.enable()
# I am still uncomfortable with this. More ideas on speeding up this part are welcome
ships_df['point'] = ships_df.apply(lambda x: Point(x['Position Longitude'], x['Position Latitude']), axis=1)
现在将您的函数矢量化以处理 Point
def get_seaname(pnt:Point):
for i,j in enumerate(iho_df.geometry):
if pnt.within(j):
return iho_df.NAME.iloc[i]
既然您的方法适用于单个点,请将点列转换为 Point 对象的向量并向量化您的方法
get_seaname = np.vectorize(get_seaname)
ships_df['sea_name'] = pd.Series(get_seaname(ships_df['point'].values))
终于,我有比最初问题更快的东西了。
首先,我使用来自 IHO 海域数据集的信息创建了一个描述边界框的多边形
# Create a bbox polygon
iho_df['bbox'] = iho_df.apply(lambda x: Polygon([(x['min_X'], x['min_Y']), (x['min_X'], x['max_Y']), (x['max_X'], x['max_Y']), (x['max_X'], x['min_Y'])]), axis=1)
然后,我更改了函数,以便首先查看 bbox(这比 geometry 快很多,因为它只是一个矩形)。当一个点落在多个框内(对于边界海域)时,然后并且只有在那时它才会查看初始多边形以在匹配框(而不是在所有多边形内)中找到正确的海洋名称。
# Function that checks and maps lon lat pair with sea name
def get_seaname(long,lat):
pnt = Point(long,lat)
names = []
# Check within each bbox first to note the polygons to look at
for i,j in enumerate(iho_df.bbox):
if pnt.within(j):
names.append(iho_df.NAME.iloc[i])
# Return nan for no return
if len(names)==0:
return np.nan
# Return the single name of the sea
elif len(names)==1:
return names[0]
# Run the pnt.within() only for the polygons within the collected sea names
else:
limitizez_df = iho_df[iho_df['NAME'].isin(names)].reset_index(drop=True)
for k,n in enumerate(limitizez_df.geometry):
if pnt.within(n):
return limitizez_df.NAME.iloc[k]
这个大大缩短了时间。为了进一步提升它,我使用多处理在 CPU 核心之间进行并行化。这个想法来自另一个 Whosebug post 我现在不记得了,但这是代码。
import multiprocessing as mp
# Function that parallelizes the apply function among the cores of the CPU
def parallelize_dataframe(df, func, n_cores):
df_split = np.array_split(df, n_cores)
pool = Pool(n_cores)
df = pd.concat(pool.map(func, df_split))
pool.close()
pool.join()
return df
# Function that adds a sea_name column in the main dataframe
def add_features(df):
# Apply the function
df['sea_name'] = df.apply(lambda x: get_seaname(x['Position Longitude'], x['Position Latitude']), axis=1)
return df
最后,我没有使用 get_seaname() 的应用函数,而是将它用于 parallelize_dataframe() 函数 运行 所有可用的 CPU核心数:
### Apply the above function to the dataframe
ships_df = parallelize_dataframe(ships_df, add_features, n_cores=mp.cpu_count())
我希望我的解决方案也能帮助到其他人!
我有一个大型数据框,其中包含来自世界各地各种船只的位置数据。 imoNo
是船号。以下是数据框的示例:
# intialise data of lists.
ships_data = {'imoNo':[9321483, 9321483, 9321483, 9321483, 9321483],
'Timestamp':['2020-02-22 00:00:00', '2020-02-22 00:10:00', '2020-02-22 00:20:00', '2020-02-22 00:30:00', '2020-02-22 00:40:00'],
'Position Longitude':[127.814598, 127.805634, 127.805519, 127.808548, 127.812370],
'Position Latitude':[33.800232, 33.801899, 33.798885, 33.795799, 33.792931]}
# Create DataFrame
ships_df = pd.DataFrame(ships_data)
我需要做的是在数据框的末尾添加一列,该列将标识 sea_name
船只航行的位置。像下面这样的东西:
为了到达那里,我在 this link (IHO Sea Areas v3) 找到了一个 .shp 格式的数据集,如下所示:
所以,我的方法是遍历船舶数据集的每个(经度,纬度),检查其中的那对多边形,最后 return 匹配的海洋名称多边形。这是我的代码:
### Load libraries
import numpy as np
import pandas as pd
import geopandas as gp
import shapely.speedups
from shapely.geometry import Point, Polygon
shapely.speedups.enable()
### Check and map lon lat pair with sea name
def get_seaname(long,lat):
pnt = Point(long,lat)
for i,j in enumerate(iho_df.geometry):
if pnt.within(j):
return iho_df.NAME.iloc[i]
### Apply the above function to the dataframe
ships_df['sea_name'] = ships_df.apply(lambda x: get_seaname(x['Position Longitude'], x['Position Latitude']), axis=1)
但是,这是一个非常耗时的过程。我在我的 Mac 上本地测试了 ships_df 的前 1000 行,运行 花了大约 1 分钟。如果它线性增长,那么整个数据集我将需要大约 14 天 :-D.
任何优化上述功能的想法将不胜感激。
谢谢!
试试这个,
用apply在每个经纬度上做点(无法实现更快的方法,欢迎帮忙)
import numpy as np
import pandas as pd
import geopandas as gp
import shapely.speedups
from shapely.geometry import Point, Polygon
shapely.speedups.enable()
# I am still uncomfortable with this. More ideas on speeding up this part are welcome
ships_df['point'] = ships_df.apply(lambda x: Point(x['Position Longitude'], x['Position Latitude']), axis=1)
现在将您的函数矢量化以处理 Point
def get_seaname(pnt:Point):
for i,j in enumerate(iho_df.geometry):
if pnt.within(j):
return iho_df.NAME.iloc[i]
既然您的方法适用于单个点,请将点列转换为 Point 对象的向量并向量化您的方法
get_seaname = np.vectorize(get_seaname)
ships_df['sea_name'] = pd.Series(get_seaname(ships_df['point'].values))
终于,我有比最初问题更快的东西了。
首先,我使用来自 IHO 海域数据集的信息创建了一个描述边界框的多边形
# Create a bbox polygon
iho_df['bbox'] = iho_df.apply(lambda x: Polygon([(x['min_X'], x['min_Y']), (x['min_X'], x['max_Y']), (x['max_X'], x['max_Y']), (x['max_X'], x['min_Y'])]), axis=1)
然后,我更改了函数,以便首先查看 bbox(这比 geometry 快很多,因为它只是一个矩形)。当一个点落在多个框内(对于边界海域)时,然后并且只有在那时它才会查看初始多边形以在匹配框(而不是在所有多边形内)中找到正确的海洋名称。
# Function that checks and maps lon lat pair with sea name
def get_seaname(long,lat):
pnt = Point(long,lat)
names = []
# Check within each bbox first to note the polygons to look at
for i,j in enumerate(iho_df.bbox):
if pnt.within(j):
names.append(iho_df.NAME.iloc[i])
# Return nan for no return
if len(names)==0:
return np.nan
# Return the single name of the sea
elif len(names)==1:
return names[0]
# Run the pnt.within() only for the polygons within the collected sea names
else:
limitizez_df = iho_df[iho_df['NAME'].isin(names)].reset_index(drop=True)
for k,n in enumerate(limitizez_df.geometry):
if pnt.within(n):
return limitizez_df.NAME.iloc[k]
这个大大缩短了时间。为了进一步提升它,我使用多处理在 CPU 核心之间进行并行化。这个想法来自另一个 Whosebug post 我现在不记得了,但这是代码。
import multiprocessing as mp
# Function that parallelizes the apply function among the cores of the CPU
def parallelize_dataframe(df, func, n_cores):
df_split = np.array_split(df, n_cores)
pool = Pool(n_cores)
df = pd.concat(pool.map(func, df_split))
pool.close()
pool.join()
return df
# Function that adds a sea_name column in the main dataframe
def add_features(df):
# Apply the function
df['sea_name'] = df.apply(lambda x: get_seaname(x['Position Longitude'], x['Position Latitude']), axis=1)
return df
最后,我没有使用 get_seaname() 的应用函数,而是将它用于 parallelize_dataframe() 函数 运行 所有可用的 CPU核心数:
### Apply the above function to the dataframe
ships_df = parallelize_dataframe(ships_df, add_features, n_cores=mp.cpu_count())
我希望我的解决方案也能帮助到其他人!