2012-04-13 25 views
4

我需要實現以下功能(理想情況下在R或SQL中):給定兩個數據框(有一個用於userid的列,其餘的colums是布爾值屬性(它們只允許爲0或1))我需要返回具有兩列(userid和count)的新數據框,其中count是兩個表中每個用戶的0和1的匹配數。用戶F可能發生在兩個數據幀中,或者可能發生在一個數據幀中。在最後一種情況下,我需要爲該用戶數返回NA。我寫一個例子:如何使用SQL或R庫sqldf來完成此任務?

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) 

非常感謝提前。 最好的問候!

+0

哪些DBMS您使用的? PostgreSQL的?甲骨文? DB2? .. – 2012-04-13 17:08:00

+0

你好,我正在使用Microsoft SQL Server 2005!謝謝 – Nestorghh 2012-04-13 17:14:42

回答

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 
+0

非常感謝@Chase。優雅!魔幻! quégrande !!! – Nestorghh 2012-04-13 17:49:33

+0

還有一個問題@Chase ...你能告訴我這種方法如何改變,如果我現在需要分別計數0和1的計數,即我需要一個新的三列數據框,用戶ID和0和1的計數。非常感謝你提前。 – Nestorghh 2012-04-16 13:47:57

0

您可以使用apply函數來處理這個問題。爲了讓每一行的總和,您可以使用:

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

將返回所有的總和,但第一列,然後綁定,爲第一列來獲取ID了。

你可以在兩個數據幀上做到這一點。我不清楚在此之後期望的行爲,但也許看看merge函數。

+0

謝謝@Jeff艾倫,但這不是我所需要的。我認爲你誤解了我的問題。 – Nestorghh 2012-04-13 17:01:53

+3

'rowSums(DF1 [,-1])'也會更快。 – Chase 2012-04-13 17:04:01

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

有可能是一個更優雅的方式,但這個工程:

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")] 
+0

我們在這裏有非常類似的方法...讓我知道我有點正確的道路! +1 – Chase 2012-04-13 17:22:33

+0

@Chase:同意。儘管我喜歡你的更一般的解決方案。 – 2012-04-13 17:25:05