Reputation: 653
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
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