【问题标题】:How to do efficient vectorized update on multiple columns using data.tables?如何使用 data.tables 对多列进行有效的矢量化更新?
【发布时间】:2012-10-23 17:00:39
【问题描述】:

我有以下使用 data.frames 的代码,我想知道如何使用 data.tables,使用最高效、最矢量化的代码来编写它?

data.frame 代码:

set.seed(1)
to <- cbind(data.frame(time=seq(1:5),bananas=sample(100,5),apples=sample(100,5)),setNames(data.frame(matrix(sample(100,90,replace=T),nrow=5)),paste0(1:18)))
from <- cbind(data.frame(time=seq(1:5),blah=sample(100,5),foo=sample(100,5)),setNames(data.frame(matrix(sample(100,90,replace=T),nrow=5)),paste0(1:18)))
from
to

rownames(to) <- to$time
to[as.character(from$time),paste0(1:18)] <- from[,paste0(1:18)]
to

运行这个:

>     set.seed(1)
>     to <- cbind(data.frame(time=seq(1:5),bananas=sample(100,5),apples=sample(100,5)),setNames(data.frame(matrix(sample(100,90,replace=T),nrow=5)),paste0(1:18)))
>     from <- cbind(data.frame(time=seq(1:5),blah=sample(100,5),foo=sample(100,5)),setNames(data.frame(matrix(sample(100,90,replace=T),nrow=5)),paste0(1:18)))
>     from
  time blah foo  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1    1   66  22 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2    2   35  13 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3    3   27  47 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4    4   97  90 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5    5   61  58 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79
>     to
  time bananas apples  1   2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1    1      27     90 21  50 94 39 49 67 83 79 48 10 92 26 34 90 44 21 24 80
2    2      37     94 18  72 22  2 60 80 65  3 87 32 30 48 84 87 72 72  6 46
3    3      57     65 69 100 66 39 50 11 79 48 44 52 46 77 35 39 40 13 65 42
4    4      89     62 39  39 13 87 19 73 56 74 25 67 34  9 34 78 33 25 88 82
5    5      20      6 77  78 27 35 83 42 53 70  8 41 66 88 48 97 76 15 78 61
> 
>     rownames(to) <- to$time
>     to[as.character(from$time),paste0(1:18)] <- from[,paste0(1:18)]
>     to
  time bananas apples  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1    1      27     90 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2    2      37     94 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3    3      57     65 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4    4      89     62 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5    5      20      6 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79

基本上,我们从frompaste0(1:18) 列更新to 的列paste0(1:18),匹配times。

data.tables显然有一些优势,比如在控制台打印时不需要head,所以我正在考虑使用它们。

但是我不想手动编写:= 表达式,即尽量避免:

to[from,`1`:=i.`1`,`2`:=i.`2`, ..]

如果可能的话,我也更喜欢使用矢量化语法,而不是某种 for 循环,即尽量避免这样的事情:

for( i in 1:18 ) {
    to[from, sprintf("%d",i) := i.sprintf("%d",i)]
}

我通读了常见问题小插图和数据表介绍小插图,但我承认我可能还没有 100% 理解所有内容。

我看了Loop through columns in a data.table and transform those columns,但不能说我100%看懂了,好像说需要用for循环?

在 8374816 的底部似乎确实有某种提示,可能只使用数据框语法,添加 with=FALSE?但由于 data.frame 过程正在对行名称进行黑客攻击,我不确定它的效果如何/是否有效,我想知道它在多大程度上利用了 data.table 的效率?

【问题讨论】:

  • 您遇到的至少一半的困难是您使用数字作为列名。如果您改为在每列前面加上一个字母,使它们成为“有效”的列名,您会发现整个操作要容易得多。
  • @Justin,如果这样可以使解决方案成为可能,我可以在列前加上字母前缀。

标签: r dataframe data.table


【解决方案1】:

好问题。您展示的基本结构:

to[as.character(from$time),paste0(1:18)] <- from[,paste0(1:18)]

假设行名不能重复,或者如果它们是那么只有第一个匹配。这里,&lt;- 的 LHS 与&lt;- 的 RHS 具有相同的行数。

data.table 不同,因为通常to 中的多行可能匹配; mult 的默认值为 "all"data.table 也喜欢长格式而不是宽格式。所以这个问题有点像data.table 为它设计的东西而不是它的步伐。如果您在这 18 列中有任何 NA(即稀疏),那么长格式可能更合适。如果所有 18 列都是同一类型,那么matrix 可能更合适。

也就是说,这里有三个 data.table 选项以确保完整性。

1.使用 := 但没有 for 循环(LHS 中有多个 LHS 和多个 RHS:=RHS)

from = as.data.table(from)
to = as.data.table(to)
from
   time blah foo  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1   66  22 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2:    2   35  13 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3:    3   27  47 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4:    4   97  90 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5:    5   61  58 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79
to
   time bananas apples  1   2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1      27     90 21  50 94 39 49 67 83 79 48 10 92 26 34 90 44 21 24 80
