如何创建根据 ID/row 在 R 中的组合计算的共现矩阵?

How to create a co-occurrence matrix calculated from combinations by ID/row in R?

更新

感谢@jazzurro 的回答。这让我意识到重复项可能只会使事情复杂化。我希望只保留唯一性 values/row 可以简化任务。*

df <- data.frame(ID = c(1,2,3,4,5), 
                  CTR1 = c("England", "England", "England", "China", "Sweden"),
                  CTR2 = c("England", "China", "China", "England", NA),
                  CTR3 = c("USA", "USA", "USA", "USA", NA),
                  CTR4 = c(NA, NA, NA, NA, NA),
                  CTR5 = c(NA, NA, NA, NA, NA),
                  CTR6 = c(NA, NA, NA, NA, NA))


ID CTR1    CTR2    CTR3 CTR4 CTR5 CTR6
1  England China   USA
2  England China   USA
3  England China   USA
4  China   England USA
5  Sweden

基于以下四个条件创建共现矩阵(现在)仍然是目标:

  1. 没有被ID/row追加观察的单次观察不予考虑,即只有一个国家一次的行算作0次。

  2. 一个combination/co-occurrence应该算作1个。

  3. 在组合中也会算作自组合(美国-美国),即分配值 1。

  4. row/ID的组合没有超过 1 的值。

理想结果

         China   England   USA   Sweden

China    4        4         4      0

England  4        4         4      0        

USA      4        4         4      0

Sweden   0        0         0      0

*我已使用 中的代码删除所有非唯一观察值。


原版Post

假设我有一个数据集,其列数较少(有些 NA/empty)且行数超过 100.000,由以下示例数据框表示

df <- data.frame(ID = c(1,2,3,4,5), 
                  CTR1 = c("England", "England", "England", "China", "England"),
                  CTR2 = c("England", "China", "China", "England", NA),
                  CTR3 = c("England", "China", "China", "England", NA),
                  CTR4 = c("China", "USA", "USA", "China", NA),
                  CTR5 = c("USA", "England", "USA", "USA", NA),
                  CTR6 = c("England", "China", "USA", "England", NA))


df

ID   CTR1    CTR2    CTR3    CTR4   CTR5    CTR6         
1    England England England China  USA     England 
2    England China   China   USA    England China
3    England China   China   USA    USA     USA  
4    China   England England China  USA     England
5    England 

我想计算 ID/row 的同现次数以获得一个同现矩阵,该矩阵仅对 ID/row 的同现次数求和一次,这意味着没有超过 1 的值分配给一个组合(即,为不依赖于行内频率和顺序的共现分配值 1,为没有 co-occurrence/combination by ID/row 分配值 0),

1 England-England-England => 1
2 England-England => 1
3 England-China => 1
4 England- => 0

另一个重要方面是计算连续出现一次但与其他观察结果结合的观察结果,例如美国在第 1 行。他们应该为他们自己的共现获得 1 的值(因为他们在一个组合中,即使不与他们自己一起出现)这样组合 USA-USA 也得到分配的值 1。

1    England England England China  USA  England 
USA-USA => 1
China-China => 1
USA-China => 1
England-England => 1
England-USA => 1
England-China => 1

由于 row/ID 的组合行数不应 >1,结果为:

        China   England   USA 

China    1        1         1        

England  1        1         1        

USA      1        1         1

根据示例数据框,这应该会导致以下结果,其中每个组合的值 4 都分配给每个组合,因为每个组合至少出现在四行中,并且每个字符串都是组合的一部分原始数据框的:

         China   England   USA 

China    4        4         4        

England  4        4         4        

USA      4        4         4

所以统计有五个条件:

  1. 不考虑 ID/row 没有额外观察的单一观察,即不计算只有一个国家一次的行。
  2. 组合应计为 1。
  3. 多次出现的观察结果不会为交互作用带来更高的价值,即同一个国家多次出现并不重要。
  4. 在组合中(即使同一个国家没有连续出现两次)会导致计算为自组合,即分配值 1。
  5. row/ID 的组合没有超过 1 的值。

我尝试通过使用 dplyrdata.tablebase aggregateplyr 调整来自 [1], [2], , , [5] and [6] 的代码来实现这一点,但我不知道不关心一行内的顺序,但我也不想总结一行内的所有组合,到目前为止我还没有得到想要的结果。

我是 R 的新手。非常感谢任何帮助。

数据

我修改了你的数据,让数据更能代表你的实际情况。

#   ID    CTR1    CTR2    CTR3  CTR4    CTR5    CTR6
#1:  1 England England England China     USA England
#2:  2 England   China   China   USA England   China
#3:  3 England   China   China   USA     USA     USA
#4:  4   China England England China     USA England
#5:  5  Sweden    <NA>    <NA>  <NA>            <NA>


df <- structure(list(ID = c(1, 2, 3, 4, 5), CTR1 = c("England", "England", 
"England", "China", "Sweden"), CTR2 = c("England", "China", "China", 
"England", NA), CTR3 = c("England", "China", "China", "England", 
NA), CTR4 = c("China", "USA", "USA", "China", NA), CTR5 = c("USA", 
"England", "USA", "USA", ""), CTR6 = c("England", "China", "USA", 
"England", NA)), class = c("data.table", "data.frame"), row.names = c(NA, 
-5L))

更新

看到楼主之前的问题,我心里清楚了。我想这就是你想要的,Seb.

# Transform the data to long-format data. Remove rows that have zero character (i.e, "") or NA. 

melt(setDT(df), id.vars = "ID", measure = patterns("^CTR"))[nchar(value) > 0 & complete.cases(value)] -> foo

# Get distinct value (country) in each ID group (each row)
unique(foo, by = c("ID", "value")) -> foo2

# 
# Seeing this question, you want to create a matrix with crossprod().

crossprod(table(foo2[, c(1,3)])) -> mymat

# Finally, you need to change diagonal values. If a value is equal to one,
# change it to zero. Otherwise, keep the original value.

diag(mymat) <- ifelse(diag(mymat) <= 1, 0, mymat)

#value
#value     China England Sweden USA
#China       4       4      0   4
#England     4       4      0   4
#Sweden      0       0      0   0
#USA         4       4      0   4

这是一个使用 base::table 的选项:

#get paired combi and remove those from same country
pairsDF <- as.data.frame(do.call(rbind, 
    by(df, df$ID, function(x) t(combn(unlist(x[-1L]), 2L)))))

#tabulate pairs
duppairs <- rbind(pairsDF, data.frame(V1=pairsDF$V2, V2=pairsDF$V1))
tab <- table(duppairs, useNA="no")

#set diagonals to be the count of countries if count is at least 2
cnt <- c(table(unlist(df[-1L])))
cnt[cnt==1L] <- 0L
diag(tab) <- cnt[names(diag(tab))]

输出:

         V2
V1        China England Sweden USA
  China       4       4      0   4
  England     4       4      0   4
  Sweden      0       0      0   0
  USA         4       4      0   4

数据:

df <- data.frame(ID = c(1,2,3,4,5), 
    CTR1 = c("England", "England", "England", "China", "Sweden"),
    CTR2 = c("China", "China", "China", "England", NA),
    CTR3 = c("USA", "USA", "USA", "USA", NA),
    CTR4 = c(NA, NA, NA, NA, NA),
    CTR5 = c(NA, NA, NA, NA, NA),
    CTR6 = c(NA, NA, NA, NA, NA))