Search code examples
rfiltermatchsapplycbind

Extract value from cell where one column/row value is equal to the column name


I've seen several threads on solutions to this, but I am struggling implementing them. I have a df with columns across the top with descriptions, and then I have a list of samples with data that are grouped by description. I need to extract the values where the descriptions match the column names.

I have tried different solutions using match, cbind, sapply...etc, but get errors about an invalid type(matrix) or having duplicate row names.

 df1
 #row   description    sample    ball   square    circle
 1      ball           1a        .78      .04      .22
 2      ball           7b3       .32      .33      .33
 3      square         aaabc     .02      .90      .05
 4      circle         ggg3      .05      .04      .90
 5      circle         44        .01      .25      .70

My output would be:

 df2
 #row   description    sample    value
 1      ball           1a        .78      
 2      ball           7b3       .32      
 3      square         aaabc     .90      
 4      circle         ggg3      .90
 5      circle         44        .70

And then taking that one step further, I would then filter it

 df2 %>%
 filter(value < .9) %>%
 select(description, sample, value)

Resulting in:

 #row   description    sample    value
 1      ball           1a        .78      
 2      ball           7b3       .32      
 3      circle         44        .70

I know this is a duplicate, I'm just drawing a blank as to why I can't get the solutions to work with this data set.


Solution

  • We can use a row/column indexing to extract the values that match the column names with the 'description' column values

    m1 <- cbind(seq_len(nrow(df1)), match(df1$description, names(df1)[3:5]))
    data.frame(df1[1:3], value = df1[3:5][m1])
    #  description sample ball value
    #1        ball     1a 0.78  0.78
    #2        ball    7b3 0.32  0.32
    #3      square  aaabc 0.02  0.90
    #4      circle   ggg3 0.05  0.90
    #5      circle     44 0.01  0.70
    

    Or with tidyverse

    library(tidyverse)
    df1 %>% 
       rowwise %>% 
       transmute(description, sample, value = get(description))
    # A tibble: 5 x 3
    #  description sample value
    #  <chr>       <chr>  <dbl>
    #1 ball        1a      0.78
    #2 ball        7b3     0.32
    #3 square      aaabc   0.9 
    #4 circle      ggg3    0.9 
    #5 circle      44      0.7 
    

    data

    df1 <- structure(list(description = c("ball", "ball", "square", "circle", 
     "circle"), sample = c("1a", "7b3", "aaabc", "ggg3", "44"), ball = c(0.78, 
     0.32, 0.02, 0.05, 0.01), square = c(0.04, 0.33, 0.9, 0.04, 0.25
     ), circle = c(0.22, 0.33, 0.05, 0.9, 0.7)), class = "data.frame", 
      row.names = c("1", 
      "2", "3", "4", "5"))