如何创建一个函数来计算单个单元中特定字符的共现次数?

Posted

技术标签:

【中文标题】如何创建一个函数来计算单个单元中特定字符的共现次数?【英文标题】:How can I create a function that counts the number of co-occurrences of specific characters in a single unit? 【发布时间】:2019-10-26 09:21:59 【问题描述】:

我正在尝试创建一个函数,使 R 能够读取每个单数单元 (ID) 并计算该单元中特定字符的共现次数。 数据集如下:

       ID    class  weight
1       1       A       1.0
2       1       A       1.0
3       1       B       1.0
4       2       A       1.0
5       2       B       1.0
6       2       C       1.0
7       3       B       1.0
8       4       B       1.0
9       4       C       1.0
10      4       C       1.0
11      4       D       1.0
12      4       D       1.0
13      5       A       0.9
14      5       B       0.9
15      5       C       0.9
16      5       D       0.9
17      6       B       0.8
18      6       B       0.8
19      7       C       0.7
20      7       C       0.7
21      7       D       0.7
22      7       D       0.7
23      8       C       0.6
24      8       D       0.6
25      9       D       0.5
26      9       E       0.5
27      9       E       0.5
28     10       C       0.4
29     10       C       0.4
30     10       C       0.4
31     10       E       0.4
32     11       A       0.3
33     11       A       0.3
34     11       A       0.3
35     12       A       0.2
36     12       B       0.2
37     12       C       0.2
38     13       B       0.1
39     13       D       0.1
40     13       D       0.1
41     13       E       0.1
42     14       D       1.0
43     14       E       1.0
44     15       B       1.0
45     15       B       1.0
46     15       C       1.0
47     15       C       1.0
48     15       D       1.0
49     16       C       1.0
50     16       D       1.0
51     16       E       1.0
52     16       E       1.0
53     17       B       1.0
54     17       C       1.0
55     17       C       1.0
56     18       D       1.0
57     18       D       1.0
58     18       E       1.0
59     19       E       1.0
60     19       E       1.0
61     20       B       1.0
62     20       D       1.0
63     20       E       1.0
64     20       E       1.0

我尝试创建一个循环函数,但我不知道如何正确指定表达式。 R 应该识别从 1 到 20 的 ID,并在每个 ID 中计算字符同时出现的次数。不仅如此,每个共现都必须通过 ID 的特定权重来加权。 关于生成循环函数的任何想法?

一些细节: 在 ID 1 中,A 和 B 类同时出现两次(第一次 A 与 B,第二次 A 与 B),乘以权重 (1) 得出初步值 2。 在循环完成整个列表后,A 和 B 的共现值应为 4.1,并且该值应以如下所示的 5x5 矩阵报告:

   A   B    C    D    E
A  1   4.1  ..
B 4.1  1    ..
C ..   ..   1
D ..             1
E ..                  1

相同类之间的共现将仅为 1。

输入(数据) 结构(列表(ID = c(1L,1L,1L,2L,2L,2L,3L,4L,4L,4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 7L, 7L, 7L, 7L, 8L, 8L, 9L, 9L, 9L, 10L, 10L, 10L, 10L, 11L, 11L, 11L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 14L, 14L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 18L, 18L, 18L, 19L, 19L, 20L, 20L, 20L, 20L), class= c(“A”,“A”,“B”,“A”,“B”,“C”,“B”,“B”,“C”,“C”, “D”、“D”、“A”、“B”、“C”、“D”、“B”、“B”、“C”、“C”、“D”、“D”、 “C”、“D”、“D”、“E”、“E”、“C”、“C”、“C”、“E”、“A”、“A”、“A”、 “A”、“B”、“C”、“B”、“D”、“D”、“E”、“D”、“E”、“B”、“B”、“C”、 “C”、“D”、“C”、“D”、“E”、“E”、“B”、“C”、“C”、“D”、“D”、“E”、 “E”, “E”, “B”, “D”, “E”, “E”), 重量 = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9, 0.9, 0.9, 0.9, 0.8, 0.8, 0.7, 0.7, 0.7, 0.7, 0.6, 0.6, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)), row.names = c(NA, -64L), class= c("data.table", "data.frame"), .internal.selfref = ) GC() 已用 (Mb) gc 触发器 (Mb) 已用最大 (Mb) Ncell 2672851 142.8 4316924 230.6 4316924 230.6 Vcell 5761794 44.0 12425324 94.8 29629603 226.1 库(数据表) 数据

