我搜索了这个问题的答案,发现了类似的答案(计算每个组中的行数,计算R数据框列中变量值的唯一组合,按组计算元素的出现次数),但没有一个解决了我的特定问题。
我有一个包含变量年
、ID
和code
的数据框。每个人都有一个ID
,并且在(潜在的)多个年
的过程中可以有多个code
值。
df = data.frame(ID = c(1,1,1,1, 2,2,2, 3, 4,4,4,4,4,4,4,4, 5,5,5),
year = c(2018, 2018, 2020, 2020,
2020, 2020, 2020,
2011,
2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020,
2018, 2019, 2020),
code = c("A", "B", "C", "D",
"A", "B", "Q",
"G",
"A", "B", "Q", "G", "C", "D", "T", "S",
"S", "Z", "F")
)
df
ID year code
1 1 2018 A
2 1 2018 B
3 1 2020 C
4 1 2020 D
5 2 2020 A
6 2 2020 B
7 2 2020 Q
8 3 2011 G
9 4 2019 A
10 4 2019 B
11 4 2019 Q
12 4 2019 G
13 4 2020 C
14 4 2020 D
15 4 2020 T
16 4 2020 S
17 5 2018 S
18 5 2019 Z
19 5 2020 F
我想要的是另一个数据帧,给出两个不同值的code
在ID
和年
的分组中同时出现的次数(在这个例子中,A和B共出现3次,A和C共出现0次),然后我将用于网络分析。
到目前为止,我有这个语法:
1:制作数据的宽版本
library(tidyverse)
wide = df %>%
group_by(year, ID) %>%
mutate(row = row_number()) %>%
ungroup() %>%
pivot_wider(
id_cols = c(ID, year),
names_from = row,
names_prefix = "code_",
values_from = code
)
2:制作节点列表
nodes = distinct(df, code) %>% rowid_to_column("id")
3:制作边缘列表
#edge list needs to be three vars: source, dest, and weight
# source and dest are simply code names that (potentially) co-occur in the same year for an ID
# weight is the number of times the codes co-occurred in the same year for each ID.
#all combinations of two codes
edges = combn(x = nodes$code, m = 2 ) %>%
t() %>%
as.data.frame()
colnames(edges) = c("source", "dest")
edges$weight = NA_integer_
#oh, no! a for() loop! a coder's last ditch effort to make something work
for(i in 1:nrow(edges)){
source = edges$source[i]
dest = edges$dest[i]
#get the cases with the first code of interest
temp = df %>%
filter( code == source ) %>%
select(ID, year)
#get the other codes that occurred for that ID in that year
temp = left_join(temp,
wide,
by = c("ID", "year"))
#convert to a logical showing if the other codes are the one I want
temp = temp %>% mutate_at(vars(starts_with("code_")),
function(x){ x == dest }
)
#sum the number of times `source` and `dest` co-occurred
temp$dest = temp %>% select(starts_with("code_")) %>% rowSums(., na.rm=TRUE)
edges$weight[i] = sum(temp$dest, na.rm = TRUE)
}
编辑以添加结果:
结果:
edges
source dest weight
1 A B 3
2 A C 0
3 A D 0
4 A Q 2
5 A G 1
6 A T 0
7 A S 0
8 A Z 0
9 A F 0
10 B C 0
11 B D 0
12 B Q 2
13 B G 1
14 B T 0
15 B S 0
16 B Z 0
17 B F 0
18 C D 2
19 C Q 0
20 C G 0
21 C T 1
22 C S 1
23 C Z 0
24 C F 0
25 D Q 0
26 D G 0
27 D T 1
28 D S 1
29 D Z 0
30 D F 0
31 Q G 1
32 Q T 0
33 Q S 0
34 Q Z 0
35 Q F 0
36 G T 0
37 G S 0
38 G Z 0
39 G F 0
40 T S 1
41 T Z 0
42 T F 0
43 S Z 0
44 S F 0
45 Z F 0
这给了我我想要的(一个数据帧显示A和B共出现3次,A和C共出现0次,A和D共出现0次,A和G共出现1次,A和Q共出现2次,等等…)。所以这是有效的,但即使对于这个小例子也需要一两秒钟。我真正的数据集是~3,000,000次观察。我让它运行了一段时间,但停止了它,却发现它完成了~1%。
有没有更好/更快的方法来做到这一点?
这是一个替代方案,它只是做一个连接,所以对于大数据来说可能非常快。
library(data.table)
setDT(df)
df[df, on = c('ID','year'), allow.cartesian = TRUE][
code<i.code, .N, .(pair = paste0(code, i.code))]
#> pair N
#> 1: AB 3
#> 2: CD 2
#> 3: AQ 2
#> 4: BQ 2
#> 5: GQ 1
#> 6: AG 1
#> 7: BG 1
#> 8: CT 1
#> 9: DT 1
#> 10: ST 1
#> 11: CS 1
#> 12: DS 1
这应该可以。由于sort
,您每对只能获得一个条目。
library(data.table)
setDT(df)
all_pairs <- function(x) {
if (length(x) > 1) {
sapply(combn(sort(x), 2, simplify = FALSE), paste, collapse = '')
} else {
c()
}
}
df[,.(pairs = all_pairs(code)), .(ID, year)][,.N, .(pairs)]
#> pairs N
#> 1: AB 3
#> 2: CD 2
#> 3: AQ 2
#> 4: BQ 2
#> 5: AG 1
#> 6: BG 1
#> 7: GQ 1
#> 8: CS 1
#> 9: CT 1
#> 10: DS 1
#> 11: DT 1
#> 12: ST 1