Вычислить расстояния между прямой и всеми точками на пересекающейся плоскости в r - PullRequest
0 голосов
/ 18 сентября 2018

У меня есть правильная прямоугольная сетка значений x, y, z (изображение).
У меня также есть линия x, y, z, которая пересекает сетку.

Для каждой плоскости (z-уровня) в сетке, я хочу вычислить евклидово расстояние между каждой точкой сетки и точкой пересечения линии с этой сеткой.

Пример:

# create regular 3D array of values
vec <- array(1:21,c(21,21,21))
dimnames(vec) = list(seq(-10,10), seq(-10,10), seq(-10,10))

# convert to data.frame (with names x, y, z, value)
grid <- melt(vec, varnames=c("x","y","z"))

# and a set of points along a line
line <- data.frame(
  x = seq(-10, 10),
  y = seq(-10, 10),
  z = seq(-10, 10)
)

Iпопробовал несколько вещей и остановился на использовании цикла for для z-значений.

Решение:

# loop through each z-level to compute the euclidean distance between 
# all points on the plane at that level, and 
# the point on the line at that level.
tmp = data.frame()
for(i in line$z) { 
  point <- subset(line, z == i)
  plane <- subset(grid, z == i)

  plane$euclidean = (plane$x - point$x)^2 + (plane$y - point$y)^2
  if(nrow(tmp) == 0) {
    tmp = plane
  } else {
    tmp = rbind(tmp, plane)
  }
}

Я не был особенно доволен этим решением, хотя оно было относительно быстрым.Мне не понравилась идея, что мне пришлось разделить и рекомбинировать набор данных, что привело к новому упорядочению / сортировке, и я чувствовал, что я делаю что-то не так, когда прибегаю к циклу for в r.

У меня есть сильное чувство, что этот способ несколько неэффективен и что могут быть другие (более эффективные?) Способы сделать это, используя один или несколько из следующих вариантов:

  1. безfor цикл, subset и rbind метод.
  2. с использованием линейной алгебры
  3. с использованием одной из apply функций
  4. с использованием пространственных типов данных и функции sp::spDistsN1()

Другое решениебыло merge сетки и линии на z-значении, а затем выполнить прямой расчет, но шаг merge был очень медленным, и столбцы x, y, z в результирующем data.frame переименовывались из-за дублирующего столбцаимена.


Обновления для уточнения:

  1. Хотя значение в каждой точке (пикселе) изображения не имеет значения для расчета расстояния, оно требуется позже и должно бытьпринес с собой.
  2. Как и в моем собственном решении, мне нужны значения расстояния, назначенные для каждой точки x, y, z.Таким образом, выходные данные должны включать (x, y, z, value, евклидово), но не обязательно должны быть data.frame.

1 Ответ

0 голосов
/ 19 сентября 2018

Вот моя tidyverse попытка, хотя и не очевидная, если она на самом деле яснее, чем ваш подход.Концептуально я думаю, что это в основном то же самое: для каждого z вычислить расстояние между точкой линии для этого z со всеми остальными точками.

  1. Привести массив к кадру данных (таким образомв основном аналогично melt)
  2. Создайте функцию, которая использует dist для вычисления расстояний между точкой и матрицей.dist фактически вычисляет между всеми строками матрицы, поэтому мы хотим сохранить только нижний ряд получившегося треугольника расстояний.Тем не менее, это, вероятно, все еще быстрее, чем делать евклидово расстояние вручную.
  3. nest данных, поэтому у нас есть одна строка на z, с x и y в качестве кадров данных в столбце списка
  4. left_join, линия указывает на и затем использует pmap, чтобы применить нашу новую функцию
  5. unnest, чтобы у нас были столбцы для x, y, zточки в сетке, px и py, которые являются точкой пересечения линии, и distance, которая является расстоянием до точки.по одной строке на точку в сетке.
vec <- array(1:21,c(21,21,21))
dimnames(vec) = list(x = seq(-10,10), y = seq(-10,10), z = seq(-10,10))

library(tidyverse)
grid <- vec %>% # same thing as melt basically
  as.tbl_cube(met_name = "value") %>%
  as_tibble() 

line <- data.frame(
  px = seq(-10, 10),
  py = seq(-10, 10),
  pz = seq(-10, 10)
)

my_dist <- function(point_x, point_y, mat){
  point_mat <- rbind(c(point_x, point_y), mat)
  dist_mat <- as.matrix(dist(point_mat))
  dist_vec <- dist_mat[nrow(dist_mat), 1:(ncol(dist_mat) - 1)]
  attributes(dist_vec) <- NULL
  return(dist_vec)
}

grid %>%
  select(-value) %>% 
  nest(x, y) %>% # One row per z
  left_join(line, by = c("z" = "pz")) %>%
  mutate(distance = pmap(list(px, py, data), my_dist)) %>%
  unnest() # Expand back out to one row per point
#> # A tibble: 9,261 x 6
#>        z    px    py distance     x     y
#>    <int> <int> <int>    <dbl> <int> <int>
#>  1   -10   -10   -10     28.3   -10   -10
#>  2   -10   -10   -10     28.3    -9   -10
#>  3   -10   -10   -10     27.6    -8   -10
#>  4   -10   -10   -10     26.9    -7   -10
#>  5   -10   -10   -10     26.2    -6   -10
#>  6   -10   -10   -10     25.6    -5   -10
#>  7   -10   -10   -10     25      -4   -10
#>  8   -10   -10   -10     24.4    -3   -10
#>  9   -10   -10   -10     23.9    -2   -10
#> 10   -10   -10   -10     23.3    -1   -10
#> # ... with 9,251 more rows

Создано в 2018-09-18 пакетом представитель (v0.2.0).

...