【问题讨论】:

dput(mat) 与您的第一个数据框不匹配。这是故意的吗?你能dput你帖子顶部的那个吗? 是的,很抱歉。当我发布它时,我的大脑被炸了。现在是正确的版本。 dput 的语法存在一些问题,但选择尾随structure 似乎复制了您的数据 如果说,对于id = n,我们有c("A", "A", "B", "B"),它是如何计算的。这算作 4 还是 2,还是...? 4 将是我正在寻找的。​​span> 【参考方案1】:

我尝试过做空玛丽安的解决方案,但只完成了前两部分。它使用您似乎已经在使用的data.table

dt <- data[, `:=` (Count = .N), by = list(ID, class)] %>%
  dcast(., ID + weight ~ class, value.var = "Count")

eg.dt <- merge(unique(data$class), unique(data$class), all = TRUE) %>%
  setnames(., c("x", "y"), c("Var1", "Var2"))

代码没有大的减少。如果我想出更多,我会更新。

【讨论】:

【参考方案2】:

这是一种方法:

library(tidyverse)

数据

data <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 7L, 7L, 7L, 7L, 8L, 8L, 9L, 9L, 9L, 10L, 10L, 10L, 10L, 11L, 11L, 11L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 14L, 14L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 18L, 18L, 18L, 19L, 19L, 20L, 20L, 20L, 20L), class = c("A", "A", "B", "A", "B", "C", "B", "B", "C", "C", "D", "D", "A", "B", "C", "D", "B", "B", "C", "C", "D", "D", "C", "D", "D", "E", "E", "C", "C", "C", "E", "A", "A", "A", "A", "B", "C", "B", "D", "D", "E", "D", "E", "B", "B", "C", "C", "D", "C", "D", "E", "E", "B", "C", "C", "D", "D", "E", "E", "E", "B", "D", "E", "E"), weight = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9, 0.9, 0.9, 0.9, 0.8, 0.8, 0.7, 0.7, 0.7, 0.7, 0.6, 0.6, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)), row.names = c(NA, -64L), class = c("data.table", "data.frame")) %>% as_tibble()

主要

创建一个“计数”数据框:

(df <- data %>%
  count(ID, class, weight) %>%
  spread(class, n, fill = 0))

获取所有类的组合:

eg <- expand.grid(unique(data$class), unique(data$class), stringsAsFactors = FALSE)

用配对和加权计数制作一个小标题:

final <- map2(
  eg$Var1,
  eg$Var2,
  ~ df %>% select(.x, .y, weight) %>% 
    mutate(counts = !!sym(.x) * !!sym(.y)) %>%
    mutate(wt_counts = counts * weight) %>%
    select(wt_counts) %>%
    sum() %>%
    tibble(Var1 = .x, Var2 = .y, wt_count = .)
) 

转换为矩阵:

finalmatrix <- bind_rows(final) %>%
  mutate(wt_count = ifelse(Var1 == Var2, 1, wt_count)) %>%
  spread(Var2, wt_count) %>%
  select(-Var1) %>%
  as.matrix()

最后,设置名称:

row.names(finalmatrix) <- colnames(finalmatrix)

结果

> finalmatrix
    A    B    C    D   E
A 1.0  4.1  2.1  0.9 0.0
B 4.1  1.0 10.1  6.1 2.1
C 2.1 10.1  1.0 11.3 3.2
D 0.9  6.1 11.3  1.0 8.2
E 0.0  2.1  3.2  8.2 1.0

注意

我个人不喜欢我的解决方案持续了多长时间,而且我找不到使用 rlang 的东西 (!!sym()) 的方法,但它仍然有效。

【讨论】:

【参考方案3】:

编辑:

已修改以匹配@Marian Minar 的回答。还添加了一个tidyverse 解决方案,这是这个小数据集的三种方法中最快的。

