【问题标题】:Loop in R data.table: Create groups by variable distribution, then compute means by new groupsR data.table 中的循环:按变量分布创建组,然后按新组计算均值
【发布时间】:2017-09-22 22:15:23
【问题描述】:

我有一组变量(大约 21 个),我想循环并为每个变量执行以下操作: 1. 分组为 10 组,按年份,分组由年度分布的十分位数确定 2. 根据这些新组计算均值(相等且加权)。

测试数据:

set.seed(4)
YR = data.table(yr=1962:2015)
ID = data.table(id=10001:11000)
DT <- YR[,as.list(ID), by = yr] # intentional cartesian join
rm("YR","ID")
# 54,000 obs now add data
DT[,`:=` (ratio = rep(sample(10),each=5400)+rnorm(nrow(DT)),
          ratio2 = rep(sample(5),each=10800)+rnorm(nrow(DT)),
          weight = abs(rnorm(nrow(DT)))*100,
          val = rnorm(nrow(DT))
            )]
DT
         yr    id     ratio   ratio2    weight        val
    1: 1962 10001  6.689275 4.895357 129.10487 -0.2022073
    2: 1962 10002  4.718753 4.505419 140.70420 -0.0887587
    3: 1962 10003  5.786855 4.359488 242.10988  0.9511465
    4: 1962 10004  7.896540 4.049974  89.23235 -1.3822148
    5: 1962 10005  7.776863 2.233036 177.79650 -1.0671091
   ---                                                   
53996: 2015 10996 10.613272 3.345091 153.81424  0.9269429
53997: 2015 10997 11.260932 1.804315  15.68129 -1.6618414
53998: 2015 10998  8.591909 3.332643 134.80929 -1.1632596
53999: 2015 10999  9.143039 3.012160 178.77301 -0.4761060
54000: 2015 11000  7.470945 4.068919 121.13470 -1.7594423

所以,我想循环遍历 ratio,然后是 ratio2 等,计算每一个的十分位数,然后通过每个新计算的十分位数来总结 val。请注意,这些不是编号变量,因此我无法使用 paste() 和 1:21 向量重新创建名称。 首先,我编写了这个函数来进行分组:

# [function] pctl.grp - order data into groups based on percentil breakpoints
# Number of groups passed 
pctl.grp <- function(dat, grp) {
  bp <- quantile(dat, probs = c(0,seq(100/grp,100,100/grp))/100)
  cut(dat,bp,labels = FALSE, include.lowest = TRUE)
}

然后我可以像这样进行一次迭代:

# adds in new variable containing 10 groups numbered 1-10
DT[,ratiogrp := lapply(.SD, pctl.grp, 10), by = .(yr), .SDcols = c("ratio")]

DT[,.(ewval = mean(val), 
      ewratio = mean(ratio),
      vwval = weighted.mean(val, weight, na.rm = TRUE), 
      vwratio = weighted.mean(ratio, weight, na.rm = TRUE))  ,by=ratiogrp][order(ratiogrp)]

这给出了预期的结果:

    ratiogrp        ewval  ewratio        vwval  vwratio
 1:        1 -0.027994385 3.576939 -0.039512050 3.572319
 2:        2 -0.001146009 4.329835  0.005093692 4.331433
 3:        3 -0.009087386 4.784103 -0.012764902 4.767494
 4:        4 -0.014961467 5.094431 -0.015464918 5.110614
 5:        5  0.014705294 5.373705  0.015276699 5.364962
 6:        6 -0.010195630 5.645182 -0.014102394 5.618484
 7:        7  0.001297953 5.949583 -0.012839401 5.925634
 8:        8 -0.009300910 6.265297 -0.007141404 6.263371
 9:        9  0.012970539 6.651047  0.018474949 6.684825
10:       10  0.003841495 7.363449 -0.004225650 7.351828

但是我如何在每个变量中循环 21 次呢?我可以像这样轻松获取变量的名称:

> grep(c("ratio"), names(DT))
[1] 3 4
> names(DT)[grep(c("ratio"), names(DT))]
[1] "ratio"  "ratio2"

所以想想for (z in 1:length(namelist)) {} 或其他东西会起作用。但我不确定如何在 data.table 结构中引用这些名称(或数字)来重新创建我在上面所做的。

