Python 如何快速从geodataframe中删除相同的几何图形?
在使用itertools.combinations()进行for循环期间,我对项目执行geodataframe修改。我检查是否没有重复的几何图形,在我的例子中是LineString。如果是,我将删除其中一个 pandasdrop_duplicates()方法在这里不起作用,因为我们处理的是具有点坐标的空间几何体,这些点坐标可能分布不同,但由相同的线串表示Python 如何快速从geodataframe中删除相同的几何图形?,python,numpy,geopandas,Python,Numpy,Geopandas,在使用itertools.combinations()进行for循环期间,我对项目执行geodataframe修改。我检查是否没有重复的几何图形,在我的例子中是LineString。如果是,我将删除其中一个 pandasdrop_duplicates()方法在这里不起作用,因为我们处理的是具有点坐标的空间几何体,这些点坐标可能分布不同,但由相同的线串表示 import geopandas import itertools gdf = geopandas.read_file('example.g
import geopandas
import itertools
gdf = geopandas.read_file('example.geojson')
for a, b in itertools.combinations(gdf.geometry, 2):
if a.equals(b) == True:
try:
gdf.drop(gdf[gdf['geometry'] == b].index.values, inplace=True)
except:
continue
gdf.to_file('example.geojson', driver='GeoJSON')
这段代码运行得很好,但我的文件可能非常大。对于大型文件,执行时间非常长。有没有更快的方法
我在考虑使用numpy将所有几何图形存储在一个矩阵中。但是我不知道如何在矩阵上使用带有numpy的equals()方法(from)。谢谢你的帮助
来自示例.geojson
的geodataframe如下所示:
geometry
0 LINESTRING (42.70275 9.94481, 42.70030 9.94783)
1 LINESTRING (42.70030 9.94783, 42.70275 9.94481)
2 LINESTRING (42.70275 9.94481, 42.69700 9.97133)
3 LINESTRING (42.69700 9.97133, 42.70275 9.94481)
4 LINESTRING (42.60179 10.34216, 42.70030 9.94783)
...
解决方案。
这是一个受@DanielKonstantinov solution和重复数据消除启发的完整解决方案。我们将一个geodataframe作为输入,并返回修改后的geodataframe
你能上传一个小的示例.geojson
文件来使用吗?gdf.geometry是否可以散列?你会用努尼克吗?通过检查每个组合来消除重复将是非常低效的,因为计算机科学,加上你正在修改你正在迭代的容器。@KennyOstrom谢谢。但是没有nunique
不起作用,因为我想它们是几何体。例如,表中的前两个LineString对于pandas是不同的,但从地理空间的角度来看是相同的,因为它们具有相同的两个坐标。您需要一个可哈希类型,以便可以使用集合。我从“pip install geopandas”中得到了一个错误,所以我只想指出一个集合提供了O(n)重复检测,而组合提供了O(n^2)。另外,当您在容器上进行迭代时,不要修改它——将其作为生成新集合或其他内容的生成器。非常感谢!它工作得很好,而且速度更快。我学到了很多。在deduplicate deletate
中,我猜x
中的take_沿_轴是数据
。我在帖子中上传了这个问题的全局解决方案。@Tim我很高兴能帮上忙!修正了一个打字错误。当你有时间的时候,你能编辑一下标题吗?我想主要的问题不是加速iTertools的组合。在我看来,你在评论中提到的要重要得多。对我来说,理解如何使用像您这样的结构是一个挑战,尤其是如何对它们进行排序。谢谢你的提问)是的,你是对的!我改变了标题,即使不是那样,我想还是更好。总的来说,我现在可以用numpy处理我的表,并避免使用pandas方法,numpy真的令人印象深刻。
import geopandas
import numpy as np
from shapely.geometry import LineString
def solution(frame: geodataframe) -> geodataframe:
linestring = frame.geometry
coordinates = [list(x.coords) for x in linestring]
matrix = np.array(coordinates)
result = deduplicate(matrix)
final_result = [list(map(tuple, pair)) for pair in result.tolist()]
lines = [{'geometry': LineString(pair)} for pair in final_result]
output = geopandas.GeoDataFrame(lines)
return output
import numpy as np
def deduplicate(geo_data: np.ndarray # shape == (N, 4)
) -> np.ndarray: # deduplicated data with origin order
data = geo_data.reshape(-1, 2, 2)
dt = f'f{data.itemsize}' # f4 or f8
data = data.view([('x', dt), ('y', dt)])
# eliminate differences
ixs = np.argsort(data, -2, order=('x', 'y'))
data_no_df = np.take_along_axis(data, ixs, axis=-2) # sorted by 'x' then by 'y'
# get unique
unique_sorted_data, uni_ixs = np.unique(data_no_df, True, axis=0)
uni_ixs.sort() # inplace sort 1d-array
data_deduplicated = geo_data[uni_ixs] # unique, originally ordered and shaped
return data_deduplicated
def _test():
geo_data = np.array([[42.70275, 9.94481, 42.7003 , 9.94783],
[42.7003 , 9.94783, 42.70275, 9.94481],
[42.70275, 9.94481, 42.697 , 9.97133],
[42.697 , 9.97133, 42.70275, 9.94481],
[42.60179, 10.34216, 42.7003 , 9.94783]])
data_deduplicated = deduplicate(geo_data)
print(data_deduplicated)
>>> _test()
[[42.70275 9.94481 42.7003 9.94783]
[42.70275 9.94481 42.697 9.97133]
[42.60179 10.34216 42.7003 9.94783]]
large_data = np.random.randint(0, 10, size=(1000, 4)).astype('d')
%timeit deduplicate(large_data)
1.98 ms ± 9.37 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)