我正在计算点和多个线段之间的测地距离。每个线段都有唯一的识别号。我希望从距离函数返回距离,使它们本质上绑在一起。我还希望保持功能,如对距离进行排序,并使用标签或位置对其进行索引,并返回距离数据和标签。像带有索引的Pandas系列之类的东西,但是我不能使用系列,因为数据被返回到Pandas DataFrame中,然后扩展系列并使其变得混乱。这是一个例子:
In [1]: '''Note that all this happens inside an apply function of a Pandas Series'''
labels = [25622, 25621, 25620, 25619, 25618]
dist = vect_dist_funct(pt, labels) #vect_dist_funct does the computations, and returns distances in meters
dist
Out[1]: array([296780.2217658355, 296572.4476883276, 296364.21166884096,
296156.4366241771, 295948.6610171968], dtype=object)
然而,我想要的是这样的字典,标签和距离固有地相互联系:
{25622 : 296780.2217658355,
25621 : 296572.4476883276,
25620 : 296364.21166884096,
25619 : 296156.4366241771,
25618 : 295948.6610171968}
但现在我失去了价值观的功能。我不能轻易地对它们进行排序,或者比较它们,或者任何东西。我看了Numpy Structured Arrays,它们似乎可行,但是如果我无法对距离进行排序,并得到最近段的索引,那对我来说就没那么大了。我可以使用其他任何数据类型吗?
长篇故事和背景
我正在尝试进行空间连接。通过在RTree中搜索(example),我得到了一个点最有可能更接近的段的索引。这些是标签中的索引。然后,我查看线几何表,找到所选标签的线几何,并计算每个线段的点距离。
后续步骤涉及检查空间连接的完整性。在某些情况下,最近的不是最佳连接候选者,并且需要在其他参数上评估连接。因此,我的计划是从最近的部分向外工作。其中包括对距离进行排序,获取最近段的索引,然后使用该索引查看段表并提取该行的其他属性以供检查。如果可以确认匹配,则接受所述段,否则拒绝该算法,并且算法将移动到下一个最接近的段。
执行所有这些操作的数据类型是我正在寻找的,而不会破坏计算它的段之间的距离。
使用Pandas的问题
这就是实际调用函数的方式:
joined = points['geometry'].apply(pointer, centroid=line['centroid'], tree_idx=tree_idx))
然后在pointer
内,会发生这种情况:
def pointer(point, centroid, tree_idx):
intersect = list(tree_idx.intersection(point.bounds))
if len(intersect) > 0:
points = pd.Series([point.coords[0]]*len(intersect)).values
polygons = centroid.loc[intersect].values
dist = vect_dist_funct(points, polygons)
return pd.Series(dist, index=intercept, name='Dist').sort_values()
else:
return pd.Series(np.nan, index=[0], name='Dist')
然后,joined
看起来像这样:
这是因为不计算所有点(行是点)和所有行(列是行)之间的距离。这太昂贵了(4M点,每个状态180k行,整个数据集50个状态)。此外,与返回两个Numpy数组时相比,此生成joined
的DataFrame合并操作会将运行时间增加7倍。返回两个Numpy数组的问题是,保持距离和线ID始终保持一致并不容易。
Points,Lines,tree_idx
的示例请注意,这是列和行中的截断数据集。我只包括相关列,而不包括其他数据:
分:
geometry
id
88400001394219 0.00 POINT (-105.2363291 39.6988139)
0.25 POINT (-105.2372017334178 39.69899060448157)
0.50 POINT (-105.2380177896182 39.69933953105642)
0.75 POINT (-105.2387202141595 39.69988447162143)
1.00 POINT (-105.2393222 39.7005405)
88400002400701 0.00 POINT (-104.7102833 39.8318348)
0.25 POINT (-104.7102827 39.831966625)
0.50 POINT (-104.7102821 39.83209845)
0.75 POINT (-104.7102815 39.832230275)
1.00 POINT (-104.7102809 39.8323621)
所以这基本上是线上的插值点。行id是索引的第一级,第二级是插值点的百分比。这形成了第一个数据集,即我想从第二个数据集中带来一些属性的数据集。
行:
geometry centroid
id
71345 POLYGON ((-103.2077992965318 40.58026765162965... (-103.20073265160862, 40.576450381964975)
71346 POLYGON ((-103.2069505830457 40.58155121711739... (-103.19987394433825, 40.57774903464972)
71347 POLYGON ((-103.2061017677045 40.58283487609803... (-103.19901204453959, 40.57905245493993)
71348 POLYGON ((-103.2052000154291 40.58419853220472... (-103.19815200508097, 40.58035300329024)
71349 POLYGON ((-103.2043512639656 40.58548197865339... (-103.19729445792181, 40.58164972491414)
71350 POLYGON ((-103.2035025651746 40.5867652936463,... (-103.1964362470977, 40.5829473948391)
71351 POLYGON ((-103.2026535431035 40.58804903349249... (-103.19557847342394, 40.58424434094705)
71352 POLYGON ((-103.201804801526 40.58933229190573,... (-103.19472966696722, 40.58552767098465)
71353 POLYGON ((-103.2009557884142 40.59061590473365... (-103.19388484652855, 40.58680427447224)
71354 POLYGON ((-103.2001001699726 40.59190793446012... (-103.19303392095904, 40.5880882237994)
这是第二个数据集的一部分(本答案开头提到的标签是此数据集的索引)。目标是以智能方式将属性从此数据集传输到点数据集。第一步是找到每个点的最近线。然后我将比较点数据集中的一些属性和线数据集,并确认或拒绝连接,就像我提到的那样。
tree_idx:
使用以下代码创建tree_idx:
import rtree
lines_bounds = lines['geometry'].apply(lambda x: x.bounds)
tree_idx = rtree.index.Index()
for i in lines_bounds.index:
tree_idx.insert(i, lines_bounds.loc[i])
答案 0 :(得分:1)
所以我认为您的整体问题是您正在创建一个DataFrame
,其中列标签是intercept
值。我想你想要做的是创建一个DataFrame
,其中一列包含截距值,而另一列包含距离。我将尝试为您提供我认为有用的代码,但是如果没有原始数据就很难确定,因此您需要对其进行一些修改才能使其完美运行。
首先,我会修改vect_dist_funct
所以如果第一个参数是标量,它会创建正确长度的列表,如果第二个参数为空,则返回NaN
。
接下来,我将所有有用的值作为列添加到DataFrame中:
points['intersect'] = points['geometry'].apply(lambda x: np.array(tree_idx.intersection(x.bounds)))
points['polygons'] = points['intersect'].apply(lambda x: centroid.loc[x].values)
points['coords0'] = points['geometry'].apply(lambda x: x.coords[0])
points['dist'] = points.apply(lambda x: vect_dist_funct(x.coords0, x.polygons), axis=1)
这将为您提供一个包含所有距离的列。如果你真的希望可以访问截距值,那么你可以创建一个只有截距和距离的DataFrame
,然后将截距作为另一个多索引级别来避免过多的NaN
值:
pairs = points.apply(lambda x: pd.DataFrame([x['intersect'], x['dist']], index=['intersect', 'dist']).T.stack(), axis=1)
pairs = pairs.stack(level=0).set_index('intersect', append=True)
pairs.index = pairs.index.droplevel(level=2)
这应该给你一个Series
,其中第一个索引是id,第二个是百分比,第三个是交叉,值是距离。
答案 1 :(得分:0)
所以,我认为索引是标签的数据框可能是最简单的
distances = {25622 : 296780.2217658355,
25621 : 296572.4476883276,
25620 : 296364.21166884096,
25619 : 296156.4366241771,
25618 : 295948.6610171968}
df = pd.DataFrame([tup for tup in distances.items()],columns=["label", "dist"]).sort_values('dist').set_index('label')
df
输出:
dist
label
25618 295948.661017
25619 296156.436624
25620 296364.211669
25621 296572.447688
25622 296780.221766
然后,如果您想按标签名称访问距离
df.loc[25620]
Out:
dist 296364.211669
Name: 25620, dtype: float64
然后,如果你想找到标签附近'那一点,您可以使用
获取行号row_num = df.index.get_loc(25620)
print(row_num)
Out: 2
然后你可以访问"附近"点df.iloc[row_number]
df.iloc[3]
Out:
dist 296572.447688
Name: 25621, dtype: float64
这涵盖了你需要的一切吗?
答案 2 :(得分:0)
在完成所有事情后,在尝试使TheBlackCat的答案工作约3小时后,我决定使用xarray。所以现在pointer
函数看起来像这样:
def pointer(point, centroid, tree_idx):
intersect = list(tree_idx.intersection(point.bounds))
if len(intersect) > 0:
points = pd.Series([point.coords[0]]*len(intersect)).values
polygons = centroid.loc[intersect].values
dist = vect_dist_funct(points, polygons)
sorter = np.argsort(dist)
return xr.DataArray(dist[sorter], [('dim0', np.asarray(intersect)[sorter])])
else:
return xr.DataArray(np.nan)
完成。这符合我的需要。我有距离和它们一起计算的段ID,这样一个转换就会影响另一个。并且距离仍然可以操作,xarray
还为我提供了分组,合并等方面的高级功能。
此外,在一个州的0.1%数据上运行大约需要一分钟,对于10%的数据需要10分钟。因此,我预计100%的数据大约是100分钟。但老实说,即使一个州需要3个小时,我仍然可以在一天内完成所有50个州(在16核服务器上使用多线程)。所以我暂时对此很满意。感谢我得到的所有建议。特别是@TheBlackCat,@ michael_j_ward和@hpaulj。