【问题标题】:R - overcome "cut" ignoring values outside of range in data tableR - 克服“剪切”忽略数据表中范围之外的值
【发布时间】:2018-02-16 23:38:16
【问题描述】:

我正在比较为期两年的每日土壤湿度 (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 之间,因此结果数据表tmp1 中根本没有0.6-0.8 类别。

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

有谁知道如何解决这个问题,即如何“强制”cut 考虑超出范围的值?我需要 tmp1 和 tmp2 中的所有 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 

任何意见表示赞赏。

【问题讨论】:

  • cut 有 right 和 include.lowest 这样的参数,我相信它们可以解决您的问题
  • @MichaelChirico 不,不幸的是,这些论点并没有解决 cut 忽略“空”垃圾箱。

标签: r data.table categories binning


【解决方案1】:

使用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 执行此操作。重要的是您如何将结果制成表格,而不是如何对它们进行分组...

【讨论】:

  • Fwiw,我写了另一个使用 CJ 填充级别的示例franknarf1.github.io/r-tutorial/_book/…
  • 非常感谢您的回答,我仍在我的真实数据集上验证它,但它似乎工作正常。有什么方法可以解释NA 值的比例吗?我仍在尝试完全消化这段代码......
  • @thiagoveloso 哦,我忘了包括那些,但我认为我的编辑修复了它。
  • 成功了,非常感谢。由于我们没有使用cut,所以我有一个问题。这种方法中的“边界”是如何计算的?例如,0.2 的值将被归类为 0-0.2 或 0.2-0.4?
  • @thiagoveloso 间隔类似于 [0.0, 0.2), [0.2, 0.4), .... [0.8, Inf),低于 0.0 的任何值都被分配为 NA。之所以如此,是因为我们将roll 设置为下面最接近的值。请参阅?data.table,了解如何改为滚动到上面最接近的值(...我认为是roll = -Inf)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-18
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2022-10-23
  • 1970-01-01
相关资源
最近更新 更多