重新编码是调查数据的常见做法,但最明显的路线需要的时间比应有的多。
通过我的计算机上system.time()
提供的示例数据完成相同任务的最快代码获胜。
## Sample data
dat <- cbind(rep(1:5,50000),rep(5:1,50000),rep(c(1,2,4,5,3),50000))
dat <- cbind(dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat)
dat <- as.data.frame(dat)
re.codes <- c("This","That","And","The","Other")
优化代码。
for(x in 1:ncol(dat)) {
dat[,x] <- factor(dat[,x], labels=re.codes)
}
当前system.time()
:
user system elapsed
4.40 0.10 4.49
提示:dat <- lapply(1:ncol(dat), function(x) dat[,x] <- factor(dat[,x],labels=rc)))
速度不快。
答案 0 :(得分:10)
我的计算机显然要慢得多,但结构是一种非常快速的方法:
> system.time({
+ dat1 <- dat
+ for(x in 1:ncol(dat)) {
+ dat1[,x] <- factor(dat1[,x], labels=re.codes)
+ }
+ })
user system elapsed
11.965 3.172 15.164
>
> system.time({
+ m <- as.matrix(dat)
+ dat2 <- data.frame( matrix( re.codes[m], nrow = nrow(m)))
+ })
user system elapsed
2.100 0.516 2.621
>
> system.time(dat3 <- data.frame(lapply(dat, structure, class='factor', levels=re.codes)))
user system elapsed
0.484 0.332 0.820
# this isn't because the levels get re-ordered
> all.equal(dat1, dat2)
> all.equal(dat1, dat3)
[1] TRUE
答案 1 :(得分:10)
结合@DWin's answer和Most efficient list to data.frame method?的回答:
system.time({
dat3 <- list()
# define attributes once outside of loop
attrib <- list(class="factor", levels=re.codes)
for (i in names(dat)) { # loop over each column in 'dat'
dat3[[i]] <- as.integer(dat[[i]]) # convert column to integer
attributes(dat3[[i]]) <- attrib # assign factor attributes
}
# convert 'dat3' into a data.frame. We can do it like this because:
# 1) we know 'dat' and 'dat3' have the same number of rows and columns
# 2) we want 'dat3' to have the same colnames as 'dat'
# 3) we don't care if 'dat3' has different rownames than 'dat'
attributes(dat3) <- list(row.names=c(NA_integer_,nrow(dat)),
class="data.frame", names=names(dat))
})
identical(dat2, dat3) # 'dat2' is from @Dwin's answer
答案 2 :(得分:8)
试试这个:
m <- as.matrix(dat)
dat <- data.frame( matrix( re.codes[m], nrow = nrow(m)))
答案 3 :(得分:7)
data.table
答案供您考虑。我们只使用了setattr()
,data.frame
和data.frame
列。无需转换为data.table
。
再次测试数据:
dat <- cbind(rep(1:5,50000),rep(5:1,50000),rep(c(1L,2L,4L,5L,3L),50000))
dat <- cbind(dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat)
dat <- as.data.frame(dat)
re.codes <- c("This","That","And","The","Other")
现在更改类并直接设置每列的级别,参考:
require(data.table)
system.time(for (i in 1:ncol(dat)) {
setattr(dat[[i]],"levels",re.codes)
setattr(dat[[i]],"class","factor")
}
# user system elapsed
# 0 0 0
identical(dat, <result in question>)
# [1] TRUE
0.00赢了吗?随着您增加数据的大小,此方法保持为0.00 。
好的,我承认,我将所有列的输入数据略微更改为integer
(问题在第三列中有double
个输入数据)。这些double
列必须转换为integer
,因为factor
仅对integer
个向量有效。正如其他答案所述。
因此,严格按照问题中的输入数据进行操作,并包括double
到integer
转换:
dat <- cbind(rep(1:5,50000),rep(5:1,50000),rep(c(1,2,4,5,3),50000))
dat <- cbind(dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat,dat)
dat <- as.data.frame(dat)
re.codes <- c("This","That","And","The","Other")
system.time(for (i in 1:ncol(dat)) {
if (!is.integer(dat[[i]]))
set(dat,j=i,value=as.integer(dat[[i]]))
setattr(dat[[i]],"levels",re.codes)
setattr(dat[[i]],"class","factor")
})
# user system elapsed
# 0.06 0.01 0.08 # on my slow netbook
identical(dat, <result in question>)
# [1] TRUE
请注意,set
也适用于data.frame
。您无需转换为data.table
即可使用它。
很明显,这些时间非常短暂。因为它只是一个小的输入数据集:
dim(dat)
# [1] 250000 36
object.size(dat)
# 68.7 Mb
从中扩大应该会发现更大的差异。但即便如此,我认为应该(几乎)可测量的速度最快。但是,在这个规模上,没有人会想到这一点。
setattr
函数也在bit
包中,顺便说一下。因此,0.00方法可以使用data.table
或bit
来完成。要通过引用进行类型转换(如果需要),需要set
或:=
(均在data.table
中),afaik。
答案 4 :(得分:6)
class()的帮助页面说该类&lt; - 已弃用并用作。方法。我还没弄清楚为什么当数据明显在对象中时,之前的努力报告了0个观察结果,但是这个方法产生了一个完整的对象:
system.time({ dat2 <- vector(mode="list", length(dat))
for (i in 1:length(dat) ){ dat2[[i]] <- dat[[i]]
storage.mode(dat2[[i]]) <- "integer"
attributes(dat2[[i]]) <- list(class="factor", levels=re.codes)}
names(dat2) <- names(dat)
dat2 <- as.data.frame(dat2)})
#--------------------------
user system elapsed
0.266 0.290 0.560
> str(dat2)
'data.frame': 250000 obs. of 36 variables:
$ V1 : Factor w/ 5 levels "This","That",..: 1 2 3 4 5 1 2 3 4 5 ...
$ V2 : Factor w/ 5 levels "This","That",..: 5 4 3 2 1 5 4 3 2 1 ...
$ V3 : Factor w/ 5 levels "This","That",..: 1 2 4 5 3 1 2 4 5 3 ...
$ V4 : Factor w/ 5 levels "This","That",..: 1 2 3 4 5 1 2 3 4 5 ...
$ V5 : Factor w/ 5 levels "This","That",..: 5 4 3 2 1 5 4 3 2 1 ...
$ V6 : Factor w/ 5 levels "This","That",..: 1 2 4 5 3 1 2 4 5 3 ...
$ V7 : Factor w/ 5 levels "This","That",..: 1 2 3 4 5 1 2 3 4 5 ...
$ V8 : Factor w/ 5 levels "This","That",..: 5 4 3 2 1 5 4 3 2 1 ...
snipped
所有36列都在那里。
答案 5 :(得分:3)
制造因素很昂贵;只执行一次与使用structure
的命令相当,而且在我看来,最好不要依赖于如何构建因子。
rc <- factor(re.codes, levels=re.codes)
dat5 <- as.data.frame(lapply(dat, function(d) rc[d]))
编辑2:有趣的是,这似乎是lapply
确实加速的情况。这个for循环要慢得多。
for(i in seq_along(dat)) {
dat[[i]] <- rc[dat[[i]]]
}
编辑1:您还可以通过更精确地使用类型来加快速度。尝试使用任何解决方案(尤其是原始解决方案)将数据创建为整数,如下所示。有关详细信息,请参阅我之前的答案here。
dat <- cbind(rep(1:5,50000),rep(5:1,50000),rep(c(1L,2L,4L,5L,3L),50000))
这也是一个好主意,因为从浮点转换为整数,就像在这里所有更快的解决方案中所做的那样,可能会产生意外行为,请参阅this question。