Bündelung ungerichteter Linien

16

Ich suche nach einem effizienten Weg, um Leitungen unabhängig von ihrer Richtung zu gruppieren. Das bedeutet, dass eine Linie zwischen New York und Los Angeles im selben Cluster liegen sollte wie eine Linie in der anderen Richtung zwischen Los Angeles und New York. Die Start- / Endpunktpositionen sollten ähnlich sein (dh San Diego nach Long Island sollte sich im selben Cluster wie LA-NY befinden, aber wahrscheinlich nicht von San Francisco nach Boston), und es gibt keine Zwischenpunkte. Eingabedaten ähneln diesem Beispiel:

Bildbeschreibung hier eingeben (Von Cassiopeia sweet in der japanischen Wikipedia GFDL oder CC-BY-SA-3.0 , über Wikimedia Commons)

Ich habe zuvor versucht, die Linien im Voraus zu sortieren, z. B. um sie alle von West nach Ost laufen zu lassen, aber dies löst nicht das Problem für Linien, die von Nord nach Süd und umgekehrt verlaufen.

Kennen Sie einen Algorithmus, der sich mit diesem Problem befasst? Ich habe gesucht, aber abgesehen vom Algorithmus zur Berechnung der durchschnittlichen Richtung ungerichteter Segmente habe ich nichts entfernt hilfreiches gefunden, daher muss ich die falschen Suchbegriffe verwenden.

Underdunkel
quelle
1
Ich würde die Koordinaten beider Enden berechnen und STR (set ([x1, y1, x2, y2])) verwenden, um das Zeichenfolgenfeld zu füllen. Sie können dieses Feld zusammenfassen, um eindeutige Werte zu finden
FelixIP

Antworten:

10

Wenn ich Sie richtig verstehe, möchten Sie Linien bündeln, die in etwa gleich sind, ohne Rücksicht auf die Richtung.

Hier ist eine Idee, von der ich denke, dass sie funktionieren könnte.

  1. Teilen Sie die Linien in Start- und Endpunkt

  2. Gruppieren Sie die Punkte und erhalten Sie die Cluster-ID

  3. Suchen Sie nach Zeilen mit derselben Cluster-ID-Kombination. Das sind ein Cluster

Dies sollte in PostGIS (natürlich :-)) Version 2.3 möglich sein

Ich habe die ST_ClusterDBSCAN-Funktion nicht getestet, sie sollte jedoch funktionieren.

Wenn Sie eine Linientabelle wie diese haben:

CREATE TABLE the_lines
(
   geom geometry(linestring),
   id integer primary key
)

Und Sie möchten den Cluster erstellen, bei dem Start- und Endpunkt maximal 10 km voneinander entfernt sind. Und es müssen mindestens 2 Punkte vorhanden sein, um ein Cluster zu sein, dann könnte die Abfrage ungefähr so ​​lauten:

WITH point_id AS
   (SELECT (ST_DumpPoints(geom)).geom, id FROM the_lines),
point_clusters as
   (SELECT ST_ClusterDBSCAN(geom, 10000, 2) cluster_id, id line_id FROM point_id) 
SELECT array_agg(a.line_id), a.cluster_id, b.cluster_id 
FROM point_clusters a 
     INNER JOIN point_clusters b 
     ON a.line_id = b.line_id AND a.cluster_id < b.cluster_id
GROUP BY a.cluster_id, b.cluster_id

Durch die Verbindung mit erhalten a.cluster_id<b.cluster_idSie eine vergleichbare Cluster-ID unabhängig von der Richtung.

Nicklas Avén
quelle
Danke Nicklas! Ich mag diesen Ansatz, weil er mich nicht zwingt, verschiedene Einheiten (dh Winkel und Entfernungen) beim Clustering zu mischen.
underdark
5

Möchten Sie wirklich nur nach Richtung gruppieren, ohne Rücksicht auf Herkunft oder Ziel? Wenn ja, gibt es einige sehr einfache Möglichkeiten. Am einfachsten ist es vielleicht, die Peilung jeder Linie zu berechnen, zu verdoppeln und als Punkt auf einem Kreis zu zeichnen. Da sich die Vorwärts- und Rückwärtslager um 180 Grad unterscheiden, unterscheiden sie sich nach dem Verdoppeln um 360 Grad und zeichnen daher genau an der gleichen Stelle. Nun gruppieren Sie die Punkte in der Ebene mit einer beliebigen Methode.