Tidyverse

  mat_ans_2 <- DF%>%
    count(ID, class, weight)%>%
    inner_join(., ., by = 'ID')%>%
    filter(class.x != class.y)%>%
    group_by(class.x, class.y)%>%
    summarize(co_occur = sum(weight.x * n.x * n.y))%>%
    spread(key = 'class.x', value = 'co_occur', fill = 0L)%>%
    column_to_rownames('class.y')%>%
    as.matrix()

  diag(mat_ans_2) <- 1L

data.table - 在这个数据集上速度较慢

dt <- as.data.table(DF)[, .N, by = .(ID, class, weight)]

dt2 <- dt[dt, on = 'ID', .(class, i.class, weight, N, i.N), by = .EACHI, allow.cartesian = T
          ][class != i.class, .(co_occur = sum(weight * N * i.N)), by = .(class, i.class)]

dt3 <- dcast(dt2, class ~ i.class, fill = 0, value.var = 'co_occur')

mat_ans <- as.matrix(dt3[,-1])

rownames(mat_ans) = colnames(mat_ans)
diag(mat_ans) <- 1L

这是使用xtabs的一种奖励方式

dt <- setkey(as.data.table(DF)[, .N, by = .(ID, class, weight)], ID)

dt_mat <- xtabs(co_occur ~ i.class + class,
                data = dt[dt, .(class, i.class, co_occur = weight*N*i.N), allow.cartesian = T]
                )

diag(dt_mat) <- 1L

性能:

Unit: milliseconds
        expr     min        lq       mean    median       uq      max neval
     cole_dt  9.7538  10.36345  10.966212  10.84040  11.1854  15.8167   100
   cole_tidy  5.5976   5.79765   6.221044   5.96675   6.1522  10.0465   100
  cole_xtabs  6.2134   6.65480   7.062921   6.94780   7.2503  13.9981   100
 marian_tidy 95.9504 100.08345 103.244376 101.95380 104.7970 125.7495   100

数据:

DF <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 7L, 7L, 7L, 7L, 8L, 8L, 9L, 9L, 9L, 10L, 10L, 10L, 10L, 11L, 11L, 11L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 14L, 14L, 15L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 18L, 18L, 18L, 19L, 19L, 20L, 20L, 20L, 20L)
                     , class = c("A", "A", "B", "A", "B", "C", "B", "B", "C", "C", "D", "D", "A", "B", "C", "D", "B", "B", "C", "C", "D", "D", "C", "D", "D", "E", "E", "C", "C", "C", "E", "A", "A", "A", "A", "B", "C", "B", "D", "D", "E", "D", "E", "B", "B", "C", "C", "D", "C", "D", "E", "E", "B", "C", "C", "D", "D", "E", "E", "E", "B", "D", "E", "E")
                     , weight = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.9, 0.9, 0.9, 0.9, 0.8, 0.8, 0.7, 0.7, 0.7, 0.7, 0.6, 0.6, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1))
                , row.names = c(NA, -64L)
                , class = c("data.table", "data.frame")
)

【讨论】:

Cole,我认为你是对的:ID 15 总共有四 (4) 个 B 和 C 共现,这意味着加权计数应该是 4。如果你的计算为 2,那会在结果中解释我的 10.1 和你的 8.1。我在对 OP 的评论中澄清了这种情况,他解释说 ID 15 中看到的情况意味着 B 和 C 同时出现 4 次(我的长篇大论说我认为我是对的 :-)) . 是的,您采用了正确的方法。我修改了答案并添加了不同的tidyverse 方法。 不错的工作和比较,我希望 OP 很快就会响起,让我们知道他的想法。

以上是关于如何创建一个函数来计算单个单元中特定字符的共现次数?的主要内容,如果未能解决你的问题,请参考以下文章

词共现矩阵

计算由另一列值分组的列值在 pandas 数据框中的共现

如何计算特定字母在字符串中出现的次数? (C++)

来自嵌套单词列表的共现矩阵

Excel如何统计某单元格内特定字符串出现的次数

如何计算一行excel单元格中相同字符的出现次数