2:    2      37     94 18  72 22  2 60 80 65  3 87 32 30 48 84 87 72 72  6 46
3:    3      57     65 69 100 66 39 50 11 79 48 44 52 46 77 35 39 40 13 65 42
4:    4      89     62 39  39 13 87 19 73 56 74 25 67 34  9 34 78 33 25 88 82
5:    5      20      6 77  78 27 35 83 42 53 70  8 41 66 88 48 97 76 15 78 61
setkey(to,time)
setkey(from,time)
to[from,paste0(1:18):=from[.GRP,paste0(1:18),with=FALSE]]
   time bananas apples  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1      27     90 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2:    2      37     94 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3:    3      57     65 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4:    4      89     62 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5:    5      20      6 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79

to[from,paste0(1:18):=from[,paste0(1:18),with=FALSE],mult="first"]
   time bananas apples  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1      27     90 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2:    2      37     94 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3:    3      57     65 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4:    4      89     62 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5:    5      20      6 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79

注意我使用的是最新的 v1.8.3,这是选项 1 工作所必需的(.GRP 刚刚添加,外部的 with=FALSE 不再需要)。

2。使用一个列表列来存储长度为 18 的向量,而不是 18 列

to = data.table( time=seq(1:5),
                 bananas=sample(100,5),
                 apples=sample(100,5),  
                 v18=replicate(5,sample(100,18),simplify=FALSE))
from =  data.table( time=seq(1:5),
                    blah=sample(100,5),
                    foo=sample(100,5),
                    v18=replicate(5,sample(100,18),simplify=FALSE))
setkey(to,time)
setkey(from,time)

from
   time blah foo                 v18
1:    1   56  97   88,47,1,71,69,18,
2:    2   69  40   96,99,60,3,33,27,
3:    3   65  84 100,38,56,72,84,55,
4:    4   98  74 91,69,24,63,27,100,
5:    5   46  52    65,4,59,41,8,51,

to
   time bananas apples                 v18
1:    1      66     73 100,36,74,77,68,46,
2:    2      19     37   84,88,92,8,37,52,
3:    3      94     77   37,94,13,7,93,43,
4:    4      88      2  27,93,71,16,46,66,
5:    5      91     91   85,94,58,49,19,1,

to[from,v18:=i.v18]
to
   time bananas apples                 v18
1:    1      66     73   88,47,1,71,69,18,
2:    2      19     37   96,99,60,3,33,27,
3:    3      94     77 100,38,56,72,84,55,
4:    4      88      2 91,69,24,63,27,100,
5:    5      91     91    65,4,59,41,8,51,

如果您不习惯列出列打印,则尾随逗号表示该向量中有更多项目。只打印前 6 个。

3.在data.table 上使用data.frame 语法

to = as.data.table(to)
from = as.data.table(from)
setkey(to,time)
setkey(from,time)

from
   time blah foo  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1   66  22 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2:    2   35  13 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3:    3   27  47 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4:    4   97  90 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5:    5   61  58 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79

to
   time bananas apples  1   2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1      27     90 21  50 94 39 49 67 83 79 48 10 92 26 34 90 44 21 24 80
2:    2      37     94 18  72 22  2 60 80 65  3 87 32 30 48 84 87 72 72  6 46
3:    3      57     65 69 100 66 39 50 11 79 48 44 52 46 77 35 39 40 13 65 42
4:    4      89     62 39  39 13 87 19 73 56 74 25 67 34  9 34 78 33 25 88 82
5:    5      20      6 77  78 27 35 83 42 53 70  8 41 66 88 48 97 76 15 78 61

to[from, paste0(1:18)] <- from[,paste0(1:18),with=FALSE]
to
   time bananas apples  1  2   3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18
1:    1      27     90 98  2 100 46 58 60 69 46 62 19 29 42 64 90 30 19 72 60
2:    2      37     94 74 72  50 52  8 57 61 18 56 53 90  7 85 65 20 76 39 12
3:    3      57     65 36 11  49 21  4 53 24 75 33  8 45 34 86 75 89 73 11 85
4:    4      89     62 44 45  18 23 65 99 26 11 46 28 78 73 40 61 51 95 93 32
5:    5      20      6 15 65  76 60 93 51 73 87 51 22 89 34 39 91 88 55 29 79

所以&lt;-的LHS可以使用data.table键控连接语法;即to[from]。只是这个方法(目前在 R 中)将复制 整个 to 数据集。这就是:= 的引入,通过引用提供更新来避免这种情况。此外,如果from 中的每一行与to 中的多行匹配,则&lt;- 的 RHS 需要扩展以对齐(由您的用户),否则 RHS 将被回收以填充 LHS。这就是为什么在data.table 中,我们喜欢:=j 内部,都在[...] 内部。

【讨论】:

    猜你喜欢
    • 2014-08-06
    • 2017-05-29
    • 2020-01-15
    • 2022-08-16
    • 2016-06-19
    • 2010-09-29
    • 2016-03-02
    • 1970-01-01
    • 2023-03-21
    相关资源
    最近更新 更多