R Data.Table:为每一行动态更新不同的列
R Data.Table: Dynamically Update a Different Column for each Row
我正在编写一些代码,我需要在其中找到一组列的最大值,然后更新该最大值。考虑这个玩具示例:
test <- data.table(thing1=c('AAA','BBB','CCC','DDD','EEE'),
A=c(9,5,4,2,5),
B=c(2,7,2,6,3),
C=c(6,2,5,4,1),
ttl=c(1,1,3,2,1))
结果 data.table 如下所示:
thing1
A
B
C
ttl
AAA
9
2
6
1
BBB
5
7
2
1
CCC
4
2
5
3
DDD
2
6
4
2
EEE
5
3
1
1
目标是找到具有最大值的列(A
、B
或C
)并将该值替换为当前值减去0.1倍的值ttl
列(即 new_value=old_value - 0.1*ttl
)。其他列(不包含最大值)应保持不变。生成的 DT 应如下所示:
thing1
A
B
C
ttl
AAA
8.9
2
6
1
BBB
5
6.9
2
1
CCC
4
2
4.7
3
DDD
2
5.8
4
2
EEE
4.9
3
1
1
这样做的“明显”方法是编写一个 for 循环并循环遍历 DT 的每一行。这很容易做到,这就是我正在改编的代码所做的。然而,真正的 DT 比我的玩具示例大得多,for 循环需要一些时间 运行,这就是为什么我试图调整代码以利用矢量化并摆脱循环。
这是我目前的情况:
test[,max_position:=names(.SD)[apply(.SD,1,function(x) which.max(x))],.SDcols=(2:4)]
test[,newmax:=get(max_position)-ttl*.1,by=1:nrow(test)]
生成此 DT:
|thing1|A|B|C|ttl|max_position|newmax|
|------|-|-|-|---|-|-|
|AAA|9|2|6|1|A|8.9|
|BBB|5|7|2|1|B|6.9|
|CCC|4|2|5|3|C|4.7|
|DDD|2|6|4|2|B|5.8|
|EEE|5|3|1|1|A|4.9|
问题在于将 newmax
列的值分配回需要去的地方。我天真地尝试了这个,连同其他一些东西,它告诉我“'max_position' not found”:
test[,(max_position):=newmax,by=1:nrow(test)]
通过重塑 DT 来解决问题很简单,这是我目前的解决方案(见下文),但我担心使用完整的 DT 两次重塑也会很慢(虽然可能更好比 for 循环)。关于如何按预期进行这项工作有什么建议吗?
整形方案,供参考:
test[,max_position:=names(.SD)[apply(.SD,1,function(x) which.max(x))],.SDcols=(2:4)]
test[,newmax:=get(max_position)-ttl*.1,by=1:nrow(test)]
test <- setDT(gather(test,idgroup,val,c(A,B,C)))
test[,maxval:=max(val),by='thing1']
test[val==maxval,val:=newmax][,maxval:=NULL]
test <- setDT(spread(test,idgroup,val))
使用 OP 的代码,replace
可以工作
test[, (2:4) := replace(.SD, which.max(.SD), max(.SD, na.rm = TRUE) - 0.1 * ttl),
by = 1:nrow(test),.SDcols = 2:4]
-输出
> test
thing1 A B C ttl
1: AAA 8.9 2.0 6.0 1
2: BBB 5.0 6.9 2.0 1
3: CCC 4.0 2.0 4.7 3
4: DDD 2.0 5.8 4.0 2
5: EEE 4.9 3.0 1.0 1
在 base R
中,使用 row/column 索引
可能会更快
test1 <- as.data.frame(test)
m1 <- cbind(seq_len(nrow(test1)), max.col(test1[2:4], "first"))
test1[2:4][m1] <- test1[2:4][m1] - 0.1 * test1$ttl
我正在编写一些代码,我需要在其中找到一组列的最大值,然后更新该最大值。考虑这个玩具示例:
test <- data.table(thing1=c('AAA','BBB','CCC','DDD','EEE'),
A=c(9,5,4,2,5),
B=c(2,7,2,6,3),
C=c(6,2,5,4,1),
ttl=c(1,1,3,2,1))
结果 data.table 如下所示:
thing1 | A | B | C | ttl |
---|---|---|---|---|
AAA | 9 | 2 | 6 | 1 |
BBB | 5 | 7 | 2 | 1 |
CCC | 4 | 2 | 5 | 3 |
DDD | 2 | 6 | 4 | 2 |
EEE | 5 | 3 | 1 | 1 |
目标是找到具有最大值的列(A
、B
或C
)并将该值替换为当前值减去0.1倍的值ttl
列(即 new_value=old_value - 0.1*ttl
)。其他列(不包含最大值)应保持不变。生成的 DT 应如下所示:
thing1 | A | B | C | ttl |
---|---|---|---|---|
AAA | 8.9 | 2 | 6 | 1 |
BBB | 5 | 6.9 | 2 | 1 |
CCC | 4 | 2 | 4.7 | 3 |
DDD | 2 | 5.8 | 4 | 2 |
EEE | 4.9 | 3 | 1 | 1 |
这样做的“明显”方法是编写一个 for 循环并循环遍历 DT 的每一行。这很容易做到,这就是我正在改编的代码所做的。然而,真正的 DT 比我的玩具示例大得多,for 循环需要一些时间 运行,这就是为什么我试图调整代码以利用矢量化并摆脱循环。
这是我目前的情况:
test[,max_position:=names(.SD)[apply(.SD,1,function(x) which.max(x))],.SDcols=(2:4)]
test[,newmax:=get(max_position)-ttl*.1,by=1:nrow(test)]
生成此 DT: |thing1|A|B|C|ttl|max_position|newmax| |------|-|-|-|---|-|-| |AAA|9|2|6|1|A|8.9| |BBB|5|7|2|1|B|6.9| |CCC|4|2|5|3|C|4.7| |DDD|2|6|4|2|B|5.8| |EEE|5|3|1|1|A|4.9|
问题在于将 newmax
列的值分配回需要去的地方。我天真地尝试了这个,连同其他一些东西,它告诉我“'max_position' not found”:
test[,(max_position):=newmax,by=1:nrow(test)]
通过重塑 DT 来解决问题很简单,这是我目前的解决方案(见下文),但我担心使用完整的 DT 两次重塑也会很慢(虽然可能更好比 for 循环)。关于如何按预期进行这项工作有什么建议吗?
整形方案,供参考:
test[,max_position:=names(.SD)[apply(.SD,1,function(x) which.max(x))],.SDcols=(2:4)]
test[,newmax:=get(max_position)-ttl*.1,by=1:nrow(test)]
test <- setDT(gather(test,idgroup,val,c(A,B,C)))
test[,maxval:=max(val),by='thing1']
test[val==maxval,val:=newmax][,maxval:=NULL]
test <- setDT(spread(test,idgroup,val))
使用 OP 的代码,replace
可以工作
test[, (2:4) := replace(.SD, which.max(.SD), max(.SD, na.rm = TRUE) - 0.1 * ttl),
by = 1:nrow(test),.SDcols = 2:4]
-输出
> test
thing1 A B C ttl
1: AAA 8.9 2.0 6.0 1
2: BBB 5.0 6.9 2.0 1
3: CCC 4.0 2.0 4.7 3
4: DDD 2.0 5.8 4.0 2
5: EEE 4.9 3.0 1.0 1
在 base R
中,使用 row/column 索引
test1 <- as.data.frame(test)
m1 <- cbind(seq_len(nrow(test1)), max.col(test1[2:4], "first"))
test1[2:4][m1] <- test1[2:4][m1] - 0.1 * test1$ttl