为每一列计算唯一值

31

我想返回数据框中每列唯一(不同)值的计数。例如,如果我有以下表格:

 Testdata <- data.frame(var_1 = c("a","a","a"), var_2 = c("b","b","b"), var_3 = c("c","d","e"))

 var_1 | var_2 | var_3
 a     | b     | c 
 a     | b     | d
 a     | b     | e
我希望您的输出为:

我希望输出结果为:

 Variable | Unique_Values
 var_1    | 1
 var_2    | 1
 var_3    | 3

我尝试使用unique函数来操作循环,例如:

 for(i in names(Testdata)){
    # Code using unique function
 }

不过我怀疑有一种更简单的方法。

10个回答

37

您可以使用apply

apply(Testdata, 2, function(x) length(unique(x)))
# var_1 var_2 var_3 
#     1     1     3

7
我会建议使用lapply而不是apply来更好地处理大规模数据。例如,lapply(Testdata, function(x) length(unique(x)))。这是一些更大的测试数据:Testdata <- data.frame(replicate(15, sample(letters[1:sample(26, 1)], 1e6, replace = TRUE))) - A5C1D2H2I1M1N2O1R2T1

31

dplyr 中:

Testdata %>% summarise_all(n_distinct)

(对于那些想了解完整语法的人。

dplyr >0.8.0中使用purrr语法:

)
Testdata %>% summarise_all(list(~n_distinct(.)))

dplyr <0.8.0 中:

Testdata %>% summarise_all(funs(n_distinct(.)))

有关汇总多列的更多信息,请参见此处:https://dplyr.tidyverse.org/reference/summarise_all.html


8
使用lengths函数:
lengths(lapply(Testdata, unique))

# var_1 var_2 var_3 
#     1     1     3 

6

这实际上是对 @Ananda Mahto 的评论的改进。它没有适合在评论中,所以我决定将其添加为答案。

sapply 实际上比 lapply 稍微快一些,并且以与 apply 相同的紧凑形式输出结果。

在实际数据上进行测试运行的结果:

> start <- Sys.time()
> apply(datafile, 2, function(x)length(unique(x)))
          symbol.           date     volume 
             1371            261      53647 
> Sys.time() - start
Time difference of 1.619567 secs
> 
> start <- Sys.time()
> lapply(datafile, function(x)length(unique(x)))
$symbol.
[1] 1371

$date
[1] 261

$volume
[1] 53647

> Sys.time() - start
Time difference of 0.07129478 secs
> 
> start <- Sys.time()
> sapply(datafile, function(x)length(unique(x)))
          symbol.              date             volume 
             1371               261              53647 
> Sys.time() - start
Time difference of 0.06939292 secs

datafile有大约350万行数据。

引用帮助文本:

sapply是lapply的用户友好版本和包装器,默认情况下返回一个向量、矩阵或(如果 simplify="array")适当时返回数组,通过应用simplify2array()。sapply(x, f, simplify=FALSE, USE.NAMES=FALSE)与lapply(x, f)相同。


4
在这里,我使用了 dplyrtidyr 来统计(使用您的 Testdata 数据框):
Testdata %>% 
  gather(var, value) %>% 
  distinct() %>% 
  count(var)

# # A tibble: 3 × 2
#     var     n
#   <chr> <int>
# 1 var_1     1
# 2 var_2     1
# 3 var_3     3

1
这是一种替代方案:

aggregate(values ~ ind, unique(stack(Testdata)), length)
#     ind values
# 1 var_1      1
# 2 var_2      1
# 3 var_3      3

这需要列是字符类型。

1

我刚试过所有的解决方案,其中两个解决方案没有起作用,一个是使用aggregate和tidyr的方法,另一个是使用其他方法。我认为使用数据表是一个很好的选择,

setDT(Testdata)[, lapply(.SD, uniqueN), .SDcols=c("var_1","var_2","var_3")]
   #    var_1 var_2 var_3
   # 1:     1     1     3

我试图将它们相互比较

library(microbenchmark)
Mycomp = microbenchmark(
  apply = apply(Testdata, 2, function(x)length(unique(x))),
  lapply = lapply(Testdata, function(x)length(unique(x))),
  sapply = sapply(Testdata, function(x)length(unique(x))),
  #base = aggregate(values ~ ind, unique(stack(Testdata)), length),
  datatable = setDT(Testdata)[, lapply(.SD, uniqueN), .SDcols=c("var_1","var_2","var_3")],
  times=50
)

#Unit: microseconds
#      expr     min      lq     mean   median      uq     max neval cld
#     apply 163.315 176.678 192.0435 181.7915 192.047 608.859    50  b 
#    lapply 138.217 147.339 157.9684 153.0640 165.829 254.145    50 a  
#    sapply 160.338 169.124 178.1486 174.3965 185.548 203.419    50  b 
# datatable 667.937 684.650 698.1306 696.0160 703.390 874.073    50   c

0

collapse::fNdistinct 接受一个 data.frame

library(collapse)
fNdistinct(Testdata)
# var_1 var_2 var_3 
#     1     1     3

而且它快速。有10000行和10000列的数据:

Testdata = data.frame(replicate(1e4, sample(letters[1:sample(26, 1)], 1e4, replace = TRUE)))
system.time(fNdistinct(Testdata))
# user  system elapsed 
# 0.38    0.00    0.37

0
library(purrr)
Testdata %>% map_dbl(n_distinct)
var_1 var_2 var_3 
    1     1     3 

# in your format
Testdata %>% map_dbl(n_distinct)%>%melt(value.name = "unique_counts")
      unique_counts
var_1             1
var_2             1
var_3             3

虽然这可能回答了作者的问题,但它缺少一些解释性的词语和/或文档链接。裸代码片段没有周围的一些短语是不太有帮助的。您也可以在如何撰写一个好的答案中找到很多帮助。请编辑您的答案。 - Roy Scheffers

0
使用更近期的语法,使用 dplyr
library(tidyverse)

Testdata <- data.frame(var_1 = c("a","a","a"), var_2 = c("b","b","b"), var_3 = c("c","d","e"))

Testdata %>% 
  summarise(across(everything(), n_distinct)) %>%
  pivot_longer(everything())
#> # A tibble: 3 × 2
#>   name  value
#>   <chr> <int>
#> 1 var_1     1
#> 2 var_2     1
#> 3 var_3     3

2023-08-02创建,使用reprex v2.0.2生成


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