您将如何使用SQL或R库sqldf执行此任务?

时间:2012-04-13 16:06:57

标签: sql sql-server-2005 r

我需要实现以下函数(理想情况下在R或SQL中):给定两个数据框(有一个用于userid的列,其余的colums是booleans属性(它们只允许为0或1))I需要返回一个包含两列(userid和count)的新数据框,其中count是两个表中每个用户的0和1的匹配数。用户F可以出现在两个数据帧中,也可以只出现在一个数据帧中。在最后一种情况下,我需要为该用户计数返回NA。我写了一个例子:

DF1
ID c1 c2 c3 c4 c5
1   0  1  0  1  1
10  1  0  1  0  0
5   0  1  1  1  0
20  1  1  0  0  1
3   1  1  0  0  1
6   0  0  1  1  1
71  1  0  1  0  0
15  0  1  1  1  0
80  0  0  0  1  0

DF2  
ID c1 c2 c3 c4 c5
5   1  0  1  1  0
6   0  1  0  0  1
15  1  0  0  1  1
80  1  1  1  0  0
78  1  1  1  0  0
98  0  0  1  1  1
1   0  1  0  0  1
2   1  0  0  1  1
9   0  0  0  1  0

我的函数必须返回如下内容:(以下是子集)

DF_Return
ID Count
1    4
2    NA
80   1
20   NA
   .
   .
   .

你可以给我任何建议吗?我不是sql的专家。

我把代码放在R中以生成我上面使用的实验。

 id1=c(1,10,5,20,3,6,71,15,80)
 c1=c(0,1,0,1,1,0,1,0,0)
 c2=c(1,0,1,1,1,0,0,1,0)
 c3=c(0,1,1,0,0,1,1,1,0)
 c4=c(1,0,1,0,0,1,0,1,1)
 c5=c(1,0,0,1,1,1,0,0,0)
 DF1=data.frame(ID=id1,c1=c1,c2=c2,c3=c3,c4=c4,c5=c5)
 DF2=data.frame(ID=c(5,6,15,80,78,98,1,2,9),c1=c2,c2=c1,c3=c5,c4=c4,c5=c3)

非常感谢提前。 最诚挚的问候!

5 个答案:

答案 0 :(得分:3)

这是给你的方法。第一个硬编码要比较的列,而另一个是更通用的,不知道DF1和DF2有多少列:

#Merge together using ALL = TRUE for equivlent of outer join
DF3 <- merge(DF1, DF2, by = "ID", all = TRUE, suffixes= c(".1", ".2"))
#Calculate the rowSums where the same columns match
out1 <- data.frame(ID = DF3[, 1], count = rowSums(DF3[, 2:6] ==  DF3[, 7:ncol(DF3)]))

#Approach that is agnostic to the number of columns you have
library(reshape2)
library(plyr)
DF3.m <- melt(DF3, id.vars = 1)
DF3.m[, c("level", "DF")] <- with(DF3.m, colsplit(variable, "\\.", c("level", "DF")))
out2 <- dcast(data = DF3.m, ID + level ~ DF, value.var="value")
colnames(out)[3:4] <- c("DF1", "DF2")
out2 <- ddply(out, "ID", summarize, count = sum(DF1 == DF2))

#Are they the same?
all.equal(out1, out2)
#[1] TRUE

> head(out1)
  ID count
1  1     4
2  2    NA
3  3    NA
4  5     3
5  6     2
6  9    NA

答案 1 :(得分:2)

SELECT
  COALESCE(DF1.ID, DF2.ID)  AS ID,
  CASE WHEN DF1.c1 = DF2.c1 THEN 1 ELSE 0 END +
  CASE WHEN DF1.c2 = DF2.c2 THEN 1 ELSE 0 END +
  CASE WHEN DF1.c3 = DF2.c3 THEN 1 ELSE 0 END +
  CASE WHEN DF1.c4 = DF2.c4 THEN 1 ELSE 0 END +
  CASE WHEN DF1.c5 = DF2.c5 THEN 1 ELSE 0 END AS count_of_matches
FROM
  DF1
FULL OUTER JOIN
  DF2
    ON DF1.ID = DF2.ID

答案 2 :(得分:2)

可能有更优雅的方式,但这有效:

x <- merge(DF1,DF2,by="ID",all=TRUE)
pre <- paste("c",1:5,sep="")
x$Count <- rowSums(x[,paste(pre,"x",sep=".")]==x[,paste(pre,"y",sep=".")])
DF_Return <- x[,c("ID","Count")]

答案 3 :(得分:0)

您可以使用apply功能来处理此问题。要获得每行的总和,您可以使用:

sums <- apply(df1[2:ncol(df1)], 1, sum)
cbind(df1[1], sums)

将返回除第一列之外的所有列的总和,然后将其绑定到第一列以获取ID。

您可以在两个数据框上执行此操作。我不太清楚之后的期望行为是什么,但也许看看merge函数。

答案 4 :(得分:0)

我们可以使用我的软件包safejoin中的safe_full_join,然后应用== 冲突列之间。这将产生一个新的带有逻辑的数据帧 我们可以在c*上使用rowSums列。

# devtools::install_github("moodymudskipper/safejoin")
library(safejoin)
library(dplyr)

safe_full_join(DF1, DF2, by = "ID", conflict = `==`) %>%
  transmute(ID, count = rowSums(.[-1]))
#    ID count
# 1   1     4
# 2  10    NA
# 3   5     3
# 4  20    NA
# 5   3    NA
# 6   6     2
# 7  71    NA
# 8  15     1
# 9  80     1
# 10 78    NA
# 11 98    NA
# 12  2    NA
# 13  9    NA