在R中,数据框每行唯一元素的数量

5

我有一个如下的数据框:

Group1  Group2  Group3  Group4
A       B       A       B   
A       C       B       A   
B       B       B       B   
A       C       B       D   
A       D       C       A   

我想在数据框中添加一个新列,该列将包含每行中唯一元素的计数。期望的输出是:
Group1  Group2  Group3  Group4  Count
A       B       A       B       2
A       C       B       A       3
B       B       B       B       1
A       C       B       D       4
A       D       C       A       3

我可以使用以下方法找到每行的计数:

length(unique(c(df[,c(1,2,3,4)][1,])))

我希望能对数据框中的所有行执行相同的操作。我尝试了使用var=1的apply()函数,但没有成功。同时,如果你能提供一个更加简洁优雅的解决方案就更好了。


你的“data.frame”有多少个唯一值?有多少行?你可以将数据集转换为table(row(df),as.matrix(df))格式,这样对于这些任务来说可能更方便操作。另外,可能要考虑其稀疏替代方案。 - alexis_laz
3个回答

9
我们可以使用apply函数和MARGIN =1参数遍历每一行。
df1$Count <- apply(df1, 1, function(x) length(unique(x)))
df1$Count
#[1] 2 3 1 4 3

或者使用 tidyverse

library(dplyr)
df1 %>%
    rowwise() %>%
    do(data.frame(., Count = n_distinct(unlist(.))))
# A tibble: 5 × 5
#   Group1 Group2 Group3 Group4 Count
#*  <chr>  <chr>  <chr>  <chr> <int>
#1      A      B      A      B     2
#2      A      C      B      A     3
#3      B      B      B      B     1
#4      A      C      B      D     4
#5      A      D      C      A     3

我们还可以使用正则表达式来更快地完成这个任务。这种方法基于一个假设,即每个单元格只有一个字符。

nchar(gsub("(.)(?=.*?\\1)", "", do.call(paste0, df1), perl = TRUE))
#[1] 2 3 1 4 3

更详细的解释请看这里


谢谢@akrun,您的答案展示了如何正确使用apply()来解决问题。然而,如果可能的话,您能否提供一个更优雅的方法来完成同样的任务,而不是为每一行找到唯一元素的计数? - smaug
@satnam 我更新了一个tidyverse方法,这将更加优雅。 - akrun
@satnam 添加了一种使用正则表达式的高效方法。或许更加优雅。 - akrun
3
正则表达式那个很聪明,但如果字符串超过一个字母,它就行不通了,对吧? - David Arenburg
@DavidArenburg 这是真的,但我在这里假设它是一个单个字母,根据示例来看。 - akrun
2
@akrun,从我的角度来看,那是误导性的。至少你应该在回答中清楚地表明这一点,但你往往不这样做,而是基于对真实数据集的隐含假设来回答问题。另外,你链接到自己的问题中已经有了标准的应用答案。 - talat

3

duplicated在基础R语言中:

df$Count <- apply(df,1,function(x) sum(!duplicated(x)))

#  Group1 Group2 Group3 Group4 Count
#1      A      B      A      B     2
#2      A      C      B      A     3
#3      B      B      B      B     1
#4      A      C      B      D     4
#5      A      D      C      A     3

2
虽然这里提到了一些相当不错的解决方案,但您也可以使用 data.table数据:
df <- data.frame(g1 = c("A","A","B","A","A"),g2 = c("B", "C", "B","C","D"),g3 = c("A","B","B","B","C"),g4 = c("B","A","B","D","A"),stringsAsFactors = F)

代码:

编辑: 在David Arenberg的评论后,将1:nrow(df)替换为(.I)。感谢宝贵的意见。

library(data.table)
setDT(df)[, id := .I ]
df[, count := uniqueN(c(g1, g2, g3, g4)), by=id ]
df

输出:

> df
   g1 g2 g3 g4 id count
1:  A  B  A  B  1     2
2:  A  C  B  A  2     3
3:  B  B  B  B  3     1
4:  A  C  B  D  4     4
5:  A  D  C  A  5     3

我对R中的数据表不是很熟悉,一旦我详细尝试了这个功能,我会用建设性的反馈进行评论,谢谢! - smaug
没问题,看这个。https://dev59.com/geo6XIcBkEYKwwoYKRDG - PKumar
2
这基本上与使用for循环相同,因为你正在运行1:nrow(df)(顺便说一下,data.table有一个.I操作符),因此这个解决方案没有利用data.table的优势。 - David Arenburg
能否用字符串或NSE替换c(g1, g2, g3,g4)?我尝试了eval(names(df))但没有成功。 - A Duv

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