【问题标题】:Join tables, shifting or renaming existing columns based on value in another column连接表,根据另一列中的值移动或重命名现有列
【发布时间】:2019-09-24 13:07:18
【问题描述】:

我已经导入了一个表格(Table1),格式如下:

ID    Name    Age   Start    Status1    Status2    Status3    Status4    Status5   Status6
01    John     45    2011         25         13         13         12         34        35
02    Mike     23    2017         12         15         18         45         32        25
03   Peter     34    2019         34         12         15         19         24        27
04   Sarah     21    2020         45         46         47         48         49        50

我已经生成了另一个表格(Table2),如下所示:

ID    Name    Age   Start    Year    Value
02    Mike     23    2017       3       24
03   Peter     34    2019       1       11
03   Peter     34    2019       2       13
03   Peter     34    2019       3       13
04   Sarah     21    2020       1       42
04   Sarah     21    2020       2       43
04   Sarah     21    2020       3       44

表 2 包含开始后前 3 年的预测值(包括开始),但仅在尚未过去三年的情况下(如当年,2019 年)。所以在上面的例子中,ID 01 已经超过了它们的起始值 3 年以上,所以没有预测; ID 02 比他们的 Start 值晚了两年,所以有一个预测; ID 03 和 04 的起始值为零年,因此有三个预测。

我需要将 Table2 中的 Values 转换为 Table1,替换 Status 值并将它们向右移动。决赛桌应如下所示:

ID    Name    Age   Start    Status1    Status2    Status3    Status4    Status5   Status6
01    John     45    2011         25         13         13         12         34        35
02    Mike     23    2017         24         12         15         18         45        32
03   Peter     34    2019         11         13         13         34         12        15
04   Sarah     21    2020         42         43         44         45         46        47

如您所见,ID 01 的行保持不变。对于 ID 02,旧的 Status 值向右移动了一位,并且 Status1 已被替换。对于 ID 03 和 04,旧的 Status 值向右移动了三位,并且 Status1 到 Status3 已被替换。如您所见,Status4 到 Status6 中的旧值已被删除。

我非常坚持这一点。到目前为止,我已尝试围绕替换制定一些规则,如下所示:

1) 如果 Year 距离当前年份 (2019) 超过 3 年,则保留 Status 列名称不变。例如表 1 的第 1 行。

2) 如果 Start 为 2019 或更高版本,则通过添加 3 重命名 Status 列。例如,在 Table1 的第 3 和 4 行中,应将 Status1 重命名为 Status4,依此类推。最后 3 个状态列(状态 4 到状态 6)将被删除。

3) 如果 Start 在 2019 年的 3 年内(即 2017 到 2019),则通过添加 2016 和 Start 之间的差异来重命名 Status 列。例如在 Table1 的第 2 行中,Status1 将被重命名为 Status2,依此类推。最后一列 (Status6) 将被删除。

我可以使用 if / else 对这些进行编码,但我不确定如何使用 select() 和 left_join() 将它们放入数据争吵管道中以成功执行这些更改。我正在使用 R,但如果它更容易,也可以使用 SQL。任何帮助将不胜感激。

附言我很欣赏生成的表格是一种奇怪的格式,但不幸的是这是我需要生成的。

【问题讨论】:

  • Mike 的新 Status1 中的 11 是从哪里来的?
  • @IceCreamToucan 抱歉,这是一个错字 - 我现在对其进行了编辑以修复它。

标签: r dplyr data.table purrr


【解决方案1】:

这是data.table 解决方案。大部分答案归结为merge_row 函数,该函数采用预测值和状态值的串联向量,删除NAs,然后将向量截断为6 个元素。它返回一个列表,因为这是 data.table 在使用 := 时在 RHS 上所期望的。


library(data.table)
set.seed(1234)

## Building data

table1 <- data.table(ID = 1:10,
                     Name = LETTERS[1:10],
                     Age = rpois(10, 40),
                     Start = sample(2015:2020, 10, rep = T),
                     Status1 = rpois(10, 10),
                     Status2 = rpois(10, 10),
                     Status3 = rpois(10, 10),
                     Status4 = rpois(10, 10),
                     Status5 = rpois(10, 10),
                     Status6 = rpois(10, 10))
