在R或MySQL中创建交叉频率表

在R或MySQL中创建交叉频率表,mysql,r,Mysql,R,我有一个用户id-类别对的表。用户可以分为多个类别。我正在尝试为每个可能的结果获取跨类别的计数。i、 e.A类和C类用户的数量等 我的原始数据的结构如下: library('Matrix') mat <- spMatrix(nrow=length(unique(data$category)), ncol=length(unique(data$user_id)), i = as.numeric(factor(data$category)), j = as.numeri

我有一个用户id-类别对的表。用户可以分为多个类别。我正在尝试为每个可能的结果获取跨类别的计数。i、 e.A类和C类用户的数量等

我的原始数据的结构如下:

library('Matrix')
mat <- spMatrix(nrow=length(unique(data$category)),
    ncol=length(unique(data$user_id)),
    i = as.numeric(factor(data$category)),
    j = as.numeric(factor(data$user_id)),
    x = rep(1, length(as.numeric(data$category)))
)
rownames(mat) <- levels(factor(data$category))
colnames(mat) <- levels(factor(data$user_id))
mat

#mat_row <- mat %*% t(mat)

##  Based on @user20650's comment this is even more efficient than
##    the multiplication above:
mat_row <- tcrossprod(mat)

我希望结果如下所示,显示跨类别计数:

如何在R或MySQL中实现这一点?数据相当大

以下是示例数据:

data <- structure(list(category = structure(c(1L, 2L, 2L, 1L, 3L, 3L, 
2L, 1L, 3L, 2L, 2L, 2L, 3L, 1L, 1L, 3L), .Label = c("A", "B", 
"C"), class = "factor"), user_id = c(464L, 345L, 342L, 312L, 
345L, 234L, 423L, 464L, 756L, 756L, 345L, 345L, 464L, 345L, 234L, 
312L)), .Names = c("category", "user_id"), class = "data.frame", row.names = c(NA, 
-16L))

data在MySQL中,您可以以三列格式轻松完成此操作:

select a.category, b.category, count(*)
from pairs a join
     pairs b
     on a.user_id = b.user_id
group by a.category, b.category;

在SQL中,将表生成为矩阵是一项挑战,除非您知道所有的列名。否则,您需要一个动态枢轴(google:“mysql动态枢轴”)。对于数据库中的大多数用途,最好使用三列格式

在R中,我将首先按用户拆分数据,计算该用户的所有唯一类别对,然后将其分组:

data$category <- as.character(data$category)
(combos <- do.call(rbind, tapply(data$category, data$user_id, function(x) {
  u <- unique(x)
  if (length(u) > 1) t(combn(u, 2))
  else NULL
})))
#      [,1] [,2]
# [1,] "C"  "A" 
# [2,] "A"  "C" 
# [3,] "B"  "C" 
# [4,] "B"  "A" 
# [5,] "C"  "A" 
# [6,] "A"  "C" 
# [7,] "C"  "B" 

根据提供的示例数据,我实际上不认为@josilber提供的R解决方案是正确的,尽管考虑到缺少所需的示例解决方案,我可能是错误的。我认为您可以使用igraph及其数据的二分网络表示来实现这一点,但在更大的数据集/类别上,这可能是低效的。另一种方法是,使用数据的稀疏矩阵表示法在R中进行相对有效的计算,如下所示:

library('Matrix')
mat <- spMatrix(nrow=length(unique(data$category)),
    ncol=length(unique(data$user_id)),
    i = as.numeric(factor(data$category)),
    j = as.numeric(factor(data$user_id)),
    x = rep(1, length(as.numeric(data$category)))
)
rownames(mat) <- levels(factor(data$category))
colnames(mat) <- levels(factor(data$user_id))
mat

#mat_row <- mat %*% t(mat)

##  Based on @user20650's comment this is even more efficient than
##    the multiplication above:
mat_row <- tcrossprod(mat)

您可以使用
dplyr
创建所有唯一对的列表,并使用
crossprod
统计一对类别的公用用户数

> library(dplyr)
> data <- data %>% group_by(user_id, category) %>% summarize(records = sign(n()))
> crossprod(table(data$user_id, data$category))

    A B C
  A 4 1 4
  B 1 4 2
  C 4 2 5
>库(dplyr)
>数据%group\U by(用户id,类别)%>%SUMMARY(记录=符号(n()))
>crossprod(表(数据$user\u id,数据$category))
A、B、C
A 414
B 1 4 2
C 4 2 5

您能否给出示例数据的预期输出?我假设它不是第二个数字,因为它甚至不是对称的。嗨@josiber,你是对的-第二个数字只是结果的一个例子。在我找到这个问题的解决方案之前,我还不知道如何得到任何结果。不过,该函数只是计算用户数。希望能有帮助。既然你已经发布了一个16行的例子,我想你可以手工计算。请这样做并发布预期输出,因为在这样做之前,您没有可复制的示例。您是否希望a-B在输出表中的值为1或3?唯一同时拥有这两个类别的用户是用户345,他拥有类别标签A、B、B、B、C。@SuperJohn:当你说“数据相当大”时,你说的是多少条记录?你打算用它进行协同过滤吗?另一种选择<代码>tcrossprod(表(dat))
绝对正确!但是,如果您有许多用户/类别(比如数百万),那么在稀疏矩阵表示出现问题之前,您可能会遇到内存问题。但是,在更易于管理的数据大小上,这种方法肯定是+1!同样要像josibers解决方案(对角线除外)
tcrossprod(!!table(dat))
你说得非常对,我认为这应该更有效。我修改了我的解决方案以反映这一点。我们解决方案的不同之处在于如何处理具有多个相同类别标签的人。例如,具有类别A和B的唯一用户是用户id 345。它们被标记为A一次和B三次,导致您将A-B标记为3,而我将其标记为1(一个人)。只有OP可以告诉我们哪个是首选。我认为我的解释是正确的,因为问题的语言是:“属于A类和C类的用户数量”——请注意,用户id 345只是一个用户,而不是三个。很抱歉延迟答复——我在样本数据集上尝试了这一点,效果非常好。我不得不使用SQL解决方案,因为我有3亿条记录。我没有意识到最终的数据集会那么大!我还不知道如何在R上使用这么大的数据。无论如何,谢谢,谢谢,谢谢。
> library(dplyr)
> data <- data %>% group_by(user_id, category) %>% summarize(records = sign(n()))
> crossprod(table(data$user_id, data$category))

    A B C
  A 4 1 4
  B 1 4 2
  C 4 2 5