Скупчення непрямих ліній


16

Я шукаю ефективний спосіб кластеризації ліній незалежно від їх напрямку. Це означає, що лінія між Нью-Йорком та Лос-Анджелесом повинна проходити в тому ж скупченні, що і лінія в іншому напрямку між Лос-Анджелесом та Нью-Йорком. Місце початку та кінцевої точки повинно бути схожим (тобто Сан-Дієго до Лонг-Айленду має бути в тому ж кластері, що і LA-NY, але, ймовірно, не від Сан-Франциско до Бостона), і проміжних пунктів немає. Вхідні дані будуть подібними до цього прикладу:

введіть тут опис зображення (За Касіопеєю солодкою з японської Вікіпедії GFDL або CC-BY-SA-3.0 , через Wikimedia Commons)

Я раніше намагався сортувати лінії заздалегідь, наприклад, щоб змусити їх рухатись із заходу на схід, але це не вирішує проблему для ліній, що курсують з півночі на південь та інше.

Чи знаєте ви якийсь алгоритм, що займається цією проблемою? Я шукав, але окрім алгоритму для обчислення середнього напрямку непрямих сегментів, я нічого корисного не знайшов, тому я повинен використовувати неправильні пошукові терміни.


1
Я б обчислив координати обох кінців і використав STR (set ([x1, y1, x2, y2])) для заповнення рядкового поля. Ви можете узагальнити це поле, щоб знайти унікальні значення
FelixIP

Відповіді:


10

Якщо я правильно вас розумію, ви хочете об'єднати лінії, приблизно такі самі, без поваги до напрямку.

Ось ідея, яку я думаю, могла б спрацювати

  1. розділити лінії на початкову і кінцеву точку

  2. Кластеризуйте точки і отримайте ідентифікатор кластера

  3. Знайдіть рядки з однаковою комбінацією ідентифікатора кластера. Це кластер

Це має бути можливим у PostGIS (звичайно :-)) версії 2.3

Я не перевіряв функцію ST_ClusterDBSCAN, але він повинен зробити роботу.

Якщо у вас є така лінія рядка:

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

І ви хочете створити кластер, де початкова і кінцева точки розташовані не більше 10 км. І для кластера має бути принаймні 2 бали, тоді запит може бути чимось таким:

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

Приєднавшись до a.cluster_id<b.cluster_idвас, ви отримаєте порівняний ідентифікатор кластера незалежно від напрямку.


Дякую, Ніклас! Мені подобається такий підхід, оскільки він не змушує мене змішувати різні одиниці (тобто кути та відстані) під час кластеризації.
underdark

5

Ви дійсно хочете скупчуватися виключно за напрямком, не враховуючи походження чи призначення? Якщо так, то є кілька дуже простих способів. Мабуть, найпростіше - обчислити опору кожної лінії, подвоїти її та побудувати її як точку на колі. Оскільки підшипники вперед-назад відрізняються на 180 градусів, вони різняться на 360 градусів після подвоєння і, отже, наводяться в точно тому ж місці. Тепер кластеризуйте точки в площині будь-яким способом, який вам подобається.

Ось робочий приклад, в Rйого результаті показано кольори ліній відповідно до кожного з чотирьох кластерів. Звичайно, ви, ймовірно, використовуєте ГІС для обчислення підшипників - я використовував евклідові підшипники для простоти.

Малюнок

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

Дякую! Походження та місце призначення (O&D) також мають значення. Спробував натякнути на це з "місцями початкової / кінцевої точки, має бути подібним", але мені все одно, який є О, а який є D. Все-таки я думаю, що ваше пояснення могло б наблизити мене до того рішення, яке я шукав, якби я може зрозуміти, як масштабувати значення одиничного кола до координат точок перед запуском KMeans.
underdark

Я підозрював, що ви це можете мати на увазі. Ось чому я запропонував зіставити напівнапрямки на пару координат (точок). Ви можете масштабувати ці точки (мислити полярні координати) за допомогою другої змінної та / або ввести додаткові координати для початків або пунктів призначення. Не знаючи кінцевої мети кластеризації, важко дати більше порад, оскільки відносні розміри додаткових координат (порівняно з координатами кола) визначатимуть рішення кластеризації. Ще одне рішення - використовувати перетворення Хоф .
whuber

4

Ваше роз'яснення питання вказує на те, що ви хочете, щоб кластеризація базувалася на фактичних сегментах рядків , в тому сенсі, що будь-які дві пари вихідних цілей (OD) повинні вважатися "близькими", коли обидва джерела близькі, і обидва пункти призначення близькі , незалежно від того, який момент вважається походження або призначення .

Ця формулювання передбачає, що ви вже відчуваєте відстань d між двома точками: це може бути відстань під час прольоту літака, відстань на карті, час подорожі в обидва кінці або будь-який інший показник, який не змінюється, коли O і D переключився. Єдине ускладнення полягає в тому, що сегменти не мають унікальних уявлень: вони відповідають не упорядкованим парам {O, D}, але повинні бути представлені як упорядковані пари, або (O, D) або (D, O). Тому ми можемо вважати відстань між двома впорядкованими парами (O1, D1) та (O2, D2) деякою симетричною комбінацією відстаней d (O1, O2) та d (D1, D2), таких як їх сума чи площа корінь суми їх квадратів. Запишемо це поєднання як

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

Просто визначте відстань між неупорядкованими парами меншою з двох можливих відстаней:

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

На даний момент ви можете застосувати будь-яку техніку кластеризації на основі матриці відстаней.


Як приклад, я обчислив усі 190 відстаней на карті на 20 найбільш населених містах США і просив вісім кластерів, використовуючи ієрархічний метод. (Для простоти я використав евклідові обчислення відстані і застосував методи за замовчуванням у програмному забезпеченні, яке я використовував. На практиці ви хочете вибрати відповідні відстані та методи кластеризації для вашої проблеми). Ось рішення з кластерами, позначеними кольором кожного сегмента рядка. (Кольори були випадковим чином присвоєні кластерам.)

Малюнок

Ось Rкод, який створив цей приклад. Вхідним текстом є текстовий файл із містами "Довгота" та "Широта". (Для позначення міст на малюнку воно також включає поле "Ключ".)

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

Спасибі! Чи буде обчислення парних відстаней проблемою для великих наборів даних OD?
underdark

Так, тому що з n відрізків рядків є n (n-1) / 2 обчислення відстані. Але немає ніякої притаманної проблеми: всі алгоритми кластеризації повинні знаходити відстані або відмінності між точками (або між точками та центрами кластерів). Це настільки поширена проблема, що багато алгоритмів працюють із власною функцією дистанції.
whuber
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.