Hier ist ein Arbeitsbeispiel R, dessen Ausgabe die Linien zeigt, die gemäß jedem der vier Cluster gefärbt sind. Natürlich würden Sie wahrscheinlich ein GIS verwenden, um die Lager zu berechnen - ich habe der Einfachheit halber euklidische Lager verwendet.

Zahl

cluster.undirected <- function(x, ...) {
  #
  # Compute the bearing and double it.
  #
  theta <- atan2(x[, 4] - x[, 2], x[, 3] - x[, 1]) * 2
  #
  # Convert to a point on the unit circle.
  #
  z <- cbind(cos(theta), sin(theta))
  #
  # Cluster those points.
  #
  kmeans(z, ...)
}
#
# Create some data.
#
n <- 100
set.seed(17)
pts <- matrix(rnorm(4*n, c(-2,0,2,0), sd=1), ncol=4, byrow=TRUE)
colnames(pts) <- c("x.O", "y.O", "x.D", "y.D")
#
# Plot them.
#
plot(rbind(pts[1:n,1:2], pts[1:n,3:4]), pch=19, col="Gray", xlab="X", ylab="Y")
#
# Plot the clustering solution.
#
n.centers <- 4
s <- cluster.undirected(pts, centers=n.centers)
colors <- hsv(seq(1/6, 5/6, length.out=n.centers), 0.8, 0.6, 0.25)
invisible(sapply(1:n, function(i) 
  lines(pts[i, c(1,3)], pts[i, c(2,4)], col=colors[s$cluster[i]], lwd=2))
)
whuber
quelle
Vielen Dank! Herkunft und Ziel (O & D) spielen ebenfalls eine Rolle. Ich habe versucht, mit "Start- / Endpunktpositionen sollten ähnlich sein" darauf hinzuweisen, aber es ist mir egal, welches O und welches D ist. Dennoch denke ich, dass Ihre Erklärung mich näher an die gesuchte Lösung bringen könnte, wenn ich können herausfinden, wie die Einheitskreiswerte auf die Punktkoordinaten skaliert werden, bevor KMeans ausgeführt wird.
Underdunkel
Ich vermutete, dass Sie das im Sinn haben könnten. Aus diesem Grund habe ich vorgeschlagen, die Halbrichtungen auf ein Koordinatenpaar (Punkte) abzubilden. Sie können diese Punkte (dh Polarkoordinaten) mit einer zweiten Variablen skalieren und / oder zusätzliche Koordinaten für Ursprung oder Ziel eingeben. Ohne den endgültigen Zweck der Clusterbildung zu kennen, ist es schwierig, weitere Hinweise zu geben, da die relativen Größen der zusätzlichen Koordinaten (im Vergleich zu den Kreiskoordinaten) die Clusterbildungslösungen bestimmen. Eine andere Lösung besteht darin, die Hough-Transformation auszunutzen .
Whuber
4

Ihre Klärung der Frage zeigt an, dass Sie möchten, dass die Gruppierung auf den tatsächlichen Liniensegmenten basiert , in dem Sinne, dass zwei beliebige Ursprungs-Ziel-Paare (OD-Paare) als "nahe" betrachtet werden sollten, wenn beide Ursprünge nahe und beide Ziele nahe sind , unabhängig davon , welchen Punkt Ursprung oder Ziel betrachtet .

Diese Formulierung deutet darauf hin, dass Sie bereits einen Eindruck von der Entfernung d zwischen zwei Punkten haben: Es kann sich um die Entfernung während des Fluges, die Entfernung auf der Karte, die Hin- und Rückfahrt oder eine andere Metrik handeln, die sich nicht ändert, wenn O und D gleich sind geschaltet. Die einzige Komplikation besteht darin, dass die Segmente keine eindeutigen Darstellungen haben: Sie entsprechen ungeordneten Paaren {O, D}, müssen jedoch als geordnete Paare (O, D) oder (D, O) dargestellt werden. Wir können daher den Abstand zwischen zwei geordneten Paaren (O1, D1) und (O2, D2) als eine symmetrische Kombination der Abstände d (O1, O2) und d (D1, D2) wie ihre Summe oder das Quadrat ansehen Wurzel aus der Summe ihrer Quadrate. Schreiben wir diese Kombination als

distance((O1,D1), (O2,D2)) = f(d(O1,O2), d(D1,D2)).

