如何根据 data.table 中的分类变量以编程方式创建二进制列?
How to programmatically create binary columns based on a categorical variable in data.table?
我有一个很大的(1200 万行)data.table
看起来像这样:
library(data.table)
set.seed(123)
dt <- data.table(id=rep(1:3, each=5),y=sample(letters[1:5],15,replace = T))
> dt
id y
1: 1 b
2: 1 d
3: 1 c
4: 1 e
5: 1 e
6: 2 a
7: 2 c
8: 2 e
9: 2 c
10: 2 c
11: 3 e
12: 3 c
13: 3 d
14: 3 c
15: 3 a
我想创建一个新的 data.table
包含我的变量 id
(这将是这个新的 data.table
的唯一键)和其他 5 个二进制变量,每个变量对应于每个变量如果 id 具有 y
的值,y
的类别取值 1
,否则为 0
。
输出 data.table
应如下所示:
id a b c d e
1: 1 0 1 1 1 1
2: 2 1 0 1 0 1
3: 3 1 0 1 1 1
我尝试在循环中执行此操作,但速度很慢,而且我不知道如何以编程方式传递二进制变量名称,因为它们取决于我尝试 "split" 的变量。
编辑:正如@mtoto 指出的那样,已经提出并回答了类似的问题here,但解决方案是使用reshape2
包.
我想知道是否有另一种(更快)的方法可以通过在 data.table 中使用 :=
运算符来做到这一点,因为我有一个庞大的数据集并且我正在使用这个包进行很多工作。
EDIT2:@Arun post 中函数的基准测试(约 1200 万行,约 350 万个不同的 ID 和 490 个不同的标签) y
变量(产生 490 个虚拟变量):
system.time(ans1 <- AnsFunction()) # 194s
system.time(ans2 <- dcastFunction()) # 55s
system.time(ans3 <- TableFunction()) # Takes forever and blocked my PC
如果您已经知道行的范围(因为您知道示例中的行不超过 3 行)并且您知道可以从零数组开始并使用 apply 函数的列更新该辅助 table.
中的值
我的 R 有点生锈,但我认为应该可以。此外,您传递给 apply 方法的函数可以包含根据需要添加必要的行和列的条件。
我的 R 有点生锈,所以我现在有点犹豫要不要写,但我认为这就是写的方法。
如果您正在寻找更即插即用的东西,我发现了这个小 blerb:
There are two sets of methods that are explained below:
gather() and spread() from the tidyr package. This is a newer interface to the reshape2 package.
melt() and dcast() from the reshape2 package.
There are a number of other methods which aren’t covered here, since they are not as easy to use:
The reshape() function, which is confusingly not part of the reshape2 package; it is part of the base install of R.
stack() and unstack()
从这里:: http://www.cookbook-r.com/Manipulating_data/Converting_data_between_wide_and_long_format/
如果我更精通 R,我会告诉您这些不同的方法如何处理从长列表到宽列表的冲突。我在谷歌上搜索 "Make a table from flat data in R" 想出了这个...
另请查看 this 这是与上面相同的网站和我的个人评论包装器:p
data.table 有自己的 dcast
实现,使用 data.table 的内部结构,应该很快。试一试:
dcast(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L)
# id a b c d e
# 1: 1 0 1 1 1 1
# 2: 2 1 0 1 0 1
# 3: 3 1 0 1 1 1
刚刚想到了另一种方法来处理这个问题,通过引用进行预分配和更新(也许 dcast 的逻辑应该这样做以避免中间体)。
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
剩下的就是用 1L
.
填充现有组合
dt[, {set(ans, i=.GRP, j=unique(y), value=1L); NULL}, by=id]
ans
# id b d c e a
# 1: 1 1 1 1 1 0
# 2: 2 0 0 1 1 1
# 3: 3 0 1 1 1 1
好的,我已经继续对 OP 的数据维度进行基准测试,大约 1000 万行和 10 列。
require(data.table)
set.seed(45L)
y = apply(matrix(sample(letters, 10L*20L, TRUE), ncol=20L), 1L, paste, collapse="")
dt = data.table(id=sample(1e5,1e7,TRUE), y=sample(y,1e7,TRUE))
system.time(ans1 <- AnsFunction()) # 2.3s
system.time(ans2 <- dcastFunction()) # 2.2s
system.time(ans3 <- TableFunction()) # 6.2s
setcolorder(ans1, names(ans2))
setcolorder(ans3, names(ans2))
setorder(ans1, id)
setkey(ans2, NULL)
setorder(ans3, id)
identical(ans1, ans2) # TRUE
identical(ans1, ans3) # TRUE
其中,
AnsFunction <- function() {
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
dt[, {set(ans, i=.GRP, j=unique(y), value=1L); NULL}, by=id]
ans
# reorder columns outside
}
dcastFunction <- function() {
# no need to load reshape2. data.table has its own dcast as well
# no need for setDT
df <- dcast(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L,value.var = "y")
}
TableFunction <- function() {
# need to return integer results for identical results
# fixed 1 -> 1L; as.numeric -> as.integer
df <- as.data.frame.matrix(table(dt$id, dt$y))
df[df > 1L] <- 1L
df <- cbind(id = as.integer(row.names(df)), df)
setDT(df)
}
对于小型数据集,table 函数似乎更有效,但对于大型数据集,dcast 似乎是最有效和方便的选择。
TableFunction <- function(){
df <- as.data.frame.matrix(table(dt$id, dt$y))
df[df > 1] <- 1
df <- cbind(id = as.numeric(row.names(df)), df)
setDT(df)
}
AnsFunction <- function(){
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
dt[, {set(ans, i=id, j=unique(y), value=1L); NULL}, by=id]
}
dcastFunction <- function(){
df <-dcast.data.table(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L,value.var = "y")
}
library(data.table)
library(microbenchmark)
set.seed(123)
N = 10000
dt <- data.table(id=rep(1:N, each=5),y=sample(letters[1 : 5], N*5, replace = T))
microbenchmark(
"dcast" = dcastFunction(),
"Table" = TableFunction(),
"Ans" = AnsFunction()
)
Unit: milliseconds
expr min lq mean median uq max neval cld
dcast 42.48367 45.39793 47.56898 46.83755 49.33388 60.72327 100 b
Table 28.32704 28.74579 29.14043 29.00010 29.23320 35.16723 100 a
Ans 120.80609 123.95895 127.35880 126.85018 130.12491 156.53289 100 c
> all(test1 == test2)
[1] TRUE
> all(test1 == test3)
[1] TRUE
y = apply(matrix(sample(letters, 10L*20L, TRUE), ncol=20L), 1L, paste, collapse="")
dt = data.table(id=sample(1e5,1e7,TRUE), y=sample(y,1e7,TRUE))
microbenchmark(
"dcast" = dcastFunction(),
"Table" = TableFunction(),
"Ans" = AnsFunction()
)
Unit: seconds
expr min lq mean median uq max neval cld
dcast 1.985969 2.064964 2.189764 2.216138 2.266959 2.643231 100 a
Table 5.022388 5.403263 5.605012 5.580228 5.830414 6.318729 100 c
Ans 2.234636 2.414224 2.586727 2.599156 2.645717 2.982311 100 b
我有一个很大的(1200 万行)data.table
看起来像这样:
library(data.table)
set.seed(123)
dt <- data.table(id=rep(1:3, each=5),y=sample(letters[1:5],15,replace = T))
> dt
id y
1: 1 b
2: 1 d
3: 1 c
4: 1 e
5: 1 e
6: 2 a
7: 2 c
8: 2 e
9: 2 c
10: 2 c
11: 3 e
12: 3 c
13: 3 d
14: 3 c
15: 3 a
我想创建一个新的 data.table
包含我的变量 id
(这将是这个新的 data.table
的唯一键)和其他 5 个二进制变量,每个变量对应于每个变量如果 id 具有 y
的值,y
的类别取值 1
,否则为 0
。
输出 data.table
应如下所示:
id a b c d e
1: 1 0 1 1 1 1
2: 2 1 0 1 0 1
3: 3 1 0 1 1 1
我尝试在循环中执行此操作,但速度很慢,而且我不知道如何以编程方式传递二进制变量名称,因为它们取决于我尝试 "split" 的变量。
编辑:正如@mtoto 指出的那样,已经提出并回答了类似的问题here,但解决方案是使用reshape2
包.
我想知道是否有另一种(更快)的方法可以通过在 data.table 中使用 :=
运算符来做到这一点,因为我有一个庞大的数据集并且我正在使用这个包进行很多工作。
EDIT2:@Arun post 中函数的基准测试(约 1200 万行,约 350 万个不同的 ID 和 490 个不同的标签) y
变量(产生 490 个虚拟变量):
system.time(ans1 <- AnsFunction()) # 194s
system.time(ans2 <- dcastFunction()) # 55s
system.time(ans3 <- TableFunction()) # Takes forever and blocked my PC
如果您已经知道行的范围(因为您知道示例中的行不超过 3 行)并且您知道可以从零数组开始并使用 apply 函数的列更新该辅助 table.
中的值我的 R 有点生锈,但我认为应该可以。此外,您传递给 apply 方法的函数可以包含根据需要添加必要的行和列的条件。
我的 R 有点生锈,所以我现在有点犹豫要不要写,但我认为这就是写的方法。
如果您正在寻找更即插即用的东西,我发现了这个小 blerb:
There are two sets of methods that are explained below:
gather() and spread() from the tidyr package. This is a newer interface to the reshape2 package.
melt() and dcast() from the reshape2 package.
There are a number of other methods which aren’t covered here, since they are not as easy to use:
The reshape() function, which is confusingly not part of the reshape2 package; it is part of the base install of R.
stack() and unstack()
从这里:: http://www.cookbook-r.com/Manipulating_data/Converting_data_between_wide_and_long_format/
如果我更精通 R,我会告诉您这些不同的方法如何处理从长列表到宽列表的冲突。我在谷歌上搜索 "Make a table from flat data in R" 想出了这个...
另请查看 this 这是与上面相同的网站和我的个人评论包装器:p
data.table 有自己的 dcast
实现,使用 data.table 的内部结构,应该很快。试一试:
dcast(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L)
# id a b c d e
# 1: 1 0 1 1 1 1
# 2: 2 1 0 1 0 1
# 3: 3 1 0 1 1 1
刚刚想到了另一种方法来处理这个问题,通过引用进行预分配和更新(也许 dcast 的逻辑应该这样做以避免中间体)。
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
剩下的就是用 1L
.
dt[, {set(ans, i=.GRP, j=unique(y), value=1L); NULL}, by=id]
ans
# id b d c e a
# 1: 1 1 1 1 1 0
# 2: 2 0 0 1 1 1
# 3: 3 0 1 1 1 1
好的,我已经继续对 OP 的数据维度进行基准测试,大约 1000 万行和 10 列。
require(data.table)
set.seed(45L)
y = apply(matrix(sample(letters, 10L*20L, TRUE), ncol=20L), 1L, paste, collapse="")
dt = data.table(id=sample(1e5,1e7,TRUE), y=sample(y,1e7,TRUE))
system.time(ans1 <- AnsFunction()) # 2.3s
system.time(ans2 <- dcastFunction()) # 2.2s
system.time(ans3 <- TableFunction()) # 6.2s
setcolorder(ans1, names(ans2))
setcolorder(ans3, names(ans2))
setorder(ans1, id)
setkey(ans2, NULL)
setorder(ans3, id)
identical(ans1, ans2) # TRUE
identical(ans1, ans3) # TRUE
其中,
AnsFunction <- function() {
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
dt[, {set(ans, i=.GRP, j=unique(y), value=1L); NULL}, by=id]
ans
# reorder columns outside
}
dcastFunction <- function() {
# no need to load reshape2. data.table has its own dcast as well
# no need for setDT
df <- dcast(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L,value.var = "y")
}
TableFunction <- function() {
# need to return integer results for identical results
# fixed 1 -> 1L; as.numeric -> as.integer
df <- as.data.frame.matrix(table(dt$id, dt$y))
df[df > 1L] <- 1L
df <- cbind(id = as.integer(row.names(df)), df)
setDT(df)
}
对于小型数据集,table 函数似乎更有效,但对于大型数据集,dcast 似乎是最有效和方便的选择。
TableFunction <- function(){
df <- as.data.frame.matrix(table(dt$id, dt$y))
df[df > 1] <- 1
df <- cbind(id = as.numeric(row.names(df)), df)
setDT(df)
}
AnsFunction <- function(){
ans = data.table(id = unique(dt$id))[, unique(dt$y) := 0L][]
dt[, {set(ans, i=id, j=unique(y), value=1L); NULL}, by=id]
}
dcastFunction <- function(){
df <-dcast.data.table(dt, id ~ y, fun.aggregate = function(x) 1L, fill=0L,value.var = "y")
}
library(data.table)
library(microbenchmark)
set.seed(123)
N = 10000
dt <- data.table(id=rep(1:N, each=5),y=sample(letters[1 : 5], N*5, replace = T))
microbenchmark(
"dcast" = dcastFunction(),
"Table" = TableFunction(),
"Ans" = AnsFunction()
)
Unit: milliseconds
expr min lq mean median uq max neval cld
dcast 42.48367 45.39793 47.56898 46.83755 49.33388 60.72327 100 b
Table 28.32704 28.74579 29.14043 29.00010 29.23320 35.16723 100 a
Ans 120.80609 123.95895 127.35880 126.85018 130.12491 156.53289 100 c
> all(test1 == test2) [1] TRUE > all(test1 == test3) [1] TRUE
y = apply(matrix(sample(letters, 10L*20L, TRUE), ncol=20L), 1L, paste, collapse="")
dt = data.table(id=sample(1e5,1e7,TRUE), y=sample(y,1e7,TRUE))
microbenchmark(
"dcast" = dcastFunction(),
"Table" = TableFunction(),
"Ans" = AnsFunction()
)
Unit: seconds
expr min lq mean median uq max neval cld
dcast 1.985969 2.064964 2.189764 2.216138 2.266959 2.643231 100 a
Table 5.022388 5.403263 5.605012 5.580228 5.830414 6.318729 100 c
Ans 2.234636 2.414224 2.586727 2.599156 2.645717 2.982311 100 b