【问题标题】:show unique values for each column显示每列的唯一值
【发布时间】:2019-07-24 22:09:28
【问题描述】:

我正在尝试为每列创建列类型和唯一变量的数据框。

我能够使用map(df, class) %>% bind_rows() %>% gather(key = col_name, value = col_class) 获得所需数据框格式的列类型,但无法将唯一变量变成数据框而不是列表。

下面是一个小数据框和代码,用于获取列表中的唯一变量,但不是数据框。理想情况下,我可以在一个(地图)函数中做到这一点,但如果我必须加入它们,那也没什么大不了的。


df <- data.frame(v1 = c(1,2,3,2), v2 = c("a","a","b","b"))

library(tidyverse)

map(df, class) %>% bind_rows() %>% gather(key = col_name, value = col_class)

map(df, unique)

当我尝试在 map(df, unique) 上使用与 map(df, class) 上相同的方法时,我收到以下错误:Error: Argument 2 must be length 3, not 2 这是预期的,但我不知道如何解决它。

【问题讨论】:

  • 你想要的输出是什么?
  • 另外,代替bind_rows %&gt;% spread(...) 这样做map(df, class) %&gt;% enframe() %&gt;% unnest()
  • @M-M 我没听说过enframe()unnest()。我正在努力理解文档,但是与 bind_rows() %&gt;% gather() 相比有什么好处?

标签: r dplyr purrr


【解决方案1】:

这两列中唯一值的数量不同。您需要将它们简化为单个元素。

df2 <- map(df, ~str_c(unique(.x),collapse = ",")) %>% 
    bind_rows() %>% 
    gather(key = col_name, value = col_unique)
> df2
# A tibble: 2 x 2
  col_name col_class
  <chr>    <chr>    
1 v1       1,2,3    
2 v2       a,b   

【讨论】:

    【解决方案2】:

    我们可以使用map_df 并将每一列中的classunique 值合并到一个tibble 中。由于每一列都有不同类型的变量,我们需要将它们放在一个公共类中,以便将数据绑定到一个数据框中。

    purrr::map_df(df,~tibble::tibble(class = class(.), value = as.character(unique(.))))
    
    #  class  value
    #  <chr>  <chr>
    #1 numeric 1    
    #2 numeric 2    
    #3 numeric 3    
    #4 factor  a    
    #5 factor  b    
    

    或者,如果您希望每一列只有一个值,我们可以这样做

    map_df(df, ~tibble(class = class(.), value = toString(unique(.))))
    
    #  class   value  
    #  <chr>   <chr>  
    #1 numeric 1, 2, 3
    #2 factor  a, b   
    

    在使用lapply的基础R中相同

    do.call(rbind, lapply(df, function(x) 
           data.frame(class = class(x), value = as.character(unique(x)))))
    

    do.call(rbind, lapply(df, function(x) 
            data.frame(class = class(x), value = toString(unique(x)))))
    

    【讨论】:

      【解决方案3】:

      为了解决 OP 关于enframeunnest 的评论,我设置了一个基准。

      set.seed(123)
      df <- data.frame(v1 = sample(1:100000,10000000, replace = TRUE), 
                       v2 = sample(c(letters,LETTERS),10000000, replace = TRUE))
      library(tidyverse)
      
      map(df, ~str_c(unique(.x),collapse = ",")) %>% 
        bind_rows() %>% 
        gather(key = col_name, value = col_unique)
      #> # A tibble: 2 x 2
      #>   col_name col_unique                                                      
      #>   <chr>    <chr>                                                           
      #> 1 v1       51663,57870,2986,29925,95246,68293,62555,45404,65161,46435,9642~
      #> 2 v2       S,V,k,t,z,K,f,J,n,R,W,h,M,P,q,g,C,U,a,d,Y,u,O,x,b,m,v,r,F,w,A,j~
      
      map(df, ~str_c(unique(.x),collapse = ",")) %>% 
        enframe() %>% 
        unnest()
      #> # A tibble: 2 x 2
      #>   name  value                                                              
      #>   <chr> <chr>                                                              
      #> 1 v1    51663,57870,2986,29925,95246,68293,62555,45404,65161,46435,9642,59~
      #> 2 v2    S,V,k,t,z,K,f,J,n,R,W,h,M,P,q,g,C,U,a,d,Y,u,O,x,b,m,v,r,F,w,A,j,c,~
      
      microbenchmark::microbenchmark(
      bind_gather = map(df, ~str_c(unique(.x),collapse = ",")) %>% 
                     bind_rows() %>% 
                     gather(key = col_name, value = col_unique) ,
      frame_unnest = map(df, ~str_c(unique(.x),collapse = ",")) %>% 
                      enframe() %>% 
                      unnest() ,
      times = 10)
      #> Unit: milliseconds
      #>          expr      min       lq     mean   median       uq      max neval
      #>   bind_gather 581.6403 594.6479 615.0841 612.9336 618.3057 697.6204    10
      #>  frame_unnest 568.6620 590.0003 604.2774 606.5676 624.8159 630.2372    10
      

      似乎enframe %&gt;% unnest 比使用bind_rows %&gt;% gather() 稍快。

      【讨论】:

      • 酷。谢谢你这样做。我将不得不更多地研究它的用途。
      • @alexb523 我使用unnest 已经有一段时间了,但是我刚刚在这个akrun 的answer 中听说过enframe,到目前为止我很喜欢它。
      【解决方案4】:

      这对你有用吗?

      data.table::rbindlist(list(map(df, class), map(df, function(x) list(unique(x)))))

      【讨论】:

      • 这非常接近,但我需要它是一个列名列表的表,然后是一个用于类和唯一值的列。最终结果是一个数据字典。
      猜你喜欢
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-30
      • 2014-04-07
      • 1970-01-01
      相关资源
      最近更新 更多