【问题标题】:Create a new variable in data frame with condition over another dataframe在数据框中创建一个新变量,条件超过另一个数据框
【发布时间】:2018-04-04 22:49:58
【问题描述】:

我有 2 个这样的数据框

df1

       date item 
 02/01/2017    A 
 09/01/2017    B
 14/01/2017    C

df2

      date1       date2  item    prm
 01/01/2017  03/01/2017     A    YES
 08/01/2017  10/01/2017     B    YES
 15/01/2017  17/01/2017     C    YES

目的

prm 变量是一个常量变量,它只有 1 个值。 我想在这个条件下在我的 df1 中添加变量 prm

df1$date is between df2$date1 and df2$date2 and df1$item=df2$item

但是,如果条件不匹配,那么我需要 prm 获取值“NO”

【问题讨论】:

  • 这是一个简单的 data.table 中的非等连接
  • 我尝试使用 data.table 但我的 df 从 1 800 000 行传递到 800 000 行不知道为什么,因为当我执行唯一操作时,我仍然有 1 800 000 行
  • 试试library(data.table) ; setDT(df1)[setDT(df2), on = .(item, date >= date1, date <= date2), prm := i.prm](假设日期格式正确)
  • 很好,谢谢它像所有解决方案一样工作正常

标签: r dataframe data.table sqldf


【解决方案1】:

使用data.table 提供的non-equi joins 和update on join 这将变成:

library(data.table)
setDT(df1)[setDT(df2), on = .(item, date>=date1, date<= date2), prm := i.prm][
  is.na(prm), prm := "NO"]
df1
         date item prm
1: 2017-01-02    A YES
2: 2017-01-09    B YES
3: 2017-01-14    C  NO

【讨论】:

    【解决方案2】:

    [编辑]

    如果df1 和df2 中的行数不同,您可以使用sqldf 并在df1.date between df2.date1 and df2.date2 和df1.item = df2.item 上创建LEFT JOIN 并使用CASE WHEN 语句来创建专栏prm:

    options("stringsAsFactors" = FALSE)
    
    df1 <- read.table(text = 
    "date item 
    02/01/2017    A 
    09/01/2017    B
    16/01/2017    C 
    02/01/2017    C",
    header = TRUE)
    df2 <- read.table(text =
    "date1       date2  item
    01/01/2017  03/01/2017     A 
    08/01/2017  10/01/2017     B
    15/01/2017  17/01/2017     C",
    header = TRUE)
    
    library(sqldf)
    
    
    sqldf("
      SELECT df1.*, CASE WHEN df1.item = df2.item THEN 'yes' ELSE 'no' END AS prm
      FROM df1 
      LEFT JOIN df2 
       ON df1.date BETWEEN df2.date1 AND df2.date2
       AND df1.item = df2.item
      ")
    
            date item prm
    1 02/01/2017    A yes
    2 09/01/2017    B yes
    3 16/01/2017    C yes
    4 02/01/2017    C  no
    

    【讨论】:

    • 它的工作原理与 data.table 相同,但我不知道为什么,在 sqldf 之前我的数据框有 1 800 000 行,之后它有 800 000 行
    • 是因为只有 800,000 行符合日期和项目条件吗?我已将加入更改为LEFT JOIN,这是您要找的吗?
    【解决方案3】:

    你可以在这里使用ifelse

     df1 <- read.table(text = "      date item 
     02/01/2017    A 
     09/01/2017    B
     16/01/2017    C", header = T)
    
    df2 <- read.table(text = "      date1       date2  item
     01/01/2017  03/01/2017     A 
                      08/01/2017  10/01/2017     B
                      15/01/2017  17/01/2017     C", header = T)
    
    df1$date <- as.Date(df1$date, format = "%d/%m/%Y")
    df2$date1 <- as.Date(df2$date1, format = "%d/%m/%Y")
    df2$date2 <- as.Date(df2$date2, format = "%d/%m/%Y")
    
    
    df1$prm <- ifelse(df1$date >= df2$date1 & df1$date <= df2$date2 & df1$item == df2$item, "YES" , "NO")
    
            date item prm
    1 0002-01-20    A YES
    2 0009-01-20    B YES
    3 0016-01-20    C YES
    

    【讨论】:

    • 是的,它有效,但我认为我的解释不够好,我会编辑我的问题
    • Orhan 答案有什么遗漏吗?
    • 0002-01-20 在您看来是一个有效的日期吗?这应该是df1$date &lt;- as.Date(df1$date, format = "%d/%m/%Y") 等。
    【解决方案4】:

    这是使用 dplyr 的解决方案:

    library(tidyverse)
    
    df1 = tribble(~date, ~item,
                 "02/01/2017",    "A",
                 "09/01/2017",    "B",
                 "16/01/2017",    "C")
    
    df2 = tribble(~date1, ~date2, ~item,
    "01/01/2017",  "03/01/2017",     "A",
    "08/01/2017",  "10/01/2017",     "B",
    "15/01/2017",  "15/01/2017",     "C")
    
    df3 = merge(x = df1, y = df2)
    
    
    df4 = as.data.frame(cbind(df3[1], lapply(df3[2:4], as.Date, format = "%d/%m/%Y")))
    
    
    df5 <- df4 %>%
      mutate(prm = if_else((date > date1) & (date < date2), "YES", "NO"))
    
    df5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-20
      • 1970-01-01
      • 2018-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多