R - cut2 - 一个零为零,另一个为10

时间:2017-09-26 15:26:34

标签: r data.table hmisc

我有一个带有偏斜分布的数据集,我希望将其分成具有相同数量观察值的区间 - 除了偏斜。

为了澄清,我有大量的零,而且数量相对较少的> 0观察。

如果我使用cut2将值分成10个分区,包括零,我得到以下内容。

library(data.table)
library(Hmisc)

DT<-data.table(x=rep(0,100), y=rep(0,100))
DT<-rbind(DT, data.table(x=seq(1:100),y=seq(1:100)))
DT
       x   y
  1:   0   0
  2:   0   0
  3:   0   0
  4:   0   0
  5:   0   0
 ---        
196:  96  96
197:  97  97
198:  98  98
199:  99  99
200: 100 100

只看x

data.table(DT[, cut2(x, g=10)])[,.N, by=V1]
         V1   N
1:        0 100
2: [ 1, 21)  20
3: [21, 41)  20
4: [41, 61)  20
5: [61, 81)  20
6: [81,100]  20

因此,存在100个零点已经将箱子移动到将零以上的观察结果聚集成5个桶的点。

如果我专门过滤掉零并应用10个分档,我会按预期得到以下内容...

data.table(DT[x>0, cut2(x, g=10)])[,.N, by=V1]
          V1  N
 1: [ 1, 11) 10
 2: [11, 21) 10
 3: [21, 31) 10
 4: [31, 41) 10
 5: [41, 51) 10
 6: [51, 61) 10
 7: [61, 71) 10
 8: [71, 81) 10
 9: [81, 91) 10
10: [91,100] 10

我想做的是有11个桶 - 一个用于零,10个用于非零。我当然可以将它应用于2个单独的操作,如此

DT[x==0, bin:=cut2(x, g=1)]
Warning message:
In min(diff(x.unique)) : no non-missing arguments to min; returning Inf

DT[x>0, bin:=cut2(x, g=10)]
DT[, .(min(x), max(x)), by=bin]
         bin V1  V2
 1:        0  0   0
 2: [ 1, 11)  1  10
 3: [11, 21) 11  20
 4: [21, 31) 21  30
 5: [31, 41) 31  40
 6: [41, 51) 41  50
 7: [51, 61) 51  60
 8: [61, 71) 61  70
 9: [71, 81) 71  80
10: [81, 91) 81  90
11: [91,100] 91 100

但是我必须为y重复这两个相同的操作。我的实际数据表有大约30列,所以我想知道是否有:

  1. 一个快捷方式,允许我将这两个操作一起应用于单个列?
  2. 另一个快捷方式,允许我将这两个操作应用于30列的列表?
  3. 我能看到的关键位是过滤器部分 - 分布都倾向于零,但每列包含不同数量的观察结果,并且会有不同的容器。

    任何指针都将不胜感激。

    大卫

    P.S。希望帖子的布局能够让你可以剪切和粘贴代码 - 为了清晰起见我包含了输出,但如果有问题请告诉我。

    修改 在查看@ Eddi的答案并应用我的数据后,我发现我提供的数据与我实际运行的数据存在问题。

    这些是在我的数据上使用Eddi方法运行的结果(名字被屏蔽)......

    > data.table(XXX[, cut2(yyy, m = 
    sum(yyy > 0)/10)])[, .N, by = V1]
                  V1   N
     1: [   0,    4) 284
     2: [3891,72337] 264
     3: [1212, 3891) 264
     4: [ 519, 1212) 264
     5: [ 208,  519) 263
     6: [  49,  101) 267
     7: [ 101,  208) 263
     8: [  11,   24) 258
     9: [  24,   49) 263
    10: [   4,   11) 252
     > XXX[yyy==0, .N,]
     [1] 74
    > XXX[yyy>=0, .N,]
    [1] 2642
    

    我更新了测试数据集,以便按如下方式重现这些结果 - 主要是将潜在值的范围扩展到70,000并随机生成它们而不是按顺序生成。还生成2700个而不是100个左右我正在使用

    DT<-data.table(x=rep(0,100), y=rep(0,100))
    DT<-rbind(DT, data.table(x=runif(2600,1,70000),y=runif(2600,1,70000)))
    DT
    data.table(DT[, cut2(x, m = sum(x > 0)/10)])[, .N, by = V1]
    
               V1   N
     1: [    0, 4798) 270
     2: [41289,48407) 270
     3: [11482,18413) 270
     4: [48407,55678) 270
     5: [55678,62157) 270
     6: [33040,41289) 270
     7: [25470,33040) 270
     8: [ 4798,11482) 270
     9: [62157,69983] 270
    10: [18413,25470) 270
    

    所以这已经将零桶卷入其余的10个桶中 - 不可否认,这并没有大规模地影响分布,但它是这种方法似乎稍微分解的一个列。

    对于测试数据存在错误的道歉,我没有预料到这种影响。

    欢迎任何关于如何进行的想法......

    干杯

    大卫

1 个答案:

答案 0 :(得分:1)

在得到Eddi的反馈和我自己的实验之后,我决定在列列表中使用for循环,特别是首先对零进行分区,然后分别对非零进行分区。

DT<-data.table(x=rep(0,100), y=rep(0,100))
DT<-rbind(DT, data.table(x=runif(100,1,10000),y=runif(100,1,10000)))
DT


cols <- data.table(col_name=c("x","y"))


for(col in 1:nrow(cols)){

    DT[get(cols[col, col_name])==0,(paste(cols[col,col_name],"_bin",sep="")):= cut2(get(cols[col, col_name]),g=1)]
    DT[get(cols[col, col_name])>0,(paste(cols[col, col_name],"_bin",sep="")):= cut2(get(cols[col, col_name]),g=10)]

}  

data.table(DT[, cut2(x, m = sum(x > 0)/10)])[, .N, by = V1]

                   V1   N
 1:              0.00 100
 2: [2540.22,4009.79)  10
 3: [4923.05,5736.81)  10
 4: [4009.79,4923.05)  10
 5: [ 910.57,1563.99)  10
 6: [5736.81,6121.23)  10
 7: [   9.77, 910.57)  10
 8: [9240.77,9957.27]  10
 9: [1563.99,2540.22)  10
10: [6121.23,7759.80)  10
11: [7759.80,9240.77)  10