【问题标题】:Parse number to pull out mean of multiple Numbers from a column解析数字以从列中提取多个数字的平均值
【发布时间】:2019-12-01 17:20:39
【问题描述】:

所以我有一个如下所示的数据框,我所做的是通过简单地执行income=parse_number(family_income) 创建一个新列收入,但我想要的是两个数值的平均值然后显示为收入。

第一个条目的预期输出为 (75000+99999)/2 = 87499.5

我想使用现代 R 函数,如 parse_number 或其他更好的函数来有效地执行此操作。

library(tidyr)
family_income        income
   <chr>                 <dbl>
 1 $75,000 to $99,999    75000
 2 $50,000 to $74,999    50000
 3 $0 to $9,999              0
 4 $200,000 and up      200000
 5 $100,000 to $124,999 100000

【问题讨论】:

    标签: r parsing tidyverse tidyr


    【解决方案1】:

    一种方法是删除逗号,然后提取数字并取mean

    library(stringr)
    df$income <- purrr::map_dbl(str_extract_all(str_replace_all(df$family_income,
               ",", ""), "\\d+"), ~mean(as.numeric(.x)))
    
    df
    #         family_income   income
    #1   $75,000 to $99,999  87499.5
    #2   $50,000 to $74,999  62499.5
    #3         $0 to $9,999   4999.5
    #4      $200,000 and up 200000.0
    #5 $100,000 to $124,999 112499.5
    

    使用base R,我们可以使用regmatchesgregexpr

    temp <- gsub(",", "", df$family_income)
    sapply(regmatches(temp, gregexpr("\\d+", temp)), function(x) mean(as.numeric(x)))
    

    数据

    df <- structure(list(family_income = structure(c(5L, 4L, 1L, 3L, 2L
    ), .Label = c("$0 to $9,999", "$100,000 to $124,999", "$200,000 and up", 
    "$50,000 to $74,999", "$75,000 to $99,999"), class = "factor")), 
    row.names = c(NA,-5L), class = "data.frame")
    

    【讨论】:

    • 谢谢 Ronak, parse_number 能否以某种方式从第一列中提取两个数字(即使是两列),那么我可以理解这一点。我真的很难绕着正则表达式转头
    • @VaibhavSingh 我认为parse_number 只能从字符串中提取一个(第一个)数字,而不是多个数字。
    • 更快的基础 R 方法类似于 vapply(strsplit(gsub("(\\$|,| and up)", "", df$family_income), " to ", TRUE), function(x) mean(as.numeric(x)), numeric(1L))
    【解决方案2】:

    Base R 解决方案不如@Ronak Shah 雄辩:

    df$income <- do.call("rbind",
                        lapply(strsplit(gsub("[[:alpha:]]|\\s+", "",
                           gsub(" to ", ":",
                                gsub("[[:punct:]]", "", df$family_income))), ":"),
           function(x){mean(as.numeric(x))}))
    

    【讨论】:

    • 我真的看不出这个答案有什么问题(+1)。我可能会使用unlist 而不是使用do.call(rbind, ...),但既然我们已经知道我们将要简化,使用sapplyvapply 比使用unlist(lapply(...)) 更有意义。
    • 谢谢,是的,vapply/sapply 比取消列出返回的列表更好,而且嵌套的 gsub 感觉不太好。很好的解决方案,为评论和投票欢呼 - 回报了。
    【解决方案3】:

    另一种基本 R 方法可能是:

    vapply(strsplit(gsub("(\\$|,| and up)", "", df$family_income), " to ", TRUE), 
      function(x) mean(as.integer(x)), numeric(1L))
    

    这与 Ronak Shah 的 stringr + purrr 方法一样快,甚至比目前共享的现有基本 R 方法更快。

    如果需要更高的效率,可以考虑编写如下函数:

    library(data.table)
    parse_income <- function(instring) {
      as.data.table(instring)[
        , temp := gsub("(\\$|,| and up)", "", instring)][
          , c("v1", "v2") := tstrsplit(temp, " to ", fixed = TRUE, type.convert = TRUE)][
            , rowMeans(.SD, na.rm = TRUE), .SDcols = c("v1", "v2")]
    }
    

    然后可以这样使用:

    parse_income(df$family_income)
    ## [1]  87499.5  62499.5   4999.5 200000.0 112499.5
    

    这是一个从 Ronak Shah 回答中的样本数据开始的快速基准测试。待解析的数据已扩展至10,000个值。

    inc <- rep(df$family_income, 1e4/nrow(df)) # Adjust to get a sense of how each approach scales
    
    base_am <- function(instring) {
      vapply(strsplit(gsub("(\\$|,| and up)", "", instring), " to ", TRUE), 
             function(x) mean(as.integer(x)), numeric(1L))
    }
    
    base_rs <- function(instring) {
      temp <- gsub(",", "", instring)
      sapply(regmatches(temp, gregexpr("\\d+", temp)), function(x) mean(as.numeric(x)))
    }
    
    base_hf <- function(instring) {
      do.call("rbind",
              lapply(strsplit(gsub("[[:alpha:]]|\\s+", "",
                                   gsub(" to ", ":",
                                        gsub("[[:punct:]]", "", instring))), ":"),
                     function(x){mean(as.numeric(x))}))
    }
    
    stringi_rs <- function(instring) {
      purrr::map_dbl(str_extract_all(str_replace_all(instring, ",", ""), "\\d+"), ~mean(as.numeric(.x)))
    }
    
    bench::mark(base_am(inc), base_rs(inc), base_hf(inc), stringi_rs(inc), parse_income(inc), check = FALSE)
    ## # A tibble: 5 x 13
    ##   expression             min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result            memory          time   gc          
    ##   <bch:expr>        <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>            <list>          <list> <list>      
    ## 1 base_am(inc)          48ms   49.5ms     20.1   312.69KB     2.01    10     1      498ms <dbl [10,000]>    <df[,3] [4 × 3… <bch:… <tibble [11…
    ## 2 base_rs(inc)        99.4ms  127.6ms      8.38   80.04MB     2.79     3     1      358ms <dbl [10,000]>    <df[,3] [20,01… <bch:… <tibble [4 …
    ## 3 base_hf(inc)        67.1ms   68.4ms     14.4    547.2KB     2.06     7     1      485ms <dbl[,1] [10,000… <df[,3] [7 × 3… <bch:… <tibble [8 …
    ## 4 stringi_rs(inc)     50.3ms   51.8ms     19.1   324.16KB     2.12     9     1      472ms <dbl [10,000]>    <df[,3] [38 × … <bch:… <tibble [10…
    ## 5 parse_income(inc)   14.8ms     15ms     66.1     1.01MB     0       34     0      514ms <dbl [10,000]>    <df[,3] [30 × … <bch:… <tibble [34…
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2021-12-20
      • 2019-10-05
      • 2020-06-15
      相关资源
      最近更新 更多