R data.table 无需 for 循环即可从函数和连接设置列值的方法?

问题描述 投票:0回答:1

有没有一种方法使用 R data.table 来设置需要两级函数调用才能设置的列值,有没有一种方法可以在两个 data.table 之间的联接中设置列值?

示例:这可行,但使用了 for 循环。

library(data.table)

# use something from datasets to illusrate
tAmounts<-data.table(rbind(cbind(ID="Apple", Amt=as.numeric(EuStockMarkets[1:50,2])),
                           cbind(ID="Orange", Amt=as.numeric(EuStockMarkets[1:30,3])),
                           cbind(ID="Lemon", Amt=as.numeric(EuStockMarkets[1:60,4]))))
setkey(tAmounts, ID, Amt)

# is there a data.table way to do this without the for loops?

# summary table with the full hierarchical cluster for each ID
tSummary<-tAmounts[, .(.N, Clust=list()), keyby="ID"]
for (Id in unique(tAmounts$ID)) {
  D<-dist(tAmounts[ID==Id]$Amt)
  C<-hclust(D, method="average")
  tSummary[ID==Id]$Clust<-list(C) # any way to mapply & lapply?
}
# ID      N      Clust
# Apple  50 <hclust[7]>
# Lemon  60 <hclust[7]>
# Orange 30 <hclust[7]>

也许有一种方法可以使用 lapply 和 mapply 的某种组合来表达类似

tSummary[, Clust:=hcust(dist(Amt), method="average"), by="ID")
的内容?

同样,有没有办法使用函数在连接中设置列?继续上面的例子:

# table of hierarchical cluster cuts, e.g., height of $20, height of $40
tCuts<-CJ(ID=unique(tAmounts$ID), Cut=seq(20,100,20))
setkey(tCuts, ID, Cut)
# ID    Cut
# Apple  20
# Apple  40
# ...etc...

# table with clusters taken at each cut
tClust<-tCuts[tAmounts, on="ID", allow.cartesian=TRUE]
setkey(tClust, ID, Cut, Amt)
# ID Cut    Amt
# Apple  20 1587.4
# Apple  20 1630.6
# ...etc...
# Orange 100 1789.5 

# set ClustNum for each ID, cut, and amount
for (i in 1:nrow(tCuts)) {
  Id<-tCuts[i]$ID
  tClust[ID==Id & Cut==tCuts[i]$Cut, ClustNum:=cutree(tSummary[ID==Id]$Clust[[1]], h=tCuts[i]$Cut)] # any way to mapply in a join?
}

有没有像

tClust[tCuts, ClustNum:=cutree(Clust, h=Cut)]
这样可以一次性加入并设置值的东西?

r data.table
1个回答
0
投票

解决方案:

# Part 1: Table of counts and hclust objects, one for each ID
tSummary <- 
  tAmounts[, .(.N, Clust = list(hclust(dist(Amt), method="average"))), by="ID"]

# Part 2: Clusters at heights 20, 40, ..., 100
heights <- seq(20,100,20)
tClust <-
  tSummary[, .(Cut = rep(heights, each=N),
               ClustNum = unlist(cutree(Clust[[1]], h=heights)), by=ID)
           , by=ID
           ][, Amt := tAmounts[, rep(Amt, times=length(heights)), by=ID][, ID := NULL]]

说明:

第 1 部分只需将 j 表达式的 RHS 包装在

list()
内。 在第 2 部分中,集群标签的
ClustNum
列可以直接从第 1 部分创建(无需交叉联接和中间表)。
by=ID
负责对 ID 进行迭代,而要在不循环的情况下迭代高度,我们可以使用
apply
函数:

tSummary[, unlist(lapply(heights, function(h) cutree(Clust[[1]], h=h))), by=ID]

但事实上

cutree
接受高度向量,所以即使这样也不是必要的:

tSummary[, unlist(cutree(Clust[[1]], h=heights)), by=ID]

Cut
Amt
列被“拼接”(后者依赖于
tAmounts
ID
键入)。

示例数据(确保

Amt
列数字):

library(data.table)
tAmounts <- rbind(
      data.frame(ID="Apple", Amt=as.numeric(EuStockMarkets[1:50,2])),
      data.frame(ID="Orange", Amt=as.numeric(EuStockMarkets[1:30,3])),
      data.frame(ID="Lemon", Amt=as.numeric(EuStockMarkets[1:60,4]))
    ) |> setDT()
    setkey(tAmounts, ID)

感谢@r2evans 的帮助

© www.soinside.com 2019 - 2024. All rights reserved.