根据名字分割线
split lines based on names
我有一个从 shapefile 对象创建的 GeoPandas 数据框。
然而,certian line 具有相同的名称但在非常不同的地方。
我想为每行取一个唯一的名称!
因此,我需要以某种方式拆分线,如果它们在几何上分开并重命名它们。
可以尝试计算所有街道块之间的距离,并在它们靠近时重新组合它们。
距离的计算可以在 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]
)
一种可能的方法是对每个数据点使用空间聚类。以下代码使用 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 之间的点数不同。所以首先我们得到所有点(代码中的 "coords" 列表)并预测每个点的聚类。有一种可能性很小,即我们会在一个 LINESTRING 的点中显示不同的集群。为了解决这种情况,我们获取每个集群的计数,然后过滤最大值。
我有一个从 shapefile 对象创建的 GeoPandas 数据框。 然而,certian line 具有相同的名称但在非常不同的地方。
我想为每行取一个唯一的名称! 因此,我需要以某种方式拆分线,如果它们在几何上分开并重命名它们。
可以尝试计算所有街道块之间的距离,并在它们靠近时重新组合它们。
距离的计算可以在 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]
)
一种可能的方法是对每个数据点使用空间聚类。以下代码使用 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 之间的点数不同。所以首先我们得到所有点(代码中的 "coords" 列表)并预测每个点的聚类。有一种可能性很小,即我们会在一个 LINESTRING 的点中显示不同的集群。为了解决这种情况,我们获取每个集群的计数,然后过滤最大值。