res = list()
for(i in 1:nrow(table1)) {
  if(table1[i,Start] <= 2016) {
    res[[i]] <- NULL
  } else {
    y <- table1[i, Start]
    n_y <- min(3, 3 - (2019-y))
    res[[i]] <- table1[i,
           cbind(.SD[,.(ID, Name, Age, Start)], 
                 Year = seq_len(n_y),
                 Value = rpois(n_y, 20))]
  } 
}
table2 <- do.call(rbind, res)
merge_row <- function(x) {
  x = x[!is.na(x)][1:6]
  return(lapply(1:6, FUN = function(i) x[i]))
}

## Doing the merge

merged_table <- merge(dcast(table2, ID + Name + Age + Start ~ Year, value.var = 'Value'), 
                      table1, 
                      all = TRUE)[, 
                                  paste0('Status', 1:6) := 
                                    merge_row(c(`1`, `2`, `3`, Status1, 
                                                 Status2, Status3, Status4, 
                                                 Status5, Status6)),
                                  ID][,c('1', '2', '3') := NULL][]
merged_table
#>     ID Name Age Start Status1 Status2 Status3 Status4 Status5 Status6
#>  1:  1    A  32  2019      23      22      18       8      15       8
#>  2:  2    B  41  2018      29      17       8       5      11      10
#>  3:  3    C  42  2017      20      11      14      15      13      10
#>  4:  4    D  35  2018      21      19       7       6      12      16
#>  5:  5    E  43  2019      19      17      19       8      14      18
#>  6:  6    F  36  2016      10       8       9      11      12       6
#>  7:  7    G  46  2019      18      19      20      10      13       7
#>  8:  8    H  36  2016      12      14       8      10      11      11
#>  9:  9    I  36  2020      23      16      19      13       6      11
#> 10: 10    J  28  2017      19      20       8       9       7       9

由reprex package (v0.3.0) 于 2019 年 9 月 24 日创建

【讨论】:

  • 这甚至适用于我正在使用的非常大的数据集,所以我选择了作为接受的答案。
【解决方案2】:

另一种选择是将 Table2 与 Table1 的长格式进行 rbind,取前 6 个值,然后再次转换为宽格式:

idcols <- c("ID","Name","Age","Start")
dcast(
    rbindlist(list(Table2,
        melt(Table1, id.vars=idcols, variable.name="Year", value.name="Value")))[,
            .(Status=paste0("Status", 1:6), Value=head(Value, 6)), idcols],
    as.formula(paste(paste(idcols, collapse=" + "), "~ Status")), value.var="Value")

输出:

   ID  Name Age Start Status1 Status2 Status3 Status4 Status5 Status6
1:  1  John  45  2011      25      13      13      12      34      35
2:  2  Mike  23  2017      24      12      15      18      45      32
3:  3 Peter  34  2019      11      13      13      34      12      15
4:  4 Sarah  21  2020      42      43      44      45      46      47

数据:

library(data.table)
Table1 <- fread("ID    Name    Age   Start    Status1    Status2    Status3    Status4    Status5   Status6
01    John     45    2011         25         13         13         12         34        35
02    Mike     23    2017         12         15         18         45         32        25
03   Peter     34    2019         34         12         15         19         24        27
04   Sarah     21    2020         45         46         47         48         49        50")
Table2 <- fread("ID    Name    Age   Start    Year    Value
02    Mike     23    2017       3       24
03   Peter     34    2019       1       11
03   Peter     34    2019       2       13
03   Peter     34    2019       3       13
04   Sarah     21    2020       1       42
04   Sarah     21    2020       2       43
04   Sarah     21    2020       3       44")

【讨论】:

  • 谢谢。它在测试数据上完美运行。当我尝试在我的完整数据集上运行它时,我收到一个错误“不允许负长度向量” - 我不确定为什么,可能是因为表太大了?
  • 是的,很有可能
猜你喜欢
  • 2020-10-03
  • 2021-03-08
  • 1970-01-01
  • 2017-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 2022-11-14
相关资源
最近更新 更多