【问题标题】:How to quickly remove identical geometries from a geodataframe?如何从地理数据框中快速删除相同的几何图形?
【发布时间】:2021-07-22 05:48:02
【问题描述】:

我在 for 循环中使用 itertools.combinations() 对项目执行地理数据框修改。我检查没有重复的几何图形,在我的例子中是 LineString。如果是,我删除其中一个。

pandas drop_duplicates() 方法在这里不起作用,因为我们正在处理点坐标的空间几何,这些点坐标可能分布不同,但由相同的 LineString 表示。

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() 方法(来自shapely)。感谢您的帮助。

来自example.geojson 的地理数据框看起来像:

                                               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 解决方案和deduplicate。我们将 GeoDataFrame 或 GeoSeries 作为输入,然后将其修改后返回。

import numpy as np
import geopandas as gpd
from shapely.geometry import LineString

def solution(frame):
    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 = [LineString(pair) for pair in final_result]
    return gpd.GeoSeries(lines)

【问题讨论】:

  • 你能上传一个小的example.geojson文件吗?
  • gdf.geometry 是否可散列?你能用nunique吗?通过检查每个组合来消除重复项将是极其低效的,因为计算机科学,而且您正在修改您正在迭代的容器。
  • @KennyOstrom 谢谢。但是没有nunique 不起作用,因为它们是几何图形,我猜。例如,表中的前两个 LineString 对于 pandas 是不同的,但从地理空间的角度来看是相同的,因为它们具有相同的坐标。
  • 你需要一个可散列类型,这样你就可以使用一个集合。我从“pip install geopandas”得到一个错误,所以我只想指出一个集合提供 O(n) 重复检测,而组合提供 O(n^2)。另外,在迭代容器时不要修改容器——让它成为生成新集合的生成器,或其他东西。

标签: python numpy geopandas


【解决方案1】:
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)

【讨论】:

  • 非常感谢!它工作得很好,而且真的更快。我学到了很多东西。 deduplicate 我猜 take_along_axis 中的 xdata。我在帖子中上传了该问题的全局解决方案。
  • @Tim 很高兴能帮上忙!修正了一个错字。有时间可以修改一下标题吗?我猜主要问题不是加速 itertools.combinations。在我看来,您在评论中提到的内容更为重要。理解如何使用像你这样的结构对我来说是一个挑战,尤其是如何对它们进行排序。感谢您提出这个问题)
  • 是的,你是对的!我改变了标题,即使它不完全是,我认为它更好。一般来说,我现在可以用 numpy 处理我的表并避免使用 pandas 方法,numpy 确实令人印象深刻。
猜你喜欢
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
相关资源
最近更新 更多