Raggruppamento di linee non indirizzate


16

Sto cercando un modo efficiente per raggruppare le linee indipendentemente dalla loro direzione. Ciò significa che una linea tra New York e Los Angeles dovrebbe trovarsi nello stesso ammasso di una linea nella direzione opposta tra Los Angeles e New York. Le posizioni dei punti di inizio / fine dovrebbero essere simili (cioè da San Diego a Long Island dovrebbero essere nello stesso cluster di LA-NY ma probabilmente non da San Francisco a Boston) e non ci sono punti intermedi. I dati di input sarebbero simili a questo esempio:

inserisci qui la descrizione dell'immagine (Di Cassiopeia dolce in Wikipedia giapponese GFDL o CC-BY-SA-3.0 , tramite Wikimedia Commons)

In precedenza ho cercato di ordinare in anticipo le linee, ad esempio per farle correre da ovest a est, ma questo non risolve il problema per le linee che vanno da nord a sud e viceversa.

Conosci qualche algoritmo che affronta questo problema? Ho cercato ma oltre all'algoritmo per calcolare la direzione media dei segmenti non indirizzati non ho trovato nulla di remotamente utile, quindi devo usare i termini di ricerca sbagliati.


1
Calcolo delle coordinate di entrambe le estremità e utilizzo STR (set ([x1, y1, x2, y2])) per popolare il campo stringa. È possibile riepilogare questo campo per trovare valori univoci
FelixIP

Risposte:


10

Se ho capito bene, vuoi raggruppare le linee più o meno allo stesso modo, indipendentemente dalla direzione.

Ecco un'idea che penso potrebbe funzionare.

  1. dividere le linee nel punto iniziale e finale

  2. Raggruppa i punti e ottieni l'ID del cluster

  3. Trova le linee con la stessa combinazione di ID cluster. Quelli sono un gruppo

Ciò dovrebbe essere possibile in PostGIS (ovviamente :-)) versione 2.3

Non ho testato la funzione ST_ClusterDBSCAN, ma dovrebbe funzionare.

Se hai una tabella di righe come questa:

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

E si desidera creare il cluster in cui i punti di inizio e fine sono distanti al massimo 10 km. E ci devono essere almeno 2 punti per essere un cluster, quindi la query potrebbe essere qualcosa del tipo:

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

Unendoti a a.cluster_id<b.cluster_idte otterrai un ID cluster comparabile indipendente dalla direzione.


Grazie Nicklas! Mi piace questo approccio perché non mi costringe a mescolare unità diverse (ad esempio angoli e distanze) durante il raggruppamento.
underdark

5

Vuoi davvero raggruppare solo per direzione, senza alcuna considerazione di origine o destinazione? Se è così, ci sono alcuni modi molto semplici. Forse il più semplice è calcolare il rilevamento di ogni linea, raddoppiarlo e tracciarlo come un punto su un cerchio. Poiché i cuscinetti avanti-indietro differiscono di 180 gradi, differiscono di 360 gradi dopo il raddoppio e quindi tracciano esattamente nello stesso punto. Ora raggruppa i punti nel piano usando qualsiasi metodo che ti piace.

Ecco un esempio funzionante in R, con il suo output che mostra le linee colorate in base a ciascuno dei quattro cluster. Ovviamente useresti probabilmente un GIS per calcolare i cuscinetti - ho usato i cuscinetti Euclidean per semplicità.

figura

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))
)

Grazie! Anche l'origine e la destinazione (O&D) contano. Ho cercato di accennarlo con "le posizioni dei punti di inizio / fine dovrebbero essere simili" ma non mi interessa quale sia O e quale sia D. Tuttavia, penso che la tua spiegazione potrebbe portarmi più vicino alla soluzione che stavo cercando, se può capire come ridimensionare i valori del cerchio unitario in base alle coordinate del punto prima di eseguire KMeans.
underdark

Sospettavo che potresti averlo in mente. Ecco perché ho suggerito di mappare le semi-direzioni su una coppia di coordinate (punti). Puoi ridimensionare quei punti (pensa alle coordinate polari) di una seconda variabile e / o introdurre coordinate aggiuntive per origini o destinazioni. Senza conoscere lo scopo ultimo del clustering, è difficile fornire ulteriori consigli poiché le dimensioni relative delle coordinate aggiuntive (rispetto alle coordinate del cerchio) determineranno le soluzioni di clustering. Un'altra soluzione è sfruttare la trasformazione di Hough .
whuber

4

Il chiarimento della domanda indica che si desidera che il clustering si basi sui segmenti di linea effettivi , nel senso che due coppie origine-destinazione (OD) devono essere considerate "chiuse" quando entrambe le origini sono vicine e entrambe le destinazioni sono vicine , indipendentemente da quale punto sia considerato origine o destinazione .

Questa formulazione suggerisce che hai già un senso della distanza d tra due punti: potrebbe essere la distanza mentre il piano vola, la distanza sulla mappa, il tempo di viaggio di andata e ritorno o qualsiasi altra metrica che non cambia quando O e D sono commutata. L'unica complicazione è che i segmenti non hanno rappresentazioni uniche: corrispondono a coppie non ordinate {O, D} ma devono essere rappresentate come coppie ordinate , (O, D) o (D, O). Potremmo quindi prendere la distanza tra due coppie ordinate (O1, D1) e (O2, D2) per essere una combinazione simmetrica delle distanze d (O1, O2) e d (D1, D2), come la loro somma o il quadrato radice della somma dei loro quadrati. Scriviamo questa combinazione come

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

Basta definire la distanza tra le coppie non ordinate in modo che sia la più piccola delle due distanze possibili:

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

A questo punto è possibile applicare qualsiasi tecnica di clustering basata su una matrice di distanza.


Ad esempio, ho calcolato tutte le 190 distanze punto-punto sulla mappa per 20 delle più popolose città degli Stati Uniti e ho richiesto otto cluster usando un metodo gerarchico. (Per semplicità ho usato i calcoli della distanza euclidea e ho applicato i metodi predefiniti nel software che stavo usando: in pratica vorrai scegliere le distanze appropriate e i metodi di raggruppamento per il tuo problema). Ecco la soluzione, con i cluster indicati dal colore di ogni segmento di linea. (I colori sono stati assegnati in modo casuale ai cluster.)

figura

Ecco il Rcodice che ha prodotto questo esempio. Il suo input è un file di testo con i campi "Longitudine" e "Latitudine" per le città. (Per etichettare le città nella figura, include anche un campo "Chiave".)

#
# 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)

Grazie! Il calcolo della distanza a coppie sarà un problema per grandi set di dati OD?
underdark

Sì, perché con n segmenti di linea ci sono n (n-1) / 2 calcoli di distanza. Ma non c'è nessun problema inerente: tutti gli algoritmi di clustering devono trovare distanze o differenze tra punti (o tra punti e centri del cluster). Questo è un problema così comune che molti algoritmi funzionano con una funzione di distanza personalizzata.
whuber
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.