【问题标题】:data.table replicate rows after join?加入后data.table复制行?
【发布时间】:2018-06-28 16:05:25
【问题描述】:

我不确定我是否正确面对这个问题,所以首先我将尝试展示我正在尝试解决的问题,然后展示我尝试解决的方式。随意告诉我我的错误,以及您能想到的任何更好的方法。

我有三个 data.tables(实际的“输入”一个更大,性能很重要,所以我必须尽可能多地使用):

输入:

+--------+----+----+----+------------+
|   ID   | T1 | T2 | T3 |    DATE    | 
+--------+----+----+----+------------+
| ACC001 |  1 |  0 |  0 | 31/12/2016 |
| ACC001 |  1 |  0 |  1 | 30/06/2017 |
| ACC002 |  0 |  1 |  1 | 31/12/2016 |
| ACC002 |  0 |  1 |  1 | 30/06/2017 |
+--------+----+----+----+------------+

mevs:

+------------+------------+-------------+
|    DATE    | INDEX_NAME | INDEX_VALUE |
+------------+------------+-------------+
| 31/12/2016 | GDP        |  1.05       |
| 30/06/2017 | GDP        |  1.06       |
| 31/12/2017 | GDP        |  1.07       |
| 30/06/2018 | GDP        |  1.08       |
| 31/12/2016 | CPI        |  0.02       |
| 30/06/2017 | CPI        |  0.00       |
| 31/12/2017 | CPI        | -0.01       |
| 30/06/2018 | CPI        |  0.01       |
+------------+------------+-------------+   

时间:

+------------+
|    DATE    |
+------------+
| 31/12/2016 |
| 30/06/2017 |
| 31/12/2017 |
| 30/06/2018 |
+------------+

有了这些,我需要实现两件事:

  • 将第二个 dt(mevs) 中的 GDP 和 CPI 值插入第一个(输入),以根据 T1、T2、T3、GDP 和 CPI 在最后一列中进行一些计算。

  • 对第三个 dt(时间)中给定的时间间隔进行投影,将前一个间隔中的 T1、T2 和 T3 值复制到同一 ID 中(因此 ACC001 将保留 (1, 0, 1))并得到对应日期的GDP和CPI。最终计算将使用相同的函数完成。

这应该会导致这样的“输入”dt:

+--------+----+----+----+------------+------+-------+------+
| ID     | T1 | T2 | T3 | DATE       | GDP  | CPI   | CALC |
+--------+----+----+----+------------+------+-------+------+
| ACC001 | 1  | 0  | 0  | 31/12/2016 | 1.05 | 0.02  | fun  |
| ACC001 | 1  | 0  | 1  | 30/06/2017 | 1.06 | 0.00  | fun  |
| ACC001 | 1  | 0  | 1  | 31/12/2017 | 1.07 | -0.01 | fun  |
| ACC001 | 1  | 0  | 1  | 30/06/2018 | 1.08 | 0.01  | fun  |
| ACC002 | 0  | 1  | 1  | 31/12/2016 | 1.05 | 0.02  | fun  |
| ACC002 | 0  | 1  | 1  | 30/06/2017 | 1.06 | 0.00  | fun  |
| ACC002 | 0  | 1  | 1  | 31/12/2017 | 1.07 | -0.01 | fun  |
| ACC002 | 0  | 1  | 1  | 30/06/2018 | 1.08 | 0.01  | fun  |
+--------+----+----+----+------------+------+-------+------+

我已经做到了:

  • mevs <- mevs %>% tidyr::spread(INDEX_NAME, INDEX_VALUE) 将索引值放入列中。
  • input[mevs, ':=' (GDP = i.GDP, CPI = i.CPI), on = "RUN_DATE"] 设置索引值(避免分配,如果我没记错的话)。

导致:

+--------+----+----+----+------------+------+------+------+
| ID     | C1 | C2 | C3 | DATE       | GDP  | CPI  | CALC |
+--------+----+----+----+------------+------+------+------+
| ACC001 | 1  | 0  | 0  | 31/12/2016 | 1.05 | 0.02 | fun  |
| ACC001 | 1  | 0  | 1  | 30/06/2017 | 1.06 | 0    | fun  |
| ACC002 | 0  | 1  | 1  | 31/12/2016 | 1.05 | 0.02 | fun  |
| ACC002 | 0  | 1  | 1  | 30/06/2017 | 1.06 | 0    | fun  |
+--------+----+----+----+------------+------+------+------+

