根据分组从列中减去值

时间:2019-09-10 20:10:03

标签: r dplyr data.table data-manipulation

我有一个R格式的数据框,格式如下:

@@NESTLEVEL

等有5种不同的处理方法,3个样本和10个时间点。大约还有50列各种测量值,名称不相关-我在这里只显示第一个,M1。

对于这50个测量值中的每个测量值,我想从所有后续时间点中减去它们在零时间处保持的值。例如,M1随后将如下所示:

Treatment    Sample   Time_point   M1
        A         1            0   0.12
        A         2            0   0.45
        A         3            0   0.35
        A         1            1   0.76
        A         2            1   0.45
        A         3            1   0.41
        A         1            2   0.94
        A         2            2   0.55
        A         3            2   0.44

我不知道该怎么做。我首先提取时间点零值,然后重复序列,然后减去它们。但是,我只能使它一次只能处理一列,这有点令人费解。我想知道是否有一种方法可以在管道中使用group_by和mutate来更改每列的值,但是找不到一种方法来指定需要减去的值。

4 个答案:

答案 0 :(得分:3)

d$M1 - ave(d$M1, d$Sample, d$Treatment, FUN = function(x) x[1])
#[1] 0.00 0.00 0.00 0.64 0.00 0.06 0.82 0.10 0.09

对于不止一列,请尝试

nm = c("M1")  #Add column names here
sapply(nm, function(s){
    d[[s]] - ave(d[[s]], d$Sample, d$Treatment, FUN = function(x) x[1])
})
#        M1
# [1,] 0.00
# [2,] 0.00
# [3,] 0.00
# [4,] 0.64
# [5,] 0.00
# [6,] 0.06
# [7,] 0.82
# [8,] 0.10
# [9,] 0.09

等效的tidyverse将是

d %>% group_by(Sample, Treatment) %>% mutate_at(nm, function(x) x - x[1])

答案 1 :(得分:2)

使用dplyr,您可以尝试:

df %>%
 group_by_at(1:2) %>%
 mutate(M1 = M1 - first(M1))

  Treatment Sample Time_point    M1
  <chr>      <int>      <int> <dbl>
1 A              1          0  0   
2 A              2          0  0   
3 A              3          0  0   
4 A              1          1  0.64
5 A              2          1  0   
6 A              3          1  0.06
7 A              1          2  0.82
8 A              2          2  0.1 
9 A              3          2  0.09

或对第4列中的所有列执行此操作:

df %>%
 group_by_at(1:2) %>%
 mutate_at(4:length(.), ~ . - first(.))

如果您需要先安排数据:

df %>%
 arrange_at(1:3) %>%
 group_by_at(1:2) %>%
 mutate(M1 = M1 - first(M1))

或针对多列:

df %>%
 arrange_at(1:3) %>%
 group_by_at(1:2) %>%
 mutate_at(4:length(.), ~ . - first(.))

答案 2 :(得分:1)

您可以与时间为0的数据子集联接,并使用data.table的更新联接功能。请注意,这将更新原始data.frame,而不是创建新的data.frame。

library(data.table)
setDT(df)

df[df[Time_point == 0], on = .(Treatment, Sample), 
   M1 := M1 - i.M1]

#    Treatment Sample Time_point   M1
# 1:         A      1          0 0.00
# 2:         A      2          0 0.00
# 3:         A      3          0 0.00
# 4:         A      1          1 0.64
# 5:         A      2          1 0.00
# 6:         A      3          1 0.06
# 7:         A      1          2 0.82
# 8:         A      2          2 0.10
# 9:         A      3          2 0.09

对于多列:

创建示例数据和列名向量

set.seed(2019)
df[, M2 := sample(nrow(df))]

cols <- grep('^M', names(df), value = T)

减去时间0值

df[df[Time_point == 0], on = .(Treatment, Sample), 
   (cols) := setDT(mget(cols)) - mget(paste0('i.', cols))][]

#    Treatment Sample Time_point   M1 M2
# 1:         A      1          0 0.00  0
# 2:         A      2          0 0.00  0
# 3:         A      3          0 0.00  0
# 4:         A      1          1 0.64 -3
# 5:         A      2          1 0.00 -5
# 6:         A      3          1 0.06  2
# 7:         A      1          2 0.82  2
# 8:         A      2          2 0.10  2
# 9:         A      3          2 0.09 -1

答案 3 :(得分:1)

尽管IceCreamToucan的回答很好,但我发现它有点复杂。使用data.table的方式类似于dplyr

library(data.table)
setDT(df)
df[,M1 := M1 - M1[0], by = .(Treatment, Sample)]

对于多列:

cols <- grep('^M', names(df), value = T)
df[,c(cols) := lapply(.SD,function(x){x- x[1]}),.SDcols = cols]