#r #subset
#r #подмножество
Вопрос:
Я работаю с кубами изображений и хотел бы извлечь указанные отдельные пиксели из всех слоев (куб изображения похож на стек растров в том, что в нем есть N слоев, каждый из которых соответствует определенной длине волны; каждый слой представляет собой полное 2D-изображение). Я предложил два подхода:
# sample 3-D image cube array
foo<- array(runif(512*512*16),dim=c(512,512,16))
# sample collection of x,y ordered pairs defining the pixels of interest
fooloc<- cbind(sample(1:512,256),sample(1:512,256))
pull1<-function(foo,fooloc) {
bar<-foo[fooloc[,1],fooloc[,2],]
sapply(1:(nrow(fooloc)),function(j)diag(bar[,,j]))
}
pull2 <- function(foo, fooloc) sapply(1:(nrow(fooloc)) ,function(j) foo[fooloc[j,1],fooloc[j,2],])
Второй примерно в 10 раз быстрее (на microbenchmark
). У меня просто такое ощущение, что я чего-то не хватает в возможностях [
оператора. Есть идеи?
РЕДАКТИРОВАТЬ: попытка raster
идеи Джбаума:
bfoo<-brick(foo)
pull3<-function(foo,fooloc) {
#foo<-brick(foo)
locfoo<- nrow(foo)*(fooloc[,1]-1) fooloc[,2]
extract(foo,locfoo) #default is all layers
}
эталон для (фиксированной) pull2
функции:
Unit: microseconds
expr min lq median uq max
pull3(bfoo, fooloc) 924.523 932.221 1028.435 1041.691 1380.369
pull2(foo, fooloc) 771.862 793.670 828.307 833.867 979.687
neval
5
5
Комментарии:
1. Я думаю, что на самом деле это примерно на 70% быстрее, если вы извлекаете значения из всех 16 слоев (
pull1
на данный момент захватывает только первые 4 слоя).2. Да, извините за опечатку.
3. И да,
pull2
это тоже неправильно. Я исправлю.4. С and все еще что-то не так
pull1
pull2
.subscript out of bounds
5. @jbaums да, я, наверное, сменил лошадей на полпути и перешел к
fooloc[,1] fooloc[,2]
этим выражениям.
Ответ №1:
Если столбцы fooloc
представляют местоположения строк (x) и столбцов (y), в которых вы хотели бы выполнить выборку (по всем слоям) foo
, то способ, которым вы foo
создали подмножество bar
, для меня не имеет смысла. Выбранные строки foo
, которые сохраняются в bar
, — это строка и номер столбца первого интересующего местоположения. Выбранные столбцы, которые сохраняются bar
, — это номер строки и столбца второго интересующего местоположения. Это происходит в обоих pull1
и pull2
.
Предполагая, что вы хотите выполнить детализацию по слоям в каждой точке (представленной координатой x, заданной в первом столбце, fooloc
и координатой y, заданной во втором столбце fooloc
), тогда вы можете использовать матричное подмножество, посредством которого вы передаете матрицу со столбцами для каждого измерения (x, y, zв вашем случае), и строки, представляющие интересующие местоположения (т. Е., В основном, Ваш fooloc
с дополнительным столбцом для номера среза):
xyz <- cbind(fooloc[rep(1:256, each=16), ], rep(1:16, 256))
head(xyz, 17)
# [,1] [,2] [,3]
# [1,] 326 264 1
# [2,] 326 264 2
# [3,] 326 264 3
# [4,] 326 264 4
# [5,] 326 264 5
# [6,] 326 264 6
# [7,] 326 264 7
# [8,] 326 264 8
# [9,] 326 264 9
# [10,] 326 264 10
# [11,] 326 264 11
# [12,] 326 264 12
# [13,] 326 264 13
# [14,] 326 264 14
# [15,] 326 264 15
# [16,] 326 264 16
# [17,] 244 355 1
baz <- foo[xyz]
# Above, we use fooloc[rep(1:256, each=16), ] to repeat each location
# (each row of fooloc) 16 times, and bind a new column to the right. This
# new column contains the slice number, and repeats the series 1 through 16,
# 256 times.
head(baz, 16)
# [1] 0.94575793 0.05488447 0.81821761 0.66999710
# [5] 0.40956337 0.63314819 0.12832025 0.14121603
# [9] 0.04719879 0.25077312 0.96271159 0.67870516
# [13] 0.66355153 0.23132471 0.11800990 0.04486127
Эти первые 16 элементов содержат значения 16 фрагментов в местоположении, указанном в первой строке fooloc
. Они идентичны значениям, заданным foo[fooloc[1, 1], fooloc[1, 2], ]
.
Полный baz
вектор содержит 4096 элементов, соответствующих значениям из 16 слоев, извлеченных в 256 отдельных местоположениях.
Вы можете привязать его к матрице подмножеств, чтобы отслеживать, откуда они пришли:
qux <- cbind(xyz, baz)
head(qux)
# baz
# [1,] 326 264 1 0.94575793
# [2,] 326 264 2 0.05488447
# [3,] 326 264 3 0.81821761
# [4,] 326 264 4 0.66999710
# [5,] 326 264 5 0.40956337
# [6,] 326 264 6 0.63314819
Сравнение времени с вашими существующими функциями несправедливо, поскольку pull1
и pull2
возвращает по 32 элемента каждый, и ваш вопрос предполагает, что вам нужно полное 4096. Тем не менее, это все равно быстрее, чем pull1
.
В качестве дополнительного примечания, если вы можете прочитать файл с raster()
помощью (из raster
пакета), вы можете использовать extract
его для извлечения значений из всех слоев, просто указав ваш fooloc
в качестве y
аргумента.
Комментарии:
1. Без сомнения, я
pull1
неаккуратен — я извлекаю гигантское подмножество, а затем извлекаю небольшую часть этого! Я думаюbaz
, что в конечном итоге вы будете использовать память и время:-( ; но я попробую взломать этотraster::extract
метод и дам вам знать.2. @Carl: Возможно, вы правы, и на самом деле я не включил этот шаг в свой сырой тест. Среди прочего, я предполагаю, что это будет зависеть от (1) количества слоев ваших фактических изображений; (2) количества местоположений запроса; и (3) изменяется ли набор местоположений (и поэтому
baz
необходимо будет пересчитывать для каждого изображения / анализа).3. Кроме того, будьте осторожны с
raster::extract
. Координаты увеличиваются из нижнего левого угла (менее актуально для опубликованного вами примера, где вы заранее вычисляете номер ячейки), а номер ячейки увеличивается в порядке следования строк.4. да, я это заметил :-).