Pratham
Pratham

Reputation: 179

Slice/subset in R using vector/matrix/list as indices

We can get element at 2nd row, 4th column as

iris[2,4]

How to do the same if I have a vector/matrix of the slicing elements. ie can i get any of the following to give the same output as iris[2,4] ? (Or anything similar, without creating any temporary variables)

a = c(2,4)
b = matrix(c(2,4), nrow=1)
c = list(2,4)
iris[a]
iris[b]
iris[c]

Upvotes: 1

Views: 2805

Answers (1)

AkselA
AkselA

Reputation: 8846

The value is coerced to character because you're using a matrix method for subsetting, and one of the columns is of class character.

We can circumvent this in two ways. Either we remove the character column beforehand,

v <- c(2, 4)
m <- matrix(c(2, 4), nrow=1)
l <- list(2, 4)

iris[,-5][t(v)]
# [1] 0.2
iris[,-5][m]
# [1] 0.2
iris[,-5][do.call(cbind, l)]
# [1] 0.2

or we use a list method for subsetting. (iris is a data.frame, so both are applicable)

iris[[rev(v)]]
# [1] 0.2
iris[[rev(c(m))]]
# [1] 0.2
iris[[rev(unlist(l))]]
# [1] 0.2

I know it's been years, but I just wanted to reiterate that rev() is necessary when using the output from match(, arr.ind=TRUE)

mat <- matrix(c(1:9, 1:7), 4)
(dtf <- as.data.frame(mat))
#   V1 V2 V3 V4
# 1  1  5  9  4
# 2  2  6  1  5
# 3  3  7  2  6
# 4  4  8  3  7

(id <- which(mat == 8, arr.ind=TRUE))
#      row col
# [1,]   4   2

mat[id]
# [1] 8

dtf[[rev(id)]] # Traverse as list; first column, then row
# [1] 8

dtf[id] # Indexing as matrix; first row, then column
# [1] 8

dtf[[id]] # Does not work
# Error in as.matrix(x)[[i]] : 
#   attempt to select more than one element in vectorIndex

dtf[[c(id)]] # Wrong index
# [1] 5


### Multiple matches
(id <- which(mat == 2, arr.ind=TRUE))
#      row col
# [1,]   2   1
# [2,]   3   3

mat[id]
# [1] 2 2

dtf[id]
# [1] 2 2

dtf[[rev(id)]] # Does not work any more
# Error in .subset2(x, i, exact = exact) : 
#   recursive indexing failed at level 2


### Why traversing a data.frame as list can be useful
(dtf$V1 <- as.character(dtf$V1))
# [1] "1" "2" "3" "4"

(id <- which(dtf == 1, arr.ind=TRUE)) # Will match both characters and numerics
#      row col
# [1,]   1   1
# [2,]   2   3

dtf[id] # Returns both as character
# [1] "1" "1"

l <- lapply(data.frame(t(id)), 
  function(x) {
      dtf[[rev(x)]]
  }
)
sapply(l, class) # Retains classes
#          X1          X2 
# "character"   "integer" 

Upvotes: 3

Related Questions