我不知道该怎么做:

我正在尝试使用基于“日期”的“输入”-“时间”和以下代码进行右外连接(在“我在做什么”的第二步中的“选择性连接”之前) :input <- input[time, on = "DATE"]。但它不仅不能正常工作(我在 ID 列中得到了 NA,这是我下一步需要的),它还迫使我进行分配。

在那之后,我计划根据“ID”使用“输入”-“输入”进行另一个连接,但显然我不能,因为我在这些新行中没有任何 ID 值:

+--------+----+----+----+------------+
| ID     | T1 | T2 | T3 | DATE       |
+--------+----+----+----+------------+
| ACC001 | 1  | 0  | 0  | 31/12/2016 |
| ACC001 | 1  | 0  | 1  | 30/06/2017 |
| NA     | NA | NA | NA | 31/12/2017 |
| NA     | NA | NA | NA | 30/06/2018 |
| ACC002 | 0  | 1  | 1  | 31/12/2016 |
| ACC002 | 0  | 1  | 1  | 30/06/2017 |
| NA     | NA | NA | NA | 31/12/2017 |
| NA     | NA | NA | NA | 30/06/2018 |
+--------+----+----+----+------------+

有什么方法可以根据 DATE 列的某些条件复制这些 ID?如果没有,您是否知道任何其他解决方案,可能基于rbindlist

非常感谢您能走到这一步。任何建议将不胜感激!

额外问题

避免分配

@Jaap 的解决方案因此返回了所需的 data.table。除非不可避免,否则我需要将输入转换为最后一个 data.table,而不使用标准分配 (<-)。在这种情况下如何做到这一点?

条件

我需要在脚本的最后部分介绍一个特殊性。如果在预测之前有一个没有一些注册表的 ID,则预测中的 T1/T2/T3 必须为 0。此处的 ACC002 就是这种情况,它在 2016 年 12 月 31 日之后没有注册:

