2017-08-08 82 views
1

我有兩個dataframes(df1df2)中,用guest_idmoneydate結果多重比較,而無需使用「爲」

我想以識別是否在所述第一數據幀的元件也在通過使用這些3.對於我希望使用,我將在添加一個df1變量和所述第二數據幀我會說出match,這需要1如果在df1的元素也是df2,否則爲0

然而,它們並不完美匹配(因爲t中存在一些錯誤他報告),所以我不能簡單地因此,由這3列或類似的

東西將它們合併,我想作一些簡單的比較得知:

  • 有與guest_id比較沒有錯誤,以便我想檢查時guest_iddf1匹配guest_iddf2
  • 警告:guest_id可以在數據幀
  • 當客人在df1df2,我想要再檢查0123多次出現和date。我想這樣做是這樣的:
    • 對於money使money之間的差異df1對於給定guest_id與所有的money可以在df2出現(但同樣只在一定的guest_id)。如果有一個occurence接近於0(-1到1之間,或<= 1,如果我使用abs()),那麼我認爲money比賽
    • 對於date:我做的比在money同樣的事情;我認爲,如果的df1df2$date - 5之間; df2$date + 5,然後date匹配(再次,我想只對給定的guest_id進行這些比較,而不看整個數據幀,否則它可能匹配moneydate,但是對於另一個來賓,這就是我遇到的問題)
  • 因此,如果我有一個匹配guest_idmoneydate,我可以把1我match變量,否則這是一個0

我用dplyr,但我初學者,我很難找到正確的sequ要使用的功能。我也寧願避免使用for因爲我大dataframes工作,所以它肯定會花費太多時間讓我的結果

概括起來講,這是我有:

df1 
    guest_id money  date 
     1  10.2 2017-01-01 
     1  10.3 2000-01-01 
     1  50.0 2017-01-01 
     2  10.5 2017-01-01 
     2  9.5 2017-01-01 
     3 100.0 2000-01-01 

df2 
    guest_id money  date 
     1  10  2017-01-01 
     1  10  2015-01-01 
     2  10  2017-01-03 

而且結果:

df_res 
    guest_id money date   match 
     1  10.2 2017-01-01  1 #difference with money is ok to still have a match 
     1  10.3 2000-01-01  0 #match with money but not date 
     1  50.0 2017-01-01  0 #match with date but not money 
     2  10.5 2017-01-01  1 #match with money ; difference with date is close enough to have a match 
     2  9.5 2017-01-01  1 #Also a match 
     3  100.0 2000-01-01  0 #Doesn't match with guest_id 

有誰有如何做到這一點(甚至是完全不同這個問題的方法)的想法?

+1

看看'fuzzyjoin'包,但我確定使用兩個合併列(其中一個是模糊的,一個不是)是可能的。 –

+0

謝謝!乍一看,它似乎可以幫助很多 – MBB

回答

1

data.table支持的最新版本非等距加入,這是非常方便和有效的,尤其是在上加入更新和組組合由.EACHI

library(data.table) # CRAN version 1.10.4 used 
# tolerances 
tol_m <- 1 
tol_d <- 5 

data.table(df1)[ 
    # join with modified df2 
    data.table(df2)[ 
    # create helper columns for non-equi joins 
    , `:=`(m1 = money - tol_m, m2 = money + tol_m, 
      d1 = date - tol_d, d2 = date + tol_d)] 
    # non-equi join 
    , on = c("guest_id", "money>=m1", "money<=m2", "date>=d1", "date<=d2"), 
    # aggregate group-wise, grouped by join conditions, prettify result 
    match := .N, by = .EACHI][, match := as.integer(!is.na(match))][] 
guest_id money  date match 
1:  1 10.2 2017-01-01  1 
2:  1 10.3 2000-01-01  0 
3:  1 50.0 2017-01-01  0 
4:  2 10.5 2017-01-01  1 
5:  2 9.5 2017-01-01  1 
6:  3 100.0 2000-01-01  0 

沒有非球菌加入,我們將不得不創建所有可能組合的笛卡爾積,並消除那些不符合條件的行。

+0

事實上,我嘗試過,這顯然比做安德烈的方法快很多(儘管兩者都很好)。非常感謝 ! – MBB

+1

如果'df1'或'df2'是大對象,那麼如果使用'setDT()'而不是'data.table()',則可以保證安全的內存和時間。後者需要一個副本,保留原始data.frames不變,而'setDT()'強制_in place_,即不復制,但速度更快但改變了'df1'和'df2'。 – Uwe

1

我建議做着左連接,然後再運用你的條件,如果發現任何從DF1原行過一配合:

library('stringr') 
library('dplyr') 

df3 <- left_join(df1, df2, by = 'guest_id') %>% 
    mutate_at(vars(contains('date')), ymd) %>% 
    # Checking for your condition 
    mutate(match = (abs(money.x - money.y) < 1) & (abs(date.x - date.y) < 5)) %>% 
    # Cleaning data.frame a bit 
    select(-money.y, -date.y) %>% 
    setNames(str_replace(names(.), '\\.x', '')) %>% 
    # Finding if rows had a match 
    group_by(guest_id, money, date) %>% 
    summarise(match = any(match, na.rm = TRUE)) 

df3 
# A tibble: 6 x 4 
# Groups: guest_id, money [?] 
    guest_id money  date match 
    <int> <dbl>  <date> <lgl> 
1  1 10.2 2017-01-01 TRUE 
2  1 10.3 2000-01-01 FALSE 
3  1 50.0 2017-01-01 FALSE 
4  2 9.5 2017-01-01 TRUE 
5  2 10.5 2017-01-01 TRUE 
6  3 100.0 2000-01-01 FALSE 

data.frame的我用測試:

df1 <- structure(list(guest_id = c(1L, 1L, 1L, 2L, 2L, 3L), money = c(10.2, 
10.3, 50, 10.5, 9.5, 100), date = c("2017-01-01", "2000-01-01", 
"2017-01-01", "2017-01-01", "2017-01-01", "2000-01-01")), .Names = c("guest_id", 
"money", "date"), class = "data.frame", row.names = c(NA, -6L 
)) 

df2 <- structure(list(guest_id = c(1L, 1L, 2L), money = c(10L, 10L, 
10L), date = c("2017-01-01", "2015-01-01", "2017-01-03")), .Names = c("guest_id", 
"money", "date"), class = "data.frame", row.names = c(NA, -3L 
)) 
+0

謝謝!此外,它的確很清楚地解釋了 – MBB