user1
user1

Reputation: 444

Selecting data frame value basing on the vectors

How can I select values of a data frame given two vectors storing the row and column? For mtcars

                   mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
Duster 360        14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 240D         24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
Merc 230          22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
...

I tried

data.frame(row = c(1, 3, 5),
           col = c('mpg', 'cyl', 'disp')) |>
 mutate(value = mtcars[row, col])

and expected to get

  row  col value
1   1  mpg  21.0
2   3  cyl     4
3   5 disp 360.0

instead of

  row  col value.mpg value.cyl value.disp
1   1  mpg      21.0         6        160
2   3  cyl      22.8         4        108
3   5 disp      18.7         8        360

Upvotes: 1

Views: 77

Answers (4)

benson23
benson23

Reputation: 19097

apply family in base R

One way to do it is to use sapply to go through all rows in your query data frame, and use the row and col info of each row to index the mtcars dataset.

EDIT: Credit to @Darren Tsai for providing the mapply solution.

library(dplyr)

data.frame(row = c(1, 3, 5),
                 col = c('mpg', 'cyl', 'disp')) %>% 
  mutate(value = sapply(1:nrow(.), \(x) mtcars[.[x, 1], .[x, 2]]))
  # or mapply
  mutate(value = mapply(\(x, y) mtcars[x, y], row, col))

rowwise() from dplyr

Another way of doing it is to use rowwise():

library(dplyr)

data.frame(row = c(1, 3, 5),
           col = c('mpg', 'cyl', 'disp')) %>% 
  rowwise() %>% 
  mutate(value = mtcars[row, col]) %>%
  ungroup()

Output

    row col   value
1     1 mpg      21
2     3 cyl       4
3     5 disp    360

Upvotes: 3

Merijn van Tilborg
Merijn van Tilborg

Reputation: 5887

Another way is to use diag to get the values

rows <- c(1, 3, 5)
cols <- c("mpg", "cyl", "disp")

data.frame(
  row = rows,
  col = cols,
  value = diag(as.matrix(mtcars[rows, cols]))
)

  row  col value
1   1  mpg    21
2   3  cyl     4
3   5 disp   360

Upvotes: 1

AndrewGB
AndrewGB

Reputation: 16856

Here is another tidyverse option:

library(tidyverse)

data.frame(row = c(1, 3, 5),
           col = c('mpg', 'cyl', 'disp')) %>%
  mutate(value = map2_dbl(row, col, ~ mtcars[.x, .y]))

Output

  row  col value
1   1  mpg    21
2   3  cyl     4
3   5 disp   360

Upvotes: 2

thelatemail
thelatemail

Reputation: 93833

matrix indexing to the rescue. See ?Extract:

A third form of indexing is via a numeric matrix with the one column for each dimension: each row of the index matrix then selects a single element of the array, and the result is a vector.

...

Indexing via a character matrix with one column per dimensions is also supported if the array has dimension names.

So:

df <- data.frame(row = c(1, 3, 5),
                 col = c('mpg', 'cyl', 'disp'))
## character matrix indexing
cbind(df, value = mtcars[cbind(rownames(mtcars)[df$row], df$col)])
## numeric matrix indexing
cbind(df, value = mtcars[cbind(df$row, match(df$col, colnames(mtcars)))])

## both resulting in:

##  row  col value
##1   1  mpg    21
##2   3  cyl     4
##3   5 disp   360

As @Onyambu notes, you can also use transform/base R piping if you like:

df |> transform(value = mtcars[cbind(rownames(mtcars)[row], col)])

...or the dplyr piping with mutate:

df %>% mutate(value = mtcars[cbind(rownames(mtcars)[row], col)])

Upvotes: 3

Related Questions