代码之家  ›  专栏  ›  技术社区  ›  Nettle

使用tidyverse join更新/替换数据帧中的值

  •  2
  • Nettle  · 技术社区  · 8 年前

    用查找表中的(正确的)值更新/替换主数据集中的NAS最有效的方法是什么?这是一个很普通的手术!类似的问题似乎没有整齐的解决方案。

    制约因素: 1)请假设有大量的缺失值和比给出的示例更大的查找表。因此,根据具体情况进行替换操作是不切实际的(不 case_when , if_else 等)

    2)查找表没有主数据帧的所有值,只有替换值。

    最好用tidyverse解决方案回答。类似的问题似乎没有整齐的解决方案。

    library(tidyverse)
    
    ### Main Dataframe ###
    df1 <- tibble(
      state_abbrev = state.abb[1:10],
      state_name = c(state.name[1:5], rep(NA, 3), state.name[9:10]),
      value = sample(500:1200, 10, replace=TRUE)
    )
    
    
    #> # A tibble: 10 x 3
    #>    state_abbrev state_name value
    #>    <chr>        <chr>      <int>
    #>  1 AL           Alabama      551
    #>  2 AK           Alaska       765
    #>  3 AZ           Arizona      508
    #>  4 AR           Arkansas     756
    #>  5 CA           California   741
    #>  6 CO           <NA>        1100
    #>  7 CT           <NA>         719
    #>  8 DE           <NA>         874
    #>  9 FL           Florida      749
    #> 10 GA           Georgia      580
    
    
    ### Lookup Dataframe ###
    lookup_df <- tibble(
      state_abbrev = state.abb[6:8],
      state_name = state.name[6:8]
    )
    
    #> # A tibble: 3 x 2
    #>   state_abbrev state_name 
    #>   <chr>        <chr>      
    #> 1 CO           Colorado   
    #> 2 CT           Connecticut
    #> 3 DE           Delaware
    

    理想情况下,左连接对于缺少的值有一个替换选项。唉…

    left_join(df1, lookup_df)
    #> Joining, by = c("state_abbrev", "state_name")
    #> # A tibble: 10 x 3
    #>    state_abbrev state_name value
    #>    <chr>        <chr>      <int>
    #>  1 AL           Alabama      551
    #>  2 AK           Alaska       765
    #>  3 AZ           Arizona      508
    #>  4 AR           Arkansas     756
    #>  5 CA           California   741
    #>  6 CO           <NA>        1100
    #>  7 CT           <NA>         719
    #>  8 DE           <NA>         874
    #>  9 FL           Florida      749
    #> 10 GA           Georgia      580
    

    ` `

    创建日期:2018-07-28 reprex package (v0.2.0)。

    4 回复  |  直到 7 年前
        1
  •  5
  •   Uwe    8 年前
    Nettle's 建议并转化为有效的解决方案

    df1 %>% 
      left_join(lookup_df, by = "state_abbrev") %>% 
      mutate(state_name = coalesce(state_name.x, state_name.y)) %>% 
      select(-state_name.x, -state_name.y)
    
    # A tibble: 10 x 3
       state_abbrev value state_name 
       <chr>        <int> <chr>      
     1 AL             671 Alabama    
     2 AK             501 Alaska     
     3 AZ            1030 Arizona    
     4 AR             694 Arkansas   
     5 CA             881 California 
     6 CO             821 Colorado   
     7 CT             742 Connecticut
     8 DE             665 Delaware   
     9 FL             948 Florida    
    10 GA             790 Georgia
    

    运营商表示更喜欢“tidyverse”解决方案。然而, 更新连接 已经可以使用 data.table 包裹:

    library(data.table)
    setDT(df1)[setDT(lookup_df), on = "state_abbrev", state_name := i.state_name]
    df1
    
        state_abbrev  state_name value
     1:           AL     Alabama  1103
     2:           AK      Alaska  1036
     3:           AZ     Arizona   811
     4:           AR    Arkansas   604
     5:           CA  California   868
     6:           CO    Colorado  1129
     7:           CT Connecticut   819
     8:           DE    Delaware  1194
     9:           FL     Florida   888
    10:           GA     Georgia   501
    

    基准

    library(bench)
    bm <- press(
      na_share = c(0.1, 0.5, 0.9),
      n_row = length(state.abb) * 2 * c(1, 100, 10000),
      {
        n_na <- na_share * length(state.abb)
        set.seed(1)
        na_idx <- sample(length(state.abb), n_na)
        tmp <- data.table(state_abbrev = state.abb, state_name = state.name)
        lookup_df <-tmp[na_idx] 
        tmp[na_idx, state_name := NA]
        df0 <- as_tibble(tmp[sample(length(state.abb), n_row, TRUE)])
        mark(
          dplyr = {
            df1 <- copy(df0)
            df1 <- df1 %>% 
              left_join(lookup_df, by = "state_abbrev") %>% 
              mutate(state_name = coalesce(state_name.x, state_name.y)) %>% 
              select(-state_name.x, -state_name.y)
            df1
          },
          upd_join = {
            df1 <- copy(df0)
            setDT(df1)[setDT(lookup_df), on = "state_abbrev", state_name := i.state_name]
            df1
          }
        )
      }
    
    )
    ggplot2::autoplot(bm)
    

    enter image description here

    数据表 的upate连接总是更快(请注意日志时间刻度)。

    作为 更新连接 修改数据对象,每个基准运行都使用一个新的副本。

        2
  •  2
  •   alistaire    8 年前

    目前没有一次尝试合并多个列(可以使用 a lookup table approach ifelse(is.na(value), ..., value) there has been discussion coalesce put it in a function

    library(tidyverse)
    
    df1 <- tibble(
        state_abbrev = state.abb[1:10],
        state_name = c(state.name[1:5], rep(NA, 3), state.name[9:10]),
        value = sample(500:1200, 10, replace=TRUE)
    )
    
    lookup_df <- tibble(
        state_abbrev = state.abb[6:8],
        state_name = state.name[6:8]
    )
    
    df1 %>% 
        full_join(lookup_df, by = 'state_abbrev') %>% 
        bind_cols(map_dfc(grep('.x', names(.), value = TRUE), function(x){
            set_names(
                list(coalesce(.[[x]], .[[gsub('.x', '.y', x)]])), 
                gsub('.x', '', x)
            )
        })) %>% 
        select(union(names(df1), names(lookup_df)))
    #> # A tibble: 10 x 3
    #>    state_abbrev state_name  value
    #>    <chr>        <chr>       <int>
    #>  1 AL           Alabama       877
    #>  2 AK           Alaska       1048
    #>  3 AZ           Arizona       973
    #>  4 AR           Arkansas      860
    #>  5 CA           California    938
    #>  6 CO           Colorado      639
    #>  7 CT           Connecticut   547
    #>  8 DE           Delaware      672
    #>  9 FL           Florida       667
    #> 10 GA           Georgia      1142
    
        3
  •  1
  •   GG LEE    7 年前

    df1 %>% 
      left_join(lookup_df, by = "state_abbrev") %>% 
      mutate(state_name.x = coalesce(state_name.x, state_name.y)) %>% 
      rename(state_name = state_name.x) %>%
      select(-state_name.y)
    
        4
  •  -1
  •   sbha    8 年前

    left_join(df1 %>% select(-state_name), lookup_df, by = 'state_abbrev') %>% 
      select(state_abbrev, state_name, value)
    

    match if_else mutate

    df1 %>% 
      mutate(state_name = if_else(is.na(state_name), state.name[match(state_abbrev,state.abb)], state_name))
    

    # A tibble: 10 x 3
       state_abbrev state_name  value
       <chr>        <chr>       <int>
     1 AL           Alabama       525
     2 AK           Alaska        719
     3 AZ           Arizona      1186
     4 AR           Arkansas     1051
     5 CA           California    888
     6 CO           Colorado      615
     7 CT           Connecticut   578
     8 DE           Delaware      894
     9 FL           Florida       536
    10 GA           Georgia       599       
    
    推荐文章