【问题标题】:split lines based on names根据名称分割线
【发布时间】:2017-12-27 08:32:38
【问题描述】:

我有一个 GeoPandas 数据框,它是从 shapefile 对象创建的。 但是,某些线路名称相同,但位置却大不相同。

我希望每一行都有一个唯一的名称! 因此,我需要以某种方式分割线,如果它们在几何上分开并重命名它们。

可以尝试计算所有街道块之间的距离,并在它们靠近时重新组合它们。

距离的计算可以在 Geopandas 中轻松完成:Distance Between Linestring Geopandas

一组要尝试的行:

from shapely.geometry import Point, LineString
import geopandas as gpd


line1 = LineString([
    Point(0, 0),
    Point(0, 1),
    Point(1, 1),
    Point(1, 2),
    Point(3, 3),
    Point(5, 6),
])

line2 = LineString([
    Point(5, 3),
    Point(5, 5),
    Point(9, 5),
    Point(10, 7),
    Point(11, 8),
    Point(12, 12),
])

line3 = LineString([
    Point(9, 10),
    Point(10, 14),
    Point(11, 12),
    Point(12, 15),
])

df = gpd.GeoDataFrame(
    data={'name': ['A', 'A', 'A']},
    geometry=[line1, line2, line3]
)

【问题讨论】:

标签: python python-3.x pandas geolocation geopandas


【解决方案1】:

一种可能的方法是使用每个数据点的空间聚类。以下代码使用 DBSCAN,但也许其他类型更适合。以下是它们如何工作的概述:http://scikit-learn.org/stable/modules/clustering.html

from matplotlib import pyplot as plt
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler

import numpy as np
import pandas as pd
import geopandas as gpd

df = gpd.GeoDataFrame.from_file("stackex_dataset.shp")

df 的每一行都是一些点。我们想把它们全部取出来获得集群:

ids = []
coords = []

for row in df.itertuples():
    geom = np.asarray(row.geometry)

    coords.extend(geom)
    ids.extend([row.id] * geom.shape[0])

我们在这里需要 id 来在计算后将集群恢复为 df。 这是获取每个点的聚类(我们还进行了数据归一化以获得更好的质量):

clust = DBSCAN(eps=0.5)
clusters = clust.fit_predict(StandardScaler().fit_transform(coords))

下一部分有点混乱,但我们想确保每个 id 只得到一个集群。我们为每个 id 选择最频繁的点簇。

points_clusters = pd.DataFrame({"id":ids, "cluster":clusters})
points_clusters["count"] = points_clusters.groupby(["id", "cluster"])["id"].transform('size')

max_inds = points_clusters.groupby(["id", "cluster"])['count'].transform(max) == points_clusters['count']
id_to_cluster = points_clusters[max_inds].drop_duplicates(subset ="id").set_index("id")["cluster"]

然后我们将集群编号返回到我们的数据框中,以便我们可以在此编号的帮助下枚举我们的街道。

df["cluster"] = df["id"].map(id_to_cluster)

对于 DBSCAN 和 eps=0.5 的数据(您可以使用此参数 - 它是使它们在一个集群中的最大距离。eps 越多,您获得的集群越少),我们有这种图片:

plt.scatter(np.array(coords)[:, 0], np.array(coords)[:, 1], c=clusters, cmap="autumn")
plt.show()

而独立街道的数量是8:

print(len(df["cluster"].drop_duplicates()))

如果我们制作较低的 eps,例如clust = DBSCAN(eps=0.15) 我们得到更多的簇(此时为 12 个),从而更好地分离数据:

关于杂乱的代码部分:在源 DataFrame 中我们有 170 行,每一行都是一个单独的 LINESTRING 对象。每个 LINESTRING 由 2d 个点组成,LINESTRING 之间的点数不同。因此,首先我们获取所有点(代码中的“坐标”列表)并预测每个点的集群。我们在一个 LINESTRING 的点中呈现不同的集群的可能性很小。为了解决这种情况,我们获取每个集群的计数,然后过滤最大值。

【讨论】:

  • @james, df["STREET"] = df["name"] + "_" + df["cluster"].astype(str) 获取街道名称。我很快就会把解释放在答案中。
  • @james,我更新了帖子。 eps=1.5 也更好地分离数据。
  • @james 请将 subset="id" 添加到 drop_duplicates 到这一行:id_to_cluster = points_clusters[max_inds].drop_duplicates().set_index("id")["cluster"]。当我们有两个集群的最大数量相同时,可能会导致这种情况
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-12
  • 2018-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多