显示每列的唯一值

5

我正在尝试创建一个数据框,其中包含每列的列类型和唯一变量。

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

以下是一个小数据框和代码,可以在列表中获取唯一变量,但无法在数据框中获取。理想情况下,我可以在一个(map)函数中完成这个任务,但如果必须将它们连接起来,也不是什么大问题。


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。这是预料之中的,但我不确定该如何解决这个问题。

你想要的输出是什么? - M--
此外,不要使用 bind_rows %>% spread(...),而应该使用 map(df, class) %>% enframe() %>% unnest() - M--
@M-M 我没有听说过 enframe()unnest()。我正在努力理解文档,但是相比于 bind_rows() %>% gather(),它们有什么好处呢? - alexb523
4个回答

7

这两列中独特值的数量不同。您需要将它们减少到单个元素。

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   

4
我们可以使用 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   

同样可以使用基本的R语言中的lapply函数实现。

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)))))

1
为了回应OP的评论,询问关于“enframe”和“unnest”的问题,我设置了一个基准测试。

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 %>% unnest 比使用 bind_rows %>% gather() 稍微快一些。


很好,感谢您做这件事。我需要更深入地了解它的用途。 - alexb523
@alexb523 我已经使用 unnest 有一段时间了,但说实话我刚听说 enframe 是在 akrun 的答案中,到目前为止我很喜欢它。 - M--

0
这对你有用吗?

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


这很接近了,但我需要它成为一个带有列名制表符的表格,然后再加上一个类和唯一值的列。最终结果是一个数据字典。 - alexb523

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接