根据名称分割线条

时间:2017-12-27 08:32:39

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

我有一个从shapefile对象创建的GeoPandas Dataframe。 但是,certian行具有相同的名称,但位于非常不同的位置。

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

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

可以在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]
)

1 个答案:

答案 0 :(得分:2)

一种可能的方法是使用每个数据点的空间聚类。以下代码使用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()

enter image description here

单独街道的数量为8:

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

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

enter image description here

关于代码的凌乱部分:在源DataFrame中我们有170行,每行是一个单独的LINESTRING对象。每个LINESTRING由2d点组成,LINESTRING中的点数不同。因此,首先我们得到所有点(代码中的“coords”列表)并预测每个点的聚类。我们很少有可能在一个LINESTRING的点上呈现不同的聚类。为了解决这种情况,我们计算每个集群的数量,然后过滤最大值。