Definieren Sie einfach den Abstand zwischen ungeordneten Paaren als den kleineren der beiden möglichen Abstände:

distance({O1,D1}, {O2,D2}) = min(f(d(O1,O2)), d(D1,D2)), f(d(O1,D2), d(D1,O2))).

An dieser Stelle können Sie jede Clustering-Technik anwenden, die auf einer Distanzmatrix basiert.


Als Beispiel habe ich alle 190 Punkt-zu-Punkt-Entfernungen auf der Karte für 20 der bevölkerungsreichsten US-Städte berechnet und acht Cluster mithilfe einer hierarchischen Methode angefordert. (Der Einfachheit halber habe ich Euklidische Entfernungsberechnungen verwendet und die Standardmethoden in der von mir verwendeten Software angewendet: In der Praxis werden Sie geeignete Entfernungen und Clustering-Methoden für Ihr Problem auswählen wollen.) Hier ist die Lösung, wobei die Cluster durch die Farbe jedes Liniensegments angezeigt werden. (Die Farben wurden den Clustern zufällig zugewiesen.)

Zahl

Hier ist der RCode, der dieses Beispiel erzeugt hat. Die Eingabe erfolgt in einer Textdatei mit den Feldern "Längengrad" und "Breitengrad" für die Städte. (Um die Städte in der Abbildung zu kennzeichnen, enthält sie auch ein Feld "Schlüssel".)

#
# Obtain an array of point pairs.
#
X <- read.csv("F:/Research/R/Projects/US_cities.txt", stringsAsFactors=FALSE)
pts <- cbind(X$Longitude, X$Latitude)

# -- This emulates arbitrary choices of origin and destination in each pair
XX <- t(combn(nrow(X), 2, function(i) c(pts[i[1],], pts[i[2],])))
k <- runif(nrow(XX)) < 1/2
XX <- rbind(XX[k, ], XX[!k, c(3,4,1,2)])
#
# Construct 4-D points for clustering.
# This is the combined array of O-D and D-O pairs, one per row.
#
Pairs <- rbind(XX, XX[, c(3,4,1,2)])
#
# Compute a distance matrix for the combined array.
#
D <- dist(Pairs)
#
# Select the smaller of each pair of possible distances and construct a new
# distance matrix for the original {O,D} pairs.
#
m <- attr(D, "Size")
delta <- matrix(NA, m, m)
delta[lower.tri(delta)] <- D
f <- matrix(NA, m/2, m/2)
block <- 1:(m/2)
f <- pmin(delta[block, block], delta[block+m/2, block])
D <- structure(f[lower.tri(f)], Size=nrow(f), Diag=FALSE, Upper=FALSE, 
               method="Euclidean", call=attr(D, "call"), class="dist")
#
# Cluster according to these distances.
#
H <- hclust(D)
n.groups <- 8
members <- cutree(H, k=2*n.groups)
#
# Display the clusters with colors.
#
plot(c(-131, -66), c(28, 44), xlab="Longitude", ylab="Latitude", type="n")
g <- max(members)
colors <- hsv(seq(1/6, 5/6, length.out=g), seq(1, 0.25, length.out=g), 0.6, 0.45)
colors <- colors[sample.int(g)]
invisible(sapply(1:nrow(Pairs), function(i) 
  lines(Pairs[i, c(1,3)], Pairs[i, c(2,4)], col=colors[members[i]], lwd=1))
)
#
# Show the points for reference
#
positions <- round(apply(t(pts) - colMeans(pts), 2, 
                         function(x) atan2(x[2], x[1])) / (pi/2)) %% 4
positions <- c(4, 3, 2, 1)[positions+1]
points(pts, pch=19, col="Gray", xlab="X", ylab="Y")
text(pts, labels=X$Key, pos=positions, cex=0.6)
whuber
quelle
Vielen Dank! Wird die paarweise Distanzberechnung ein Problem für große OD-Datensätze sein?
Underdunkel
Ja, weil mit n Liniensegmenten n (n-1) / 2 Entfernungsberechnungen durchgeführt werden. Es gibt jedoch kein inhärentes Problem: Alle Cluster-Algorithmen müssen Entfernungen oder Unähnlichkeiten zwischen Punkten (oder zwischen Punkten und Cluster-Zentren) feststellen. Dies ist ein so häufiges Problem, dass viele Algorithmen mit einer benutzerdefinierten Distanzfunktion arbeiten.
Whuber