我有一个数据框,有两列是点的坐标。 如果某个点位于特定位置,我需要用特定值填充一列(全部为无)。该位置和标签存储在另一个df中
这不容易解释,但我希望通过一个例子可以清楚地说明: DF1
latitude longitude LABEL
0 1.3 2.7 None
1 3.5 3.6 None
2 2.8 3.0 None
3 9.7 1.9 None
4 6.2 5.7 None
5 1.7 3.4 None
6 3.5 1.4 None
7 2.7 6.6 None
8 1.7 2.7 None
9 1.3 1.3 None
DF2
minlat maxlat minlong maxlong STRING
0 1.0 2.0 1.0 3.0 AAA
1 3.0 4.0 1.0 2.0 BBB
2 3.0 4.0 3.0 4.0 CCC
3 5.0 7.0 2.0 3.0 DDD
最终结果是:
latitude longitude LABEL
0 1.3 2.7 AAA
1 3.5 3.6 CCC
2 2.8 3.0 None
3 9.7 1.9 None
4 6.2 5.7 None
5 1.7 3.4 None
6 3.5 1.4 BBB
7 2.7 6.6 None
8 1.7 2.7 AAA
9 1.3 1.3 AAA
目前的代码是:
for i in range(len(df2)-1):
DF1.loc[(DF1['latitude']>=DF2.loc[i:i,'minlat'].at[i]) & (DF1['latitude']<DF2.loc[i:i,'maxlat'].at[i]) &
(DF1['longitude']>=DF2.loc[i:i,'minlong'].at[i]) & (DF1['longitude']<DF2.loc[i:i,'maxlong'].at[i]),'LABEL'] = DF2.loc[i:i,'STRING'].at[i]
屏幕以获得更好的缩进:
因此,对于DF2的每一行,我检查DF1的值是否在中间,并分配一个字符串
但是像这样需要很多时间。你对我能做什么有什么建议吗? 我的问题是,必须用DF2的每一行检查数字_1的每个值,而不仅仅是用具有相同索引的行
编辑:我正在尝试其他方法:
(二)
for i in range(len(xlsx_fact_maneuver_specialareas)-1):
minLat=DF2.loc[i:i,'minLat'].at[i]
maxLat=DF2.loc[i:i,'maxLat'].at[i]
minLong=DF2.loc[i:i,'maxLat'].at[i]
maxLong=DF2.loc[i:i,'maxLong'].at[i]
DF1.loc[(DF1['latitude']>=minLat) & (DF1['latitude']<maxLat) &
(DF1['longitude']>=minLong) & (DF1['longitude']<maxLong),'LABEL'] = DF2.loc[i:i,'STRING'].at[i]
这让我在本地感觉不太好,但当我在机器上尝试时,感觉更好
及
for i in range(len(xlsx_fact_maneuver_specialareas)-1):
minLat=DF2.loc[i:i,'minLat'].at[i]
maxLat=DF2.loc[i:i,'maxLat'].at[i]
minLong=DF2.loc[i:i,'maxLat'].at[i]
maxLong=DF2.loc[i:i,'maxLong'].at[i]
DF1 = DF1.assign(
label = np.select(
[(DF1['latitude']>=minLat) & (DF1['latitude']<maxLat) & (DF1['longitude']>=minLong) & (DF1['longitude']<maxLong)],
[DF2.loc[i:i,'STRING'].at[i]],
[None]))
这让我在本地感觉更好,但在机器上感觉更差
这个问题并不特别适合在Pandas本身中解决,因为没有简单的原语来处理您需要进行的计算。 更好的方法是转移到NumPy或Numba域,在较低级别上解决问题
我将提供生成最后一列的函数,假设将最后一列复制到数据帧中相对容易
最初的做法是:
这将为最后一列生成正确的结果。 (OP中提出的其他方法要么不相关,要么仅对仅使用一次的数量使用独立赋值,要么我没有设法让它们工作)
一种相对简单的方法涉及广播,在@PierreD answer中介绍,可以进一步简化为:
在假设每个位置只属于一个区域的情况下,这可以稍微简化:
但是,有大量不必要的内存分配和比较正在进行。 一种更快的方法是使用Numba显式循环,您可以显式添加短路。该守则的内容如下:
使用稍微干净但在其他方面具有可比性的输入:
在所有情况下都可以获得预期输出:
虽然产生代表问题的任意大的输入并不容易,但天真的时间安排表明Numba方法要快得多:
将此操作矢量化的一个解决方案是使用Numpy及其出色的广播功能。这为中小型数据帧提供了一个快速的解决方案,但它会随着
mask
的O[n*m]
(对于df1
的n
行和df2
行的m
行的mask
而增长(在时间和内存上),因此最终对于大型数据帧来说速度会变慢解释
关键部分是
mask
的构造。有必要对其进行细分,以了解其机制及其如何使用Numpy的广播:如您所见,上面将
a
和vmin
之间的所有比较扩展到第三维。然后我们用逻辑“所有第三轴(经度和纬度)都必须为真”投射回2D:以上表示高于
df2.iloc[j]
最小值的所有点df1.iloc[i]
为...[i, j]
我们对
vmax
做了同样的处理,得到的mask
是df1.iloc[i]
的所有点都在df2.iloc[j]
的边界框中接下来的两位是
has_any
和first
。前者表示df1
中的哪些点至少位于一个边界框中。后者是第一个这样的边界框(如df2
中的索引)其余的都是不言自明的
注释
请注意,这使用了
O[n*m]
比较(对于df1
的n
行和df2
的m
行),这对于大型矩阵来说可能太慢(尽管因为它是矢量化的,所以对于中型矩阵来说速度非常快)对于大型矩阵,更好的方法包括排序或使用KD树。见this other answer
这是另一个答案,这个答案使用了^{} 。如果边界框之间没有太多重叠,并且它们的“半径”分布也不太“随意”(
radius.max() / np.median(radius)
不太大,例如在1和2之间),则该方法特别有效它适用于p-范数1(曼哈顿)或2(欧几里德),尽管在实践中{}更快,因为平均每个点看到的候选点更少(圆的总面积小于钻石的总面积)
为什么这么快KD-trees非常适合处理这类问题。它们通过沿维度和中点在每个节点上分割空间来划分空间。一旦构建了它们,由于它们提供了分而治之的方法,查询它们的速度很快
关键功能如下:
在调用此函数之前,我们为每个边界框计算一个“半径”,该半径是对角线p范数的一半。然后,我们使用总体最大半径
r
作为KDTree
查询的最大距离。KDTree
查询(kd_points.query_ball_tree()
)有效地筛选所有边界框中心,并在一次调用中查找半径内的所有边界框中心。这是实际匹配的超集,但速度很快,大大减少了搜索空间。然后,过滤实际上在边界框中的点,并跟踪每个点的(第一个)匹配边界框作为优化(这里没有实现),我们可以考虑边界框的大小(^ {< CD8>}数组)。如果判断差异太大,则可以将边界框分为两组(例如围绕
np.median(radius)
),并且可以对每一半进行相同的搜索(如果必要,再次递归)对于OP示例,在准备以更易于使用的形式(所有Numpy数组)获取中心、边界框和半径后,该查询返回:
使用曼哈顿范数可以得到类似的结果(在本例中,相同):
下图中绘制了这两种解决方案,其中突出显示了所有候选边界框及其中心距离
r
内的实际面积:请注意,在这两种情况下,点
#2
是如何错误地包含在bbox#2
中的。第一步,使用KD树,只是一个过滤步骤。下一步是检查每个点的候选是否实际包含该点。使用该筛选(用于ok
数组的表达式),解决方案是:让我们看看如果我们有更多的点和边界框(可能有重叠),会发生什么:
对于
df1, df2 = gen_example(32, 12, 0)
,相应的图片为:候选项(查询结果,此处为
p=2
)为:因为这是一个锯齿状数组,所以我们将其转换为带有
fill_value=-1
的矩形数组:对于上面的填充阵列,这将提供:
现在,我们迭代这个数组的列,但是以贪婪的方式从以前的迭代中删除任何成功的匹配项(这就是
remaining
的原因)处理预处理等的其他功能包括:
关于OP示例:
速度测试
现在进行速度测试,其中点数
n
和边界框数m
都较大:与@norok2's numba solution的速度比较(稍微调整以跟随OP的列名):
这是该数据的24倍加速。
验证我们是否得到相同的解决方案:
相关问题 更多 >
编程相关推荐