2017-08-08 74 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