【问题标题】:How to efficiently replace ranges with their median in a dataframe如何有效地用数据框中的中值替换范围
【发布时间】:2019-09-04 17:14:35
【问题描述】:

假设我有一个名为 score.master 的数据框,如下所示:

school    perc.prof    num.tested
  A           8            482
  B          6-9           34
  C         40-49          49
  D          GE50          81
  E         80-89          26

这里,A 学校的精通百分比是 8%,测试的学生人数是 482。但是,假设当num.tested 低于某个数字(在本例中为任意 100)时,会引入数据抑制。在大多数情况下,会给出perc.prof 的范围,但在其他情况下会给出诸如“GE50”之类的值,表示大于或等于50。

我的问题是,在更大的数据集中,用中值替换范围的最佳方法是什么?例如,我希望最终的数据集如下所示:

    school    perc.prof    num.tested
      A           8            482
      B           8            34
      C           44           49
      D           75           81
      E           85           26

我知道这可以像这样手动完成:

score.master$perc.prof[score.master$perc.prof == "6-9"] <- round(median(6:9), 0)

但是实际的数据集有更多的范围组合。我想到选择正确值的一种方法是按长度;所有提供的值都是 1-2 个字符长(不超过 99% 的熟练度),而范围值是 3 个或更多字符长。

【问题讨论】:

  • 关于插补数据的问题的第二部分是统计方法问题,因此它与 SO 无关,但非常适合 Cross Validated。也许从这里删除这篇文章的那部分,并在简历上作为自己的问题发布
  • 完成,谢谢推荐。
  • 您打算如何标记要估算的组?在此示例中,您希望对除学校 A 之外的所有其他人都有估算值?那么你对学校 D 有什么期望呢?

标签: r range


【解决方案1】:

您可以使用 stringr::str_split() 获取下限和上限,然后计算中位数。 "GE50" 和类似的不能推广到这个,你可以使用 ifelse() 来处理特殊情况。

df <- data.frame(perc.prof = c('8', '6-9', '40-49', 'GE50', '80-89'))
df$lower.upper <- sapply(stringr::str_split(df$perc.prof, '-'), as.integer)
df$perc.prof.median <- sapply(df$lower.upper, median)
df$lower.upper <- NULL

> df
perc.prof perc.prof.median
1         8              8.0
2       6-9              7.5
3     40-49             44.5
4      GE50               NA
5     80-89             84.5

【讨论】:

    【解决方案2】:

    您可以执行以下操作以将您的范围转换为中位数。但是,我没有处理“GExx”或“LExx”的情况,因为它的定义不够明确。

    请注意,我的解决方案需要 stringr 包。

    score.master$perc.prof <- sapply(score.master$perc.prof, function(x){
      sep <- stringr::str_locate(x, "-")[, 1]
      if(is.na(sep)) {
        x
      } else {
        as.character(round(median(as.integer(stringr::str_sub(x, c(1L, sep+1), c(sep-1, -1L))))))
      }
    })
    

    【讨论】:

      【解决方案3】:

      这是一种 tidyverse 方法。首先,我将“GE50”替换为预期的输出,然后在可能的情况下使用tidyr::separate 拆分 perc.prof。最后一步,如果是大型学校,则使用给定的 perc.prof,或者对于小型学校,使用中位数。

      library(tidyverse)
      df %>%
        mutate(perc.prof = if_else(perc.prof == "GE50", "75", perc.prof)) %>%
        separate(perc.prof, c("low", "high"), remove = F, convert = T) %>% 
        mutate(perc.prof.adj = if_else(num.tested > 100, 
                                       as.numeric(perc.prof),
                                       rowSums(select(., low, high), na.rm = T)/2)
        )
      
        school perc.prof low high num.tested perc.prof.adj
      1      A         8   8   NA        482           8.0
      2      B       6-9   6    9         34           7.5
      3      C     40-49  40   49         49          44.5
      4      D        75  75   NA         81          37.5
      5      E     80-89  80   89         26          84.5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-31
        • 1970-01-01
        • 2020-03-24
        • 2018-01-08
        • 2020-03-16
        • 2020-03-16
        • 2020-03-14
        • 1970-01-01
        相关资源
        最近更新 更多