Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R 根据分组从列中减去值_R_Dplyr_Data.table_Data Manipulation - Fatal编程技术网

R 根据分组从列中减去值

R 根据分组从列中减去值,r,dplyr,data.table,data-manipulation,R,Dplyr,Data.table,Data Manipulation,我在R中有一个数据帧,格式如下: 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

我在R中有一个数据帧,格式如下:

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
等。有5种不同的处理方法、3个样本和10个时间点。还有大约50列不同的测量值,它们的名称不相关——我这里只显示了第一列,M1

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

 M1
 0
 0
 0
 0.64
 0
 0.06
 0.82
 0.10
 0.09
我不知道该怎么做。我首先提取时间点零值,对它们进行重复序列,然后减去它们。然而,我一次只能让它工作一列,这有点复杂。我想知道是否有一种方法可以在管道中执行此操作,使用group_by和mutate来更改每列的值,但无法找到一种方法来指定需要减去的值

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])

您可以使用时间为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)

使用
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
如果您需要先安排数据:

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

虽然IceCreamToucan的答案很好,但我发现它有点过于复杂了。使用
data.table
,方式类似于
dplyr

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

df %>%
 arrange_at(1:3) %>%
 group_by_at(1:2) %>%
 mutate_at(4:length(.), ~ . - first(.))
cols <- grep('^M', names(df), value = T)
df[,c(cols) := lapply(.SD,function(x){x- x[1]}),.SDcols = cols]

cols感谢您的快速响应-我的表在M1的右边还有大约50列,我想以同样的方式处理这些列。是否有一种方法可以使其适用于所有数据,而无需将行复制50次?具体来说,对于data.table,这个问题是以更一般的形式提出的,谨慎的做法是按时间排列数据,以确保
第一个
点是时间0处的点。