【问题标题】:How to best join one column of a data.table with another column of the same data.table?如何最好地将 data.table 的一列与同一 data.table 的另一列连接起来?
【发布时间】:2012-06-14 09:51:04
【问题描述】:

我的数据

我有一个 data.table DT,其中当前 (F0YR) 和下一个 (F1YR) 财政年度结束 (FYE) 编码为整数。因为每个下一个 FYE 最终都会变成 当前 FYE,整数将同时在列 F1YRF0YR 中。此外,我的数据包含每月观察结果,因此相同的 FYE 将出现在数据集中 多次:

library(data.table)
DT <- data.table(ID     = rep(c("A", "B"), each=9),
                 MONTH  = rep(100L:108L, times=2),
                 F0YR   = rep(c(1L, 4L, 7L), each=3, times=2),
                 F1YR   = rep(c(4L, 7L, 9L), each=3, times=2),
                 value  = c(rep(1:5, each=3), 6, 6, 7),
                 key    = "ID,F0YR")
DT
      ID MONTH F0YR F1YR value
 [1,]  A   100    1    4     1
 [2,]  A   101    1    4     1
 [3,]  A   102    1    4     1
 [4,]  A   103    4    7     2
 [5,]  A   104    4    7     2
 [6,]  A   105    4    7     2
 [7,]  A   106    7    9     3
 [8,]  A   107    7    9     3
 [9,]  A   108    7    9     3
[10,]  B   100    1    4     4
[11,]  B   101    1    4     4
...

我想做什么

对于每个IDF1YR 组合,我想获得IDF0YR 组合的值。例如:A 公司对于 FOYR==4 的值为 2。现在, 我想要一个额外的列,用于所有 ID=="A"F1YR==4 的组合,它设置为 2,在已经存在的值 1 旁边。

我尝试了什么

intDT <- DT[CJ(unique(ID), unique(F0YR)), list(ID, F0YR, valueNew = value), mult="last"]
setkey(intDT, ID, F0YR)
setkey(DT, ID, F1YR)
DT <- intDT[DT]
setnames(DT, c("F0YR.1", "F0YR"), c("F0YR", "F1YR"))
DT
      ID F1YR valueNew MONTH F0YR value
 [1,]  A    4        2   100    1     1
 [2,]  A    4        2   101    1     1
 [3,]  A    4        2   102    1     1
 [4,]  A    7        3   103    4     2
 [5,]  A    7        3   104    4     2
 [6,]  A    7        3   105    4     2
 [7,]  A    9       NA   106    7     3
 [8,]  A    9       NA   107    7     3
 [9,]  A    9       NA   108    7     3
[10,]  B    4        5   100    1     4
[11,]  B    4        5   101    1     4
...

(请注意,我在这里使用mult="last",因为虽然值应该只随着 F0YR 或 F1YR 的变化而变化,但有时它们不会,这只是我的 决胜局)。

我想要什么

这看起来可以改进。首先,我必须复制我的 DT。二、由于我加入的data.table基本相同,所以所有列名都同名 我必须重命名它们。我认为self join 将是前进的方向,但我尝试了又尝试并没有得到一个好的解决方案。我有希望 那里有一些我看不到的简单的东西……有人知道吗?还是我的数据设置方式实际上很难 (可能是因为我有每月观察,但只想加入每季度或每年变化的值)。

【问题讨论】:

  • 我认为这个问题不需要它,但:= by group 现在正在 1.8.1 中工作,也许可以用于此。 R-Forge 构建正常,二进制安装正常(在 R 2.15.0 中,因为 R-Forge 仅构建最新版本)使用 install.packages("data.table", repos="http://R-Forge.R-project.org")。检查test.data.table() 是否返回“653 个测试已完成”以确保它是最新版本。查看最新新闻,看看是否有任何新功能对此有用。
  • @MatthewDowle -- 非常好!我刚试过。看起来它适用于覆盖现有列,但不适用于创建新列。对吗?
  • @Josh 很棒。不,它应该很好地添加新列。您甚至可以对新列进行子分配,它会为您使用NA 填充列的其余部分。如果两者都不起作用,请提交错误报告或新问题。确保 test.data.table() 返回 653 tests ok 以排除使用旧版本的 v1.8.1。
  • @MatthewDowle 这确实非常好。我不知道以这种方式安装软件包有多么简单......无论如何,我也看不出:= by group 可以在这里提供帮助。试了一个小时,什么都想不通。所以我想我的解决方案是可以的。无论如何,通过该 data.table 的两列连接一个 data.table 是一个非常具体的用例。
  • 没问题,已添加答案。按组直接进入:=,按i 而非by 分组,并使用连接继承范围(V1 来自i 范围)进行引导。在:= 的第一个演示中,并没有计划走那么远,但结果就是这样!

标签: r data.table


【解决方案1】:

在这样的用例中,“先聚合,然后加入”的口头禅通常会有所帮助。因此,从您的 DT 开始,并使用 v1.8.1:

> agg = DT[,last(value),by=list(ID,F0YR)]
> agg
   ID F0YR V1
1:  A    1  1
2:  A    4  2
3:  A    7  3
4:  B    1  4
5:  B    4  5
6:  B    7  7

我称它为agg,因为我想不出更好的名字。在这种情况下,您需要 last,它并不是真正的聚合,但您知道我的意思。

然后按组通过引用更新DT。这里我们按i分组。

setkey(DT,ID,F1YR)
DT[agg,newcol:=V1]
    ID MONTH F0YR F1YR value newcol
 1:  A   100    1    4     1      2
 2:  A   101    1    4     1      2
 3:  A   102    1    4     1      2
 4:  A   103    4    7     2      3
 5:  A   104    4    7     2      3
 6:  A   105    4    7     2      3
 7:  A   106    7    9     3     NA
 8:  A   107    7    9     3     NA
 9:  A   108    7    9     3     NA
10:  B   100    1    4     4      5
11:  B   101    1    4     4      5
12:  B   102    1    4     4      5
13:  B   103    4    7     5      7
14:  B   104    4    7     5      7
15:  B   105    4    7     5      7
16:  B   106    7    9     6     NA
17:  B   107    7    9     6     NA
18:  B   108    7    9     7     NA

对吗?不确定我是否完全遵循。这些操作应该非常快,没有任何副本,并且应该扩展到大数据。至少,这是本意。

【讨论】:

  • 很好的答案,马修,非常感谢。这正是我想要的。但是有一个问题:除了newcol:=V1 部分之外,您的答案看起来非常简单,我想我尝试了所有可能的组合,其中首先包括聚合,然后加入咒语。因此,我只是用DT[agg] 而不是DT[agg,newcol:=V1] 来回答你的问题。现在,我的 DT 看起来很不一样:它只有 14 行和一些 MONTH 的 NA。这是为什么?我只是认为newcol:=V1 将 V1 重命名为 newcol。我在这里想念什么?
  • @Christoph_J 太好了。不,:= 从不重命名列。 setnames() 重命名列。 := 通过引用现有或新列进行分配。您可能缺少的部分是i 中的非连接列(即未参与连接的列,在本例中为V1)可以在j 中使用,这要归功于连接继承范围。尝试删除 newcol:= 位来查看。或研究# join inherited scope?data.table 中的示例。 DT[agg] 返回 NAs 以表示没有匹配项。另一方面,:= 通过引用更新DT;当i 行在DT 中不匹配时,没有什么要更新的。
  • 谢谢,我一定会调查的!你的包裹里有太多很酷的东西跟不上它;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
相关资源
最近更新 更多