R - Как найти точки внутри определенного контура

Я создаю диаграммы плотности с kde2d (MASS) на данных lat и lon. Я хотел бы знать, какие точки из исходных данных находятся в определенном контуре.

Я создаю контуры 90% и 50%, используя два подхода. Я хочу знать, какие точки находятся в пределах 90% контура и какие точки находятся в пределах 50% контура. Точки в 90% -ном контуре будут содержать все те, что находятся в пределах 50% контура. Последний шаг - найти точки в пределах 90% контура, которые не находятся в пределах 50% контура (мне не обязательно нужна помощь с этим шагом).

# bw = data of 2 cols (lat and lon) and 363 rows
# two versions to do this: 
# would ideally like to use the second version (with ggplot2)

# version 1 (without ggplot2) 
library(MASS)
x <- bw$lon
y <- bw$lat
dens <- kde2d(x, y, n=200)

# the contours to plot
prob <- c(0.9, 0.5)
dx <- diff(dens$x[1:2])
dy <- diff(dens$y[1:2])
sz <- sort(dens$z)
c1 <- cumsum(sz) * dx * dy 
levels <- sapply(prob, function(x) { 
    approx(c1, sz, xout = 1 - x)$y
})
plot(x,y)
contour(dens, levels=levels, labels=prob, add=T)

А вот версия 2 - с помощью ggplot2. Я бы идеально хотел использовать эту версию, чтобы найти точки в пределах 90% и 50% контуров.

# version 2 (with ggplot2)
getLevel <- function(x,y,prob) { 
    kk <- MASS::kde2d(x,y)
    dx <- diff(kk$x[1:2])
    dy <- diff(kk$y[1:2])
    sz <- sort(kk$z)
    c1 <- cumsum(sz) * dx * dy
    approx(c1, sz, xout = 1 - prob)$y
}

# 90 and 50% contours
L90 <- getLevel(bw$lon, bw$lat, 0.9)
L50 <- getLevel(bw$lon, bw$lat, 0.5)

kk <- MASS::kde2d(bw$lon, bw$lat)
dimnames(kk$z) <- list(kk$x, kk$y)
dc <- melt(kk$z)

p <- ggplot(dc, aes(x=Var1, y=Var2)) + geom_tile(aes(fill=value)) 
+ geom_contour(aes(z=value), breaks=L90, colour="red")
+ geom_contour(aes(z=value), breaks=L50, color="yellow")
+ ggtitle("90 (red) and 50 (yellow) contours of BW")

Я создаю сюжеты со всеми графическими строками лат и лон и 90% и 50% контуров. Я просто хочу знать, как извлечь точные точки, которые находятся в пределах 90% и 50%.

Я попытался найти значения z (высота графиков плотности из kde2d), которые связаны с каждой строкой значений lat и lon, но не повезло. Я также думал, что я могу добавить столбец идентификатора к данным для обозначения каждой строки, а затем как-то передать это после использования melt(). Тогда я мог бы просто подмножить данные, имеющие значения z, которые соответствуют каждому контуру, который я хочу, и посмотреть, какие lat и lon они сравниваются с исходными данными BW на основе столбца ID.

Вот картина того, о чем я говорю:

enter image description here

Я хочу знать, какие красные точки находятся в пределах 50% контура (синий) и которые находятся в пределах 90% контура (красный).

Примечание: большая часть этого кода взята из других вопросов. Большой крик всем тем, кто внес свой вклад!

Спасибо!

Ответ 1

Вы можете использовать point.in.polygon от sp

## Interactively check points
plot(bw)
identify(bw$lon, bw$lat, labels=paste("(", round(bw$lon,2), ",", round(bw$lat,2), ")"))

## Points within polygons
library(sp)
dens <- kde2d(x, y, n=200, lims=c(c(-73, -70), c(-13, -12)))  # don't clip the contour
ls <- contourLines(dens, level=levels)
inner <- point.in.polygon(bw$lon, bw$lat, ls[[2]]$x, ls[[2]]$y)
out <- point.in.polygon(bw$lon, bw$lat, ls[[1]]$x, ls[[1]]$y)

## Plot
bw$region <- factor(inner + out)
plot(lat ~ lon, col=region, data=bw, pch=15)
contour(dens, levels=levels, labels=prob, add=T)

enter image description here

Ответ 2

Я думаю, что это лучший способ, о котором я могу думать. Это использует трюк для преобразования контурных линий в объекты SpatialLinesDataFrame с помощью функции ContourLines2SLDF() из пакета maptools. Затем я использую трюк, описанный в Bivand, et al. Прикладной анализ пространственных данных с помощью R для преобразования объекта SpatialLinesDataFrame в SpatialPolygons. Затем их можно использовать с помощью функции over() для извлечения точек в каждом контурном полигоне:

##  Simulate some lat/lon data:
x <- rnorm(363, 45, 10)
y <- rnorm(363, 45, 10)

##  Version 1 (without ggplot2):
library(MASS)
dens <- kde2d(x, y, n=200)

##  The contours to plot:
prob <- c(0.9, 0.5)
dx <- diff(dens$x[1:2])
dy <- diff(dens$y[1:2])
sz <- sort(dens$z)
c1 <- cumsum(sz) * dx * dy 
levels <- sapply(prob, function(x) { 
    approx(c1, sz, xout = 1 - x)$y
})
plot(x,y)
contour(dens, levels=levels, labels=prob, add=T)

##  Create spatial objects:
library(sp)
library(maptools)

pts <- SpatialPoints(cbind(x,y))

lines <- ContourLines2SLDF(contourLines(dens, levels=levels))

##  Convert SpatialLinesDataFrame to SpatialPolygons:
lns <- slot(lines, "lines")
polys <- SpatialPolygons( lapply(lns, function(x) {
  Polygons(list(Polygon(slot(slot(x, "Lines")[[1]], 
    "coords"))), ID=slot(x, "ID"))
    }))

##  Construct plot from your points, 
plot(pts)

##  Plot points within contours by using the over() function:
points(pts[!is.na( over(pts, polys[1]) )], col="red", pch=20)
points(pts[!is.na( over(pts, polys[2]) )], col="blue", pch=20)

contour(dens, levels=levels, labels=prob, add=T)

enter image description here