【问题讨论】:

    标签: r loops data.table


    【解决方案1】:

    进入长格式...

    mDT = melt(DT, meas=patterns("ratio"), value.name = "ratio")
    setorder(mDT, variable, yr, ratio)
    mDT[, dec := cut(.I, 10, labels = FALSE), by=.(yr, variable)]
    
    mDT[, .(
      mval = mean(val), 
      mrat = mean(ratio), 
      wmval = weighted.mean(val, weight), 
      wmrat = weighted.mean(ratio, weight)
    ), keyby=.(variable, dec)]
    
        variable dec          mval     mrat        wmval    wmrat
     1:    ratio   1 -0.0279943849 3.576939 -0.039512050 3.572319
     2:    ratio   2 -0.0011460087 4.329835  0.005093692 4.331433
     3:    ratio   3 -0.0090873863 4.784103 -0.012764902 4.767494
     4:    ratio   4 -0.0149614666 5.094431 -0.015464918 5.110614
     5:    ratio   5  0.0147052939 5.373705  0.015276699 5.364962
     6:    ratio   6 -0.0101956297 5.645182 -0.014102394 5.618484
     7:    ratio   7  0.0012979528 5.949583 -0.012839401 5.925634
     8:    ratio   8 -0.0093009096 6.265297 -0.007141404 6.263371
     9:    ratio   9  0.0129705386 6.651047  0.018474949 6.684825
    10:    ratio  10  0.0038414948 7.363449 -0.004225650 7.351828
    11:   ratio2   1 -0.0120823787 1.195964 -0.016154551 1.199026
    12:   ratio2   2 -0.0072534833 1.904354 -0.030409684 1.908494
    13:   ratio2   3 -0.0283728080 2.282277 -0.028168936 2.301685
    14:   ratio2   4 -0.0068901529 2.590815  0.002836866 2.585152
    15:   ratio2   5 -0.0035769658 2.880104  0.002391468 2.872702
    16:   ratio2   6  0.0087575593 3.147469  0.004565452 3.134459
    17:   ratio2   7 -0.0052354409 3.412187 -0.005866282 3.426711
    18:   ratio2   8  0.0123337036 3.704371  0.009488475 3.701694
    19:   ratio2   9  0.0027419978 4.071582 -0.008958386 4.076264
    20:   ratio2  10 -0.0002925368 4.786477  0.003691116 4.772209
    

    【讨论】:

    • 太棒了。不过,我不确定为什么 cut(I., 10, labels=FALSE) 给出的结果与使用我的函数相同。我用我的 pctl.grp() 函数替换了 cut() 并得到了我想要的。
    • @JesseBlocher 很酷,很高兴它有效。 .I只是连续行号的向量(因为数据是按yr和变量排序的),一些向量n:mcut(n:m, 10, labels=FALSE) 会将行分成 10 个大小相等的组;由于数据是按比率排序的,因此这些组是它的十分位数。 labels=FALSE 部分意味着它将整数分配给十分位数。有点令人费解,我知道....
    • 我很确定cut(x,10) 没有将行放入相同大小的组中。如果你做x &lt;-rnorm(100),然后c &lt;- cut(x,10,labels=FALSE) 并做一个c 的直方图,你应该得到相等的高度条,但你没有。比较我的功能。这不是重点——我认为我的示例数据是随机统一的,所以两者看起来是一样的。
    • @Jesse “这不是重点”?我从未说过 cut(x, 10) 会将任何向量分成 10 个相等的组。就像我说的那样,原因比这要复杂得多。它之所以有效,是因为在按一些x 对行进行排序之后,我正在剪切一个连续行号的向量,例如n:m。请改用DT = data.table(x = rnorm(100)); setorder(DT, x); DT[, dec := cut(.I, 10, labels=FALSE)][]
    • 我没有批评的意思。实际上恰恰相反-您的方法解决了主要问题。我指出分箱功能是主要问题的外围设备。不过,我现在明白你的意思了。您将.I 传递给cut(),而不是数据,因此剪切发生在行号集上,因此将它们放在相等的组中。我不熟悉 data.table 的 .I 功能。
    猜你喜欢
    • 1970-01-01
    • 2019-09-05
    • 2019-12-29
    • 1970-01-01
    • 2016-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-30
    相关资源
    最近更新 更多