【问题标题】:Merge two datasets based on unique distinct time intervals and then create a dataset with all unmatched values (in R)根据唯一的不同时间间隔合并两个数据集,然后创建一个包含所有不匹配值的数据集(在 R 中)
【发布时间】:2020-06-06 13:10:41
【问题描述】:

我有两个独立的数据集:df1 和 df2。我想创建一个新的数据集 df3,如果日期时间在 20 秒内,它将匹配 df1 的 endtime 列与 df2 的发送列。最后,我想创建一个最终数据集,为我提供 df2 数据集(已发送/ID 数据集)中与 df1 不匹配的所有值。

 df1

 endtime                     ID

 1/7/2020  1:35:08 AM         A
 1/7/2020  1:39:00 AM         B
 1/20/2020 1:45:00 AM         C



 df2

sent                         ID

1/7/2020  1:35:20 AM          E
1/7/2020  1:42:00 AM          F
1/20/2020 1:55:00 AM          G
1/20/2020 2:00:00 AM          E

这是我想要的 df3 输出。只有一行,因为只有两个值符合 endtime 和 sent 列的 20 秒内的条件。

endtime                  sent 

1/7/2020 1:35:08 AM      1/7/2020  1:35:20 AM    

不匹配值的期望输出

sent 

1/7/2020  1:42:00 AM
1/20/2020 1:55:00 AM          
1/20/2020 2:00:00 AM          

这是输出:

df1

structure(list(endtime = structure(c(2L, 3L, 1L), .Label = c("1/10/2020 1:45:00 AM", 
"1/7/2020 1:35:08 AM", "1/7/2020 1:39:00 AM"), class = "factor"), 
ID = structure(1:3, .Label = c("A", "B", "C"), class = "factor")), class = "data.frame", row.names =   c(NA, 
 -3L))





 df2

 structure(list(sent = structure(c(3L, 4L, 1L, 2L), .Label = c("1/20/2020 1:55:00 AM", 
 "1/20/2020 2:00:00 AM", "1/7/2020 1:35:20 AM", "1/7/2020 1:42:00 AM"
 ), class = "factor"), ID = structure(c(1L, 2L, 3L, 1L), .Label = c("E", 
"F", "G"), class = "factor")), class = "data.frame", row.names = c(NA, 
-4L))

这是我尝试过的:

我正在考虑执行左连接并匹配值,或者我可以使用 merge(),但棘手的部分是将值与条件语句匹配。任何建议表示赞赏。

     df3<-crossing(endtime = as.POSIXct(df1$endtime,format ="%m/%d/%Y %I:%M:%S %p" ), 
               sent = as.POSIXct(df2$sent, format = "%m/%d/%Y %I:%M:%S %p")) %>% 
     filter((endtime - seconds(20)) <= sent, 
     (endtime + seconds(20)) >= (sent)) %>%
     mutate_all(format, format = "%m/%d/%Y %I:%M:%S %p") %>%
     distinct(sent, .keep_all = TRUE)

这很好用,但我不知道如何在 df2 数据集中找到基本上“剩余”且没有匹配项的所有值。任何建议表示赞赏。

【问题讨论】:

  • 您可以将ID 列添加到每个原始data.frame 并检查IDs 不在df3 中,即df1$ID[!df1$ID %in% df3$ID, ]...
  • 好主意! @dario
  • 但这只是显示的日期。在显示数据时更改它,但出于内部目的,将其保留为日期...但是您可以随意做,这是您的数据;)
  • 嗨,Tanisha。请再次阅读下面关于答案的 cmets(我在那里解释了原始代码的问题并提出了两种解决方案)。如果您仍有问题,请随时提出。
  • 对不起,我可能有点不清楚。不,更改不是为了保持一致性,实际上它有点不一致,因为 df1 和 df2 仍然 使用 AM/PM。我们不得不做出改变,因为出现了问题。您在问题中显示的代码存在未显示 AM/PM 的错误。只看df3的输出。没有 AM 或 PM ..(我在答案中编辑开始时显示的 tibble 表明)。这是一个问题,因为这样我们无法区分 11h 和 23h!?这就是为什么我在我的编辑中建议 df3 的改编代码。修复错误。

标签: r dplyr data.table lubridate stringr


【解决方案1】:
library(dplyr)
library(tidyr)
library(lubridate)

df1 <- structure(list(endtime = structure(c(2L, 3L, 1L), .Label = c("1/10/2020 1:45:00 AM", "1/7/2020 1:35:08 AM", "1/7/2020 1:39:00 AM"),class = "factor"),
                      ID = structure(1:3, .Label = c("A", "B", "C"), class = "factor")),
                 class = "data.frame", row.names =   c(NA, -3L))

