How to subtract control group values from multiple treatmeant groups' values in R data frame?

I have a following data frame in R:

Group Value
Control 2
Control 3
Treament1 7
Treament1 4
Treament2 5
Treament2 6

How do I get:

Group Value
Treament1 - Control 5
Treament1 - Control 1
Treament2 - Control 3
Treament2 - Control 0

I was thinking of looping and using the subset function:

controldf <- subset(df, Group == "Control")
newdf <- subset(df, Group != "Control")
newvalue <- c()
for(group in unique(df$group)) {
    if(group != "Control") {
        newvalue <- c(newvalue, subset(df, Group == group)$value - controldf$value)
    }
}
newdf$value <- newvalue

...but this looks bad (and is very slow with larger data frames). :(

Is there a better way to do this?

Upvotes: 0

Views: 33

Answers (1)

Stefano Barbi
Stefano Barbi

Reputation: 3194

Solution 1 uses dplyr and tidyr libraries. An id column is added to the data.frame assuming that subjects are ordered within group.

library(dplyr)
library(tidyr)
df |>
    group_by(Group) |>
    mutate(id = row_number()) |>
    pivot_wider(names_from = Group, values_from = Value) |>
    mutate(across(starts_with("Treament"), ~.x - Control))  |>
    select(starts_with("Treament")) |>
    rename_with(~sprintf("%s - Control", .x)) |>
    pivot_longer(everything(), names_to = "Group", values_to = "Value") |>
    arrange(Group)
##>  # A tibble: 4 × 2
##>   Group               Value
##>   <chr>               <int>
##> 1 Treament1 - Control     5
##> 2 Treament1 - Control     1
##> 3 Treament2 - Control     3
##> 4 Treament2 - Control     3

Solution 2 uses the purrr library:

library(purrr)

spl <- with(df, split(Value, Group))

imap_dfr(spl[startsWith(names(spl), "Treament")], ~{
    data.frame(Group= sprintf("%s - Control", .y),
               Value = .x - spl$Control)
    })

##>                 Group Value
##> 1 Treament1 - Control     5
##> 2 Treament1 - Control     1
##> 3 Treament2 - Control     3
##> 4 Treament2 - Control     3

Upvotes: 2

Related Questions