R 根据分组从列中减去值
我在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
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处的点。