#1 Thu 16 February 2023 16:56
- Abdoulayi111
- Participant occasionnel
- Date d'inscription: 17 Oct 2018
- Messages: 15
Clustering spatial avec poids maximum par cluster
Je souhaite faire du clustering spatial autrement dit regrouper des données ayant une proximité spatiale en se basant sur le réseau.
En effet, je dispose de deux couches de shapefiles en entrée.
Une couche "linéaire" constituant le réseau, celle sur laquelle la distance de proximité doit se baser.
Une autre couche du type "Point" sera utilisée pour le clustering.
Il est important de noter que ma couche "Point" dispose d'un champ poids.
Le cluster doit tenir compte de ce poids, pour que la somme totale des valeurs de poids dans un cluster ne dépasse pas la valeur indiquée : Ex : 200.
J'ai déjà réalisé un code utilisant DBSCAN. Cependant, cet algorithme ne tient pas compte du poids, mais de la distance séparant deux entités proches pour être considérée appartenant au même cluster.
Dans mon cas, la distance a peu d'importance tant que les entités sont les plus proches et que la somme totale des poids dans un cluster est respectée.
Je prends tout ce qui pourrait m'aider à avancer dans cette recherche.
En vous remerciant par avance de votre aide.
Hors ligne
#2 Thu 16 February 2023 17:03
- Abdoulayi111
- Participant occasionnel
- Date d'inscription: 17 Oct 2018
- Messages: 15
Re: Clustering spatial avec poids maximum par cluster
Voici le code en question avec DBSCAN ci-dessous. Il ne répond pas à mes besoins à cause de la non prise en compte du poids et de la distance maximum entre deux entités d'un même cluster.
'''python
from sklearn.cluster import DBSCAN
import networkx as nx
import pandas as pd
import numpy as np
from qgis.core import QgsProject, QgsField, QgsFeatureIterator
from qgis.PyQt.QtCore import QVariant
class Clustering:
"""
Clustering logical grouping of points (populations) based on the linear networks (road networks) """
def __init__(self, copy_network_layer, node_nearest_point_layer) -> None:
"""
:param copy_network_layer: copy_network_layer: name of the new network layer
:param node_nearest_point_layer: Name of the new node table.
"""
self.copy_network_layer = copy_network_layer
self.node_nearest_point_layer = node_nearest_point_layer
@staticmethod
def formation_graph_in_network(feat_network_layer: QgsFeatureIterator) -> nx:
"""
:param feat_network_layer: Network layer name
:return: Graph constitutes the origin and the end of the network
"""
network_graph = nx.Graph()
for feat_line in feat_network_layer:
start_point = feat_line.geometry().asPolyline()[0]
end_point = feat_line.geometry().asPolyline()[-1]
network_graph.add_edge(start_point, end_point)
return network_graph
@staticmethod
def association_node_closest_network(feat_points_layer: QgsFeatureIterator,
network_graph: nx) -> tuple[np, pd]:
"""
Association of each point entity to the network closest to it
:param feat_points_layer: point layer name
:param network_graph: origin and the end of the network
:return:
"""
# Récupération des points en utilisant qgis.core
points = []
gid = []
for feat_pt in feat_points_layer:
point = feat_pt.geometry().asPoint()
nearest_node = min(network_graph.nodes(),
key=lambda x: np.linalg.norm(np.array(x) -
np.array([point.x(),
point.y()])))
gid.append(feat_pt.id())
points.append(nearest_node)
data = pd.DataFrame({"gid": gid})
return np.array(points), data
def cluster_dbscan(self, field_weight: str) -> pd.DataFrame:
"""
:param field_weight: This field will be created for contains information cluster
The same cluster will have same number.
:return: Each entity is associated with the number of its cluster
"""
network_layer = QgsProject.instance().mapLayersByName(self.copy_network_layer)[0]
points_layer = QgsProject.instance().mapLayersByName(self.node_nearest_point_layer)[0]
network_graph = self.formation_graph_in_network(network_layer.getFeatures())
points, clustering = self.association_node_closest_network(points_layer.getFeatures(),
network_graph)
# Clustering en utilisant DBSCAN
# esp : distance maximale entre deux échantillons pour qu'ils soient considérés comme
# faisant partie du même voisinage. Il ne s'agit pas de distance entre toutes les éléments d'un même cluster
# min_samples : nombre minimum d'échantillons requis pour former une densité d'échantillons
dbscan = DBSCAN(eps=500, min_samples=3, algorithm="auto")
clustering[field_weight] = dbscan.fit_predict(points)
points_layer.dataProvider().addAttributes([QgsField("label",
QVariant.Int,
comment="Valeur des cluster")])
points_layer.updateExtents()
points_layer.updateFields()
# If not existe, it will be create
idx_label = points_layer.fields().indexFromName("label")
for [gid, label] in clustering.values:
attrs = {idx_label: int(label)}
points_layer.dataProvider().changeAttributeValues({gid: attrs})
return clustering
if __name__ == "__main__":
cluster_instance = Clustering("network_layer", "pt_node_nearest")
cluster_instance.cluster_dbscan("poids")
'''
Hors ligne