3

I have a dataframe, say:

df <- tibble(question = 1:3, a = c('chicken', 'apple', 'beer'), b = c('chicken', 'banana', 'beer'), c = c('beef', 'apple', 'wine'))

question a       b       c    
>      <int> <chr>   <chr>   <chr>
1        1 chicken chicken beef 
2        2 apple   banana  apple
3        3 beer    beer    wine 

And I would like to replace the the values in row 2 with some mapping:

apple -> 1
banana -> 2

So that the resulting output is:

question a       b       c    
>      <int> <chr>   <chr>   <chr>
1        1 chicken chicken beef 
2        2       1       2        1
3        3 beer    beer    wine 

I've looked at case_match, but it appears to want a vector and my data is rows rather than columns. I think I can use across() to just get this to apply to all columns in a particular row, but not sure how to fit these pieces together.

EDIT: I'm not interested in replacing the value at a specific index of column and row. I'm interested in recoding all the values in a row by a particular mapping.

Anthop
  • 138
  • 8
  • 1
    Does this answer your question? [How to replace certain values in a specific rows and columns with NA in R?](https://stackoverflow.com/questions/54615462/how-to-replace-certain-values-in-a-specific-rows-and-columns-with-na-in-r) – SamR Feb 08 '23 at 17:24

5 Answers5

5

"If the row isn't 2, leave the values in a:c alone, otherwise swap in these new values":

df |>
  mutate(across(a:c, ~if_else(row_number() != 2, .,
                        case_match(., "apple" ~ "1", "banana" ~ "2"))))

Result

  question       a       b    c
1        1 chicken chicken beef
2        2       1       2    1
3        3    beer    beer wine

df <- data.frame(question = 1:3,
           a = c("chicken", "apple", "beer"),
           b = c("chicken", "banana", "beer"),
           c = c("beef", "apple", "wine"))
Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • I wonder if this is simply because I'm tired and missing something, but this doesn't actually replace for me. I removed the filtering limitations in across and if_else to see if that would help, but I still get the original dataframe back. data %>% mutate(across(everything(), case_match(., 'apple' ~ '1', 'banana' ~ '2'))) – Anthop Feb 08 '23 at 18:21
  • Are you saying the output of running the code above with the sample data above is different for you, or that `df` has not been changed in so doing? (We'd need to add `df <- [the code above]` if you want `df` to change.) If you are getting different output, what output are you getting and what version of dplyr are you running? `case_match` was added in dplyr 1.1.0, just recently added to CRAN. – Jon Spring Feb 08 '23 at 19:49
  • Two things about your code in the comment above: 1) You probably can't run your code `across(everything()...` without more modification if some columns are numeric and others are character. 2) You're missing a `~` before `case_match`. I presume you could run `data %>% mutate(across(a:c, ~case_match(., 'apple' ~ "1", 'banana' ~ "2", . ~ .)))` – Jon Spring Feb 08 '23 at 19:51
  • I edited my original question to include the line of code defining the df. I also did have the `~` before `case_match` in the code, but screwed up pasting in the comment. Basically I'm running `data <- tibble(question = 1:3, a = c('chicken', 'apple', 'beer'), b = c('chicken', 'banana', 'beer'), c = c('beef', 'apple', 'wine'))` and then `data %>% mutate(across(a:c, ~case_match(., 'apple' ~ "1", 'banana' ~ "2", . ~ .)))` and the output still has apple and banana in it. – Anthop Feb 08 '23 at 20:57
  • Odd. I get the expected output with that data and code. What do you get from `packageVersion("dplyr")`? – Jon Spring Feb 08 '23 at 21:17
  • `packageVersion("dplyr")` returns `[1] ‘1.1.0’`. – Anthop Feb 08 '23 at 22:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/251738/discussion-between-jon-spring-and-anthop). – Jon Spring Feb 08 '23 at 22:19
4

You could use across with case_when and condition with row_number to apply on only the second row like this:

library(dplyr)
df %>%
  mutate(across(a:c, ~ case_when(row_number() == 2 & . == "apple" ~ '1',
                                 row_number() == 2 & . == "banana" ~ '2',
                                 TRUE ~ .)))
#>   question       a       b    c
#> 1        1 chicken chicken beef
#> 2        2       1       2    1
#> 3        3    beer    beer wine

Created on 2023-02-08 with reprex v2.0.2

Quinten
  • 35,235
  • 5
  • 20
  • 53
2

You can create a conditional function using ifelse and include the row index with all columns but the first one to apply the function:

df[2, 2:4] <- ifelse(df[2, 2:4] == "apple", 1,
                    ifelse(df[2, 2:4] == "banana", 2, df[2, 2:4]))
S-SHAAF
  • 1,863
  • 2
  • 5
  • 14
2

Using a named vector

 df1[2, 2:4] <- setNames(c(1, 2), c("apple", "banana"))[unlist(df1[2,-1])]

-output

> df1
  question       a       b    c
1        1 chicken chicken beef
2        2       1       2    1
3        3    beer    beer wine
akrun
  • 874,273
  • 37
  • 540
  • 662
1

Here's a way with match:

df[2, ] <- c(1, 2, df[2, ])[match(df[2, ], c("apple", "banana", df[2, ]))]

#  question       a       b    c
#1        1 chicken chicken beef
#2        2       1       2    1
#3        3    beer    beer wine

Might be easier to use in a function:

replace_row <- function(data, row, new, old){
  data[row, ] <- c(new, data[row, ])[match(data[row, ], c(old, data[row, ]))]
  data
}
replace_row(df, 2, c(1, 2), c("apple", "banana"))
#  question       a       b    c
#1        1 chicken chicken beef
#2        2       1       2    1
#3        3    beer    beer wine
Maël
  • 45,206
  • 3
  • 29
  • 67