Home > Back-end >  How to switch values between a group of observations in r
How to switch values between a group of observations in r

Time:11-05

I have a dataset which has two variables, one character and one numeric :

structure(list(ID = c("A", "B", "C", "D", "E", "A", "B", "C", 
"D", "E", "A", "B", "C", "D", "E", "A", "B", "C", "D", "E"), 
value = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
15, 16, 17, 18, 19, 20)), class = "data.frame", row.names = c(NA, 
-20L))

What I would like to do is to switch the values of the "value" variable between each group of "A" and "E" for every sequence of "A" and "E".

Thus, my final output should look like this:

ID   value
A      5
B      2
C      3
D      4
E      1
A      10
B      7 
C      8
D      9
E      6
A      15
B      12
C      13
D      14
E      11
A      20
B      17
C      18
D      19
E      16

I used a sequence of numbers here just to make the example easy but my real values are not a sequence of 1 to 20. So, a solution that has a pattern of number would not work in my data.

Thanks!

CodePudding user response:

In base R you can do this:

# matrix with indices of A's and E's in its two rows
ae <- matrix(which(df$ID %in% c('A', 'E')), nrow=2)
# switch A and E
df$value[ae] <- df$value[ae[2:1, ]]

df
#    ID value
# 1   A     5
# 2   B     2
# 3   C     3
# 4   D     4
# 5   E     1
# 6   A    10
# 7   B     7
# 8   C     8
# 9   D     9
# 10  E     6
# 11  A    15
# 12  B    12
# 13  C    13
# 14  D    14
# 15  E    11
# 16  A    20
# 17  B    17
# 18  C    18
# 19  D    19
# 20  E    16

CodePudding user response:

Here is a dplyr way with case_when:

library(dplyr)
df %>% 
  group_by(gp = cumsum(ID == 'A')) %>% 
  mutate(value = case_when(ID == "A" ~ value[ID == "E"],
                           ID == "E" ~ value[ID == "A"],
                           TRUE ~ value)) %>% 
  ungroup() %>% 
  select(-gp)

output

# A tibble: 20 × 2
   ID    value
   <chr> <dbl>
 1 A         5
 2 B         2
 3 C         3
 4 D         4
 5 E         1
 6 A        10
 7 B         7
 8 C         8
 9 D         9
10 E         6
11 A        15
12 B        12
13 C        13
14 D        14
15 E        11
16 A        20
17 B        17
18 C        18
19 D        19
20 E        16

CodePudding user response:

Maybe better to just switch the IDs:

df$ID <- ifelse(df$ID == "A", "E", ifelse(df$ID == "E", "A", df$ID))

CodePudding user response:

With data.table, subset on "A" and "E", then used vectorized indexing to update value:

library(data.table)

setDT(df)[ID %chin% c("A", "E"), value := value[1:.N   c(1, -1)]][]
#>     ID value
#>  1:  A     5
#>  2:  B     2
#>  3:  C     3
#>  4:  D     4
#>  5:  E     1
#>  6:  A    10
#>  7:  B     7
#>  8:  C     8
#>  9:  D     9
#> 10:  E     6
#> 11:  A    15
#> 12:  B    12
#> 13:  C    13
#> 14:  D    14
#> 15:  E    11
#> 16:  A    20
#> 17:  B    17
#> 18:  C    18
#> 19:  D    19
#> 20:  E    16

CodePudding user response:

Probably possible with shorter code, but here is a suggestion

library(tidyverse)

df %>%  
  pivot_wider(names_from = ID, 
              values_from = value) %>% 
  unnest(everything()) %>%  
  transform(A = E, 
            E = A) %>% 
  pivot_longer(cols = everything())

# A tibble: 20 x 2
   name  value
   <chr> <dbl>
 1 A         5
 2 B         2
 3 C         3
 4 D         4
 5 E         1
 6 A        10
 7 B         7
 8 C         8
 9 D         9
10 E         6
11 A        15
12 B        12
13 C        13
14 D        14
15 E        11
16 A        20
17 B        17
18 C        18
19 D        19
20 E        16

CodePudding user response:

I would just simply switch them. It seems like this could be fancier, but I would keep it simple.

vA <- df$value[df$ID == "A"]
vE <- df$value[df$ID == "E"]

df$value[df$ID == "A"] <- vE
df$value[df$ID == "E"] <- vA

df
#>    ID value
#> 1   A     5
#> 2   B     2
#> 3   C     3
#> 4   D     4
#> 5   E     1
#> 6   A    10
#> 7   B     7
#> 8   C     8
#> 9   D     9
#> 10  E     6
#> 11  A    15
#> 12  B    12
#> 13  C    13
#> 14  D    14
#> 15  E    11
#> 16  A    20
#> 17  B    17
#> 18  C    18
#> 19  D    19
#> 20  E    16
  • Related