如何创建一个函数来计算单个单元中特定字符的共现次数?
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 很快就会响起,让我们知道他的想法。以上是关于如何创建一个函数来计算单个单元中特定字符的共现次数?的主要内容,如果未能解决你的问题,请参考以下文章