R - 克服“cut”忽略数据表中范围之外的值

时间:2018-02-16 23:38:17

标签: r data.table categories binning

我正在比较两年的土壤水分(SM)测量值。在一年内,SM的范围从0到0.6。 在另一年,下雨多了,SM从0到0.8不等。在数据中,我还有一些NA's,其中SM传感器由于某种原因不起作用。 让我们重新创造类似的东西:

library(data.table)
set.seed(24)
dt1 <- data.table(date=seq(as.Date("2015-01-01"), length.out=365, by="1 day"), 
                  sm=sample(c(NA, runif(10, min=0, max=0.6)), 365, replace = TRUE))

dt2 <- data.table(date=seq(as.Date("2015-01-01"), length.out=365, by="1 day"), 
                  sm=sample(c(NA, runif(10, min=0, max=0.8)), 365, replace = TRUE))

我试图根据每个月SM类之间的值比例来比较两个数据集。 我感兴趣的课程是seq(0, 0.8, by=0.2)。我还需要计算每月失败测量的比例(NA)。

我设法通过akrun这里有用的答案来做到这一点: R - Calculate percentage of occurrences in data.table by month

tmp1 <- dt1[, n := .N, month(date)][, .(perc=100 * .N/n[1]),
                                    by=.(month=month(date),
                                         grp=cut(sm, breaks=seq(0, 0.8, by=0.2),
                                                 labels = c('0-0.2', '0.2-0.4', '0.4-0.6', '0.6-0.8')))]

tmp2 <- dt2[, n := .N, month(date)][, .(perc=100 * .N/n[1]),
                                    by=.(month=month(date),
                                         grp=cut(sm, breaks=seq(0, 0.8, by=0.2),
                                                 labels = c('0-0.2', '0.2-0.4', '0.4-0.6', '0.6-0.8')))]

然而,输出并不完全符合我的预期。 由于dt1中的值范围仅为0到0.6,因此结果数据表0.6-0.8中根本没有tmp1类别。

看起来cut忽略了最后一个类别(0.6-0.8),因为该范围内没有SM测量。这使得我的比较非常不方便,因为我在结果数据表tmp1tmp2中没有相同的组。

有人知道如何解决这个问题,即如何“强迫”cut考虑超出休息范围的值?我需要tmp1tmp2中的所有SM类别,即使它们的计数为0。

作为参考,如果我们使用table,即使其计数为零,也始终显示所有类别,此问题不会发生:

t1 <- runif(10, 0, 0.6)
t2 <- runif(10, 0, 0.8)

table(cut(t1, breaks=seq(0, 0.8, by=0.2)))

  (0,0.2] (0.2,0.4] (0.4,0.6] (0.6,0.8] 
        5         3         2         0 
table(cut(t2, breaks=seq(0, 0.8, by=0.2)))

  (0,0.2] (0.2,0.4] (0.4,0.6] (0.6,0.8] 
        1         3         2         4 

任何意见都赞赏。

1 个答案:

答案 0 :(得分:2)

使用CJ计算所有级别,即使是那些未出现在表格中的级别:

f = function(d){

    # create month column
    d[, month := month(date)]

    # roll to make cut-group column
    mdt = data.table(sm = c(NA, seq(0, .8, by=.2)))
    d[, lb := mdt[.SD, on=.(sm), roll=TRUE, x.sm]]

    # join with CJ to ensure all levels are present
    res = d[CJ(month = month, lb = mdt$sm, unique = TRUE), on=.(month, lb), .N, by=.EACHI]

    # rescale to monthly pct
    res[, pct := N/sum(N), by=month][]

}

# try it
f(dt1)
f(dt2)

您也可以使用cut执行此操作。重要的是你如何制表结果,而不是你如何对它们进行分组......