input <- fread("  ID   | T1 | T2 | T3 |    DATE    
                ACC001 |  1 |  0 |  0 | 31/12/2016 
                ACC001 |  1 |  0 |  1 | 30/06/2017 
                ACC002 |  0 |  1 |  1 | 31/12/2016", sep = "|")

最终应该变成:

+--------+----+----+----+------------+------+-------+------+
| ID     | T1 | T2 | T3 | DATE       | GDP  | CPI   | CALC |
+--------+----+----+----+------------+------+-------+------+
| ACC001 | 1  | 0  | 0  | 31/12/2016 | 1.05 | 0.02  | fun  |
| ACC001 | 1  | 0  | 1  | 30/06/2017 | 1.06 | 0.00  | fun  |
| ACC001 | 1  | 0  | 1  | 31/12/2017 | 1.07 | -0.01 | fun  |
| ACC001 | 1  | 0  | 1  | 30/06/2018 | 1.08 | 0.01  | fun  |
| ACC002 | 0  | 1  | 1  | 31/12/2016 | 1.05 | 0.02  | fun  |
| ACC002 | 0  | 0  | 0  | 30/06/2017 | 1.06 | 0.00  | fun  |
| ACC002 | 0  | 0  | 0  | 31/12/2017 | 1.07 | -0.01 | fun  |
| ACC002 | 0  | 0  | 0  | 30/06/2018 | 1.08 | 0.01  | fun  |
+--------+----+----+----+------------+------+-------+------+

最终的结果是 CALC 列依赖于 T1/T2/T3 相关多项式,在这种情况下等于 0(如果您发现直接从那里接近更好)。

【问题讨论】:

    标签: data.table r join data.table


    【解决方案1】:

    使用:

    input[, .SD[time, on = "DATE"], by = ID
          ][dcast(mevs, DATE ~ INDEX_NAME), on = "DATE", `:=` (GDP = i.GDP, CPI = i.CPI)
            ][, (2:4) := lapply(.SD, zoo::na.locf), by = ID, .SDcols = 2:4][]
    

    给予:

           ID T1 T2 T3       DATE  GDP   CPI
    1: ACC001  1  0  0 31/12/2016 1.05  0.02
    2: ACC001  1  0  1 30/06/2017 1.06  0.00
    3: ACC001  1  0  1 31/12/2017 1.07 -0.01
    4: ACC001  1  0  1 30/06/2018 1.08  0.01
    5: ACC002  0  1  1 31/12/2016 1.05  0.02
    6: ACC002  0  1  1 30/06/2017 1.06  0.00
    7: ACC002  0  1  1 31/12/2017 1.07 -0.01
    8: ACC002  0  1  1 30/06/2018 1.08  0.01
    

    这是做什么的:

    • input[, .SD[time, on = "DATE"], by = ID] 将每个 ID time data.table 连接到其余列,从而扩展 data.table。
    • 然后将宽版本的 mevs (dcast(mevs, DATE ~ INDEX_NAME)) 加入到扩展的 data.table 中。
    • 最后用 包中的na.locf 函数填充扩展data.table 中的缺失值。

    要满足更新问题的额外条件,您可以这样做:

    ones <- input[, .N, by = ID][N == 1, ID]
    
    input[, .SD[time, on = "DATE"], by = ID
          ][dcast(mevs, DATE ~ INDEX_NAME), on = "DATE", `:=` (GDP = i.GDP, CPI = i.CPI)
            ][, (2:4) := lapply(.SD, function(x) if (.BY %in% ones) replace(x, is.na(x), 0L) else zoo::na.locf(x) )
              , by = ID, .SDcols = 2:4][]
    

    给出:

           ID T1 T2 T3       DATE  GDP   CPI
    1: ACC001  1  0  0 31/12/2016 1.05  0.02
    2: ACC001  1  0  1 30/06/2017 1.06  0.00
    3: ACC001  1  0  1 31/12/2017 1.07 -0.01
    4: ACC001  1  0  1 30/06/2018 1.08  0.01
    5: ACC002  0  1  1 31/12/2016 1.05  0.02
    6: ACC002  0  0  0 30/06/2017 1.06  0.00
    7: ACC002  0  0  0 31/12/2017 1.07 -0.01
    8: ACC002  0  0  0 30/06/2018 1.08  0.01
    

    使用过的数据:

    input <- fread("  ID   | T1 | T2 | T3 |    DATE    
                    ACC001 |  1 |  0 |  0 | 31/12/2016 
                    ACC001 |  1 |  0 |  1 | 30/06/2017 
                    ACC002 |  0 |  1 |  1 | 31/12/2016 
                    ACC002 |  0 |  1 |  1 | 30/06/2017 ", sep = "|")
    
    mevs <- fread("  DATE    | INDEX_NAME | INDEX_VALUE 
                  31/12/2016 | GDP        |  1.05       
                  30/06/2017 | GDP        |  1.06       
                  31/12/2017 | GDP        |  1.07       
                  30/06/2018 | GDP        |  1.08       
                  31/12/2016 | CPI        |  0.02       
                  30/06/2017 | CPI        |  0.00       
                  31/12/2017 | CPI        | -0.01       
                  30/06/2018 | CPI        |  0.01   ", sep = "|")
    
    time <- fread("    DATE   
                   31/12/2016 
                   30/06/2017 
                   31/12/2017 
                   30/06/2018 ", sep = "|")
    

    【讨论】:

    • 非常感谢,@Jaap。那段代码非常整洁!我也不知道 zoo 包,它似乎很有用。我还有两个问题希望你能解决。我已经和他们一起编辑了我的原始帖子。再次,非常感谢。
    • @sneaky_lobster 分配是不可避免的,因为您正在扩展行数。稍后将查看其他问题。
    • @sneaky_lobster 查看更新,其中包含针对附加要求 HTH 的可能解决方案
    • 这正是我想要的!非常感谢!我不确定如何在那里实现 IF。
    • @sneaky_lobster 我显然忘了在我的回答中包含这个。查看更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 2023-01-12
    • 2016-04-22
    相关资源
    最近更新 更多