df2 <- structure(list(sent = structure(c(3L, 4L, 1L, 2L), .Label = c("1/20/2020 1:55:00 AM", "1/20/2020 2:00:00 AM", "1/7/2020 1:35:20 AM", "1/7/2020 1:42:00 AM"), class = "factor"),
                      ID = structure(c(1L, 2L, 3L, 1L), .Label = c("E", "F", "G"), class = "factor")),
                 class = "data.frame", row.names = c(NA, -4L))

编辑:

我不得不稍微更改日期的重新转换,因为它确实显示日期时间字符串的“AM”和“PM”部分:

# A tibble: 1 x 2
  endtime                sent                  
  <chr>                  <chr>                 
1 "01/07/2020 01:35:08 " "01/07/2020 01:35:20 "

之前:

mutate_all(format, format = "%m/%d/%Y %I:%M:%S %p")

之后:

mutate_all(format, format = "%m/%d/%Y %H:%M:%S")

改编代码以创建df3

df3<-crossing(endtime = as.POSIXct(df1$endtime,format ="%m/%d/%Y %I:%M:%S %p" ), 
              sent = as.POSIXct(df2$sent, format = "%m/%d/%Y %I:%M:%S %p")) %>% 
  filter((endtime - seconds(20)) <= sent, 
         (endtime + seconds(20)) >= (sent)) %>%
  mutate_all(format, format = "%m/%d/%Y %H:%M:%S") %>%
  distinct(sent, .keep_all = TRUE)

df3 现在:

# A tibble: 1 x 2
  endtime             sent               
  <chr>               <chr>              
1 01/07/2020 01:35:08 01/07/2020 01:35:20

评论:为什么sentendtime 在这里被重新转换为字符串?你不想让他们约会吗?因为我们将不得不在下面再次重铸它们。

建议的解决方案:

识别不匹配的行:

df1_unmatched <- df1 %>% 
  mutate(endtime = as.POSIXct(endtime,format ="%m/%d/%Y %H:%M:%S" )) %>% 
  filter(!endtime %in% as.POSIXct(df3$endtime,format ="%m/%d/%Y %H:%M:%S" ))

df2_unmatched <- df2 %>% 
  mutate(sent = as.POSIXct(sent, format = "%m/%d/%Y %H:%M:%S")) %>% 
  filter(!sent %in% as.POSIXct(df3$sent,format ="%m/%d/%Y %H:%M:%S" ))


df1_unmatched

返回:

              endtime ID
1 2020-01-07 01:39:00  B
2 2020-01-10 01:45:00  C

df2_unmatched

返回:

                 sent ID
1 2020-01-07 01:42:00  F
2 2020-01-20 01:55:00  G
3 2020-01-20 02:00:00  E

【讨论】:

  • 好的,我明天先试试,谢谢。有没有办法显示不匹配的发送时间?我会解决这个问题
  • 代码中还有一个bug:将mutate_all(format, format = "%m/%d/%Y %I:%M:%S %p") %&gt;%这一行替换为mutate_all(format, format = "%m/%d/%Y %H:%M:%S") %&gt;%
  • 哦!我在执行命令时没有任何错误。你能告诉我是什么错误吗?
  • df3 代码中的日期格式无法正常工作,因为它没有打印“AM”或“PM”部分(请参阅上面我编辑开头的代码示例 - 那里没有“上午”或“下午”!)。这意味着“01:00:00 AM”将与“01:00:00 PM”相同。哪个不是我们想要的?!新代码使用 24 小时格式并修复了此问题。但正如已经提到的:我强烈建议对这段代码中的多个日期转换和重新转换进行清理。
  • 我的建议是: 1.(可能是最好的方法)清理日期变量。使用as.POSIXct 将它们一次 转换为日期时间,并使用您编写的代码中的参数。不要将其重新转换为字符串。曾经 ;)。相应地调整剩余的代码。 2. 将mutate_all(format, format = "%m/%d/%Y %I:%M:%S %p") %&gt;% 替换为mutate_all(format, format = "%m/%d/%Y %H:%M:%S") %&gt;% 来修复它(这就是我在回答中使用的)。如果这令人困惑,请考虑阅读 ?as.POSIXct?format。第一个用于将字符串转换为日期,而后者用于相反的操作。
【解决方案2】:

data.table中使用非equi:

df3 <-  df1[, c("st", "et") := .(endtime - 20L, endtime + 20L)][
    df2, on=.(st<=sent, et>=sent), nomatch=0L, .(ID1=x.ID, ID2=i.ID, endtime, sent)]

输出:

   ID1 ID2             endtime                sent
1:   A   E 2020-01-07 01:35:08 2020-01-07 01:35:20

数据:

library(data.table)
setDT(df1)[, endtime := as.POSIXct(as.character(endtime),format ="%m/%d/%Y %I:%M:%S %p")]
setDT(df2)[, sent := as.POSIXct(as.character(sent), format = "%m/%d/%Y %I:%M:%S %p")]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 2020-08-03
    • 2020-06-06
    • 1970-01-01
    • 2016-07-08
    • 2021-12-29
    • 1970-01-01
    相关资源
    最近更新 更多