统计R语言中不同列中出现相似项的次数

19

我有以下数据:

df <- data.frame(
  group = c('r1','r2','r3','r4'),
  X1 = c('A','B','C','K'),
  X2 = c('A','C','M','K'),
  X3 = c('D','A','C','K')
)

> df
  group X1 X2 X3
1    r1  A  A  D
2    r2  B  C  A
3    r3  C  M  C
4    r4  K  K  K

我想基于列X1X2X3来估算一个 '相似度得分'。比如,在group r1(或第1行)中,3个元素中有2个是相似的,因此得分为2/3(约67%)。而在group r4(或第4行),得分将为3/3(100%)。期望的结果如下所示:

> df
  group X1 X2 X3 similarity_score
1    r1  A  A  D .67
2    r2  B  C  A .33
3    r3  C  M  C .67
4    r4  K  K  K 1

我该如何实现这个目标?

6个回答

18

另一个可能的解决方案:

library(dplyr)

df %>% 
  rowwise %>% 
  mutate(score = max(prop.table(table(c_across(X1:X3))))) %>% 
  ungroup

#> # A tibble: 4 × 5
#>   group X1    X2    X3    score
#>   <chr> <chr> <chr> <chr> <dbl>
#> 1 r1    A     A     D     0.667
#> 2 r2    B     C     A     0.333
#> 3 r3    C     M     C     0.667
#> 4 r4    K     K     K     1

甚至更短:

library(tidyverse)
df %>% mutate(score = pmap_dbl(across(X1:X3), ~ max(prop.table(table(c(...))))))

1
很好的回答!如果“3”没有硬编码,这将更具普适性。 - jdobres
@jdobres:我可以提供一个更短的替代方案,避免之前硬编码的“3”。 - PaulS

12

你可以去做

df$similarity <- round(apply(df[-1], 1, function(x) max(table(x))/length(x)), 2)

df
#>   group X1 X2 X3 similarity
#> 1    r1  A  A  D       0.67
#> 2    r2  B  C  A       0.33
#> 3    r3  C  M  C       0.67
#> 4    r4  K  K  K       1.00

本文档由 reprex包 (v2.0.1) 于2022年04月18日创建


10

一个tidyverse的解决方案:

library(tidyverse)

df %>% 
  rowwise() %>% 
  mutate(
    similarity_score = max(colMeans(outer(c_across(-group), c_across(-group), `==`)))
  ) 

或者,你可以使用嵌套解决方案替代c_across:

df %>% 
  group_by(group) %>% 
  nest(data = -group) %>% 
  rowwise() %>% 
  mutate(
    similarity_score = max(colMeans(outer(unlist(data), unlist(data), `==`)))
  ) %>% 
  unnest(data)

  group X1    X2    X3    similarity_score
  <chr> <chr> <chr> <chr>            <dbl>
1 r1    A     A     D                0.667
2 r2    B     C     A                0.333
3 r3    C     M     C                0.667
4 r4    K     K     K                1   

5
作为另一种选择,我们可以在单次遍历中存储所有发生的情况(而不是按行操作):
tab = table(rep(df[, 1], ncol(df) - 1), as.matrix(df[, -1]))

然后,检索每一行中数量最多的元素所占比例:

tab = tab / rowSums(tab)
tab[cbind(1:nrow(df), max.col(tab))]
#[1] 0.6666667 0.3333333 0.6666667 1.0000000

4

另一个可能的选择是先对数据进行摘要,然后再将其与数据框联接。

library(tidyverse)

df %>%
  left_join(pivot_longer(., -group) %>%
              group_by(group) %>%
              summarise(score = round(max(table(value))/length(value), 2)))

输出

  group X1 X2 X3 score
1    r1  A  A  D  0.67
2    r2  B  C  A  0.33
3    r3  C  M  C  0.67
4    r4  K  K  K  1.00

0

以下是基于R语言的另一种方法:

df$score <- round(sapply(apply(df[,c(2:4)], 1, table), first) / 3, 2) 

#   group X1 X2 X3 similarity_score
# 1    r1  A  A  D             0.67
# 2    r2  B  C  A             0.33
# 3    r3  C  M  C             0.67
# 4    r4  K  K  K             1.00

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