【问题标题】:How to create a new column in dataframe A, based on multiple conditions in dataframe B如何根据数据框 B 中的多个条件在数据框 A 中创建新列
【发布时间】:2020-07-09 01:57:50
【问题描述】:

我猜我看不到树林中的森林......因此,我想寻求帮助。

数据: dput() 输出见问题末尾。

-Dataframe dfA with the columns: ID; ts. ts being POSIXct

-Dataframe dfB with the columns: ID; start; end; state_id. ID (corresponding to the ID in dfA), start (POSIXct), end (POSIXct), state_id.

任务:

我想根据条件在 dfA 中创建一个值为 1/0 的新列。 words 中的条件:如果 dfA 和 dfB 中的 ID 匹配,并且时间戳 dfA$ts 介于​​或等于 dfB$start 和 dfB$end 之间,则值 1 应写入 dfA$x,否则为 0应该在那里。

我认为代码应该看起来像这样:

dfA$x <- iflese( dfA$ID == dfB$ID & dfA$ts >= dfB$start & dfA$ts <= dfB$end, 1, 0)

提前感谢您的帮助。

输入(dfB):

结构(列表(ID = c(1151L, 1151L, 1150L, 1150L, 1150L, 1150L, 1152L,1152L,1152L,1345L),开始=结构(c(1443142500, 1443144600, 1442934900, 1442942400, 1442944800, 1442946300, 1443103500, 1443132600, 1443137400, 1443389400), class= c("POSIXct", "POSIXt" )), 结束 = 结构(c(1443143400, 1443145500, 1442935500, 1442943000, 1442945400, 1442950200, 1443106200, 1443134100, 1443140100, 1443392400 ), class= c("POSIXct", "POSIXt")), state_id = c(1L, 2L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 1L)), row.names = c(NA, -10L), class= "data.frame")

输入(dfA):

结构(列表(ID = c(1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1151L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1150L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1152L, 1345L, 1345L, 1345L, 1345L, 1345L, 1345L, 1345L, 1345L, 1345L, 1345L), ts = 结构(c(1443141300, 1443141600, 1443141900, 1443142200, 1443142500, 1443142800, 1443143100, 1443143400, 1443143700, 1443144000, 1443144300, 1443144600, 1443144900, 1443145200, 1443145500, 1443145800, 1443146100, 1443146400, 1443146700, 1443147000, 1442934900, 1442935200, 1442935500, 1442935800, 1442936100, 1442936400, 1442936700, 1442937000, 1442937300, 1442937600, 1442937900, 1442938200, 1443103500, 1443103800, 1443104100, 1443104400, 1443104700, 1443105000, 1443105300, 1443105600, 1443105900, 1443106200, 1443106500, 1443106800, 1443107100, 1443107400, 1443107700, 1443108000, 1443369300, 1443369600, 1443369900, 1443370200, 1443370500, 1443370800, 1443371100, 1443371400, 1443371700, 1443372000), class= c("POSIXct", "POSIXt" ))), row.names = c(NA, -58L), class= "data.frame")

【问题讨论】:

    标签: r dataframe multiple-conditions


    【解决方案1】:

    这是一个更新的非等连接:

    library(data.table)
    
    setDT(dfA); setDT(dfB)
    
    dfA[dfB, match := 1, on=.(ID=ID, ts>=start, ts<=end)][, match:=ifelse(is.na(match), 0, match)]
    

          ID                  ts match
     1: 1151 2015-09-25 07:35:00     0
     2: 1151 2015-09-25 07:40:00     0
     3: 1151 2015-09-25 07:45:00     0
     4: 1151 2015-09-25 07:50:00     0
     5: 1151 2015-09-25 07:55:00     1
     6: 1151 2015-09-25 08:00:00     1
     7: 1151 2015-09-25 08:05:00     1
     8: 1151 2015-09-25 08:10:00     1
     9: 1151 2015-09-25 08:15:00     0
    10: 1151 2015-09-25 08:20:00     0
    ...
    

    【讨论】:

    • 当我使用大型数据集时,您的解决方案效果最好。也非常快(0.16 秒)......然而,一个后续问题。如果我也想添加 state_id 列,我认为它应该可以通过: dfA[dfB, state_ID := state_id, on=.(ID=ID, ts>=start, ts
    • 也许再仔细检查一下。如果您 100% 确定数字不正确匹配,那么您必须提供一个最小数据集供我(或其他人)进行测试。我知道您的数据集很大,但可能是不工作的 id 的子集?此外,检查 ID + ts (dfA) 和 ID+start (dfB) 上的重复项。
    【解决方案2】:

    可以使用data.table,有条件的替换为:=:

    data.table::setDT(dfA)
    dfA[,value := 0L]
    dfA[(get('ID') == dfB$ID) & (get('ts') >= dfB$start) & (get('ts') <= dfB$end), value := 1L]
    

    请注意,我将 dfA 中的列名放在 get 调用中,以避免与 dfB 列混淆。

    在这种情况下dfA 和dfB 必须具有相同的行数。如果他们不这样做,请使用基于ID 列的merge

    【讨论】:

    • dfA 和 dfB 的行数不同。按 ID 合并并不能解决问题,因为 dfA 和 dfB 中有多行将被合并。这意味着 dfA 和 dfB 中 ID 中的重复值。
    【解决方案3】:

    我们可以 left_join dfA 和 dfB 'ID', group_by 每个 ID 和 ts 并检查 any 值是否在该组的范围内。

    library(dplyr)
    
    dfA %>%
      left_join(dfB, by = 'ID') %>%
      group_by(ID, ts) %>% 
      summarise(x = +any(ts >= start & ts <= end))
    
    
    #      ID ts                      x
    #   <int> <dttm>              <int>
    # 1  1150 2015-09-22 23:15:00     1
    # 2  1150 2015-09-22 23:20:00     1
    # 3  1150 2015-09-22 23:25:00     1
    # 4  1150 2015-09-22 23:30:00     0
    # 5  1150 2015-09-22 23:35:00     0
    # 6  1150 2015-09-22 23:40:00     0
    # 7  1150 2015-09-22 23:45:00     0
    # 8  1150 2015-09-22 23:50:00     0
    # 9  1150 2015-09-22 23:55:00     0
    #10  1150 2015-09-23 00:00:00     0
    # … with 48 more rows
    

    【讨论】:

    • 您的解决方案适用于我在此处提供的一个小型示例数据集。然而,对于整个数据集,我得到以下错误:错误:std::bad_alloc。 .我认为这一定是由于我的data.frame的大小。对于出现此错误的其他人来说,这也是假定的问题:github.com/tidyverse/readxl/issues/150
    • @RasK 我不确定。它适用于此处共享的数据,因此我无法针对其他数据集对其进行测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 2017-03-20
    • 2021-12-09
    相关资源
    最近更新 更多