我有一个df:
head(df)
:
Year Asset1 Asset2 Asset3 Asset4 Asset5
1 1857 1729900 32570 288482 1251642 0 0 67374 89832
2 1858 1870213 35255 312262 1354817 0 0 71948 95931
3 1859 1937622 36418 322562 1399505 0 0 76773 102364
4 1860 1969257 207557 83393 1484403 0 0 83102 110802
5 1861 2107481 222969 89585 1594627 0 0 85843 114457
6 1862 2306227 235498 94619 1684234 0 0 80613 211263
我使用ddply
构建一个新的df,其中Asset 2:5除以Asset1:
dft<-ddply(df,.(Year),transform,
Asset2=Asset2/Asset1,
Asset3=Asset3/Asset1,
Asset4=Asset4/Asset1,
Asset5=Asset5/Asset1)
但如果有很多专栏,那么很多工作都很安静......有什么建议吗?
最诚挚的问候!
答案 0 :(得分:8)
这是sweep
的用途:
读入(修改过的)数据版本:
m <- read.table(text = " Year Asset1 Asset2 Asset3 Asset4 Asset5
+ 1857 1729900 32570 288482 1251642 0
+ 1858 1870213 35255 312262 1354817 0
+ 1859 1937622 36418 322562 1399505 0
+ 1860 1969257 207557 83393 1484403 0
+ 1861 2107481 222969 89585 1594627 0
+ 1862 2306227 235498 94619 1684234 0 ",header = TRUE,sep = "")
> m
Year Asset1 Asset2 Asset3 Asset4 Asset5
1 1857 1729900 32570 288482 1251642 0
2 1858 1870213 35255 312262 1354817 0
3 1859 1937622 36418 322562 1399505 0
4 1860 1969257 207557 83393 1484403 0
5 1861 2107481 222969 89585 1594627 0
6 1862 2306227 235498 94619 1684234 0
> m[,3:6] <- sweep(m[,3:6],1,m[,2],"/")
> m
Year Asset1 Asset2 Asset3 Asset4 Asset5
1 1857 1729900 0.01882768 0.16676224 0.7235343 0
2 1858 1870213 0.01885079 0.16696601 0.7244186 0
3 1859 1937622 0.01879520 0.16647313 0.7222797 0
4 1860 1969257 0.10539864 0.04234744 0.7537884 0
5 1861 2107481 0.10579882 0.04250809 0.7566507 0
6 1862 2306227 0.10211397 0.04102762 0.7302984 0
答案 1 :(得分:5)
好的,我有2个lapply
解决方案。我对上面的解决方案进行了标记,并且循环实际上比矢量化解决方案更快。为什么呢?
编辑:请参阅nograpes回答。
lapply
解决方案:
m[, 3:6] <- do.call(cbind, lapply(m[, 3:6], function(x) x/m[, 2]))
m
lapply2:
lapply(3:6, function(i) {
m[, i] <<- m[, i]/m[, 2]
})
# Year Asset1 Asset2 Asset3 Asset4 Asset5
# 1 1857 1729900 0.01882768 0.16676224 0.7235343 0
# 2 1858 1870213 0.01885079 0.16696601 0.7244186 0
# 3 1859 1937622 0.01879520 0.16647313 0.7222797 0
# 4 1860 1969257 0.10539864 0.04234744 0.7537884 0
# 5 1861 2107481 0.10579882 0.04250809 0.7566507 0
# 6 1862 2306227 0.10211397 0.04102762 0.7302984 0
在具有1000次重复的i7 windows机器上进行微基准测试的替换:
设置:
LAPPLY <- function() {
m[, 3:6] <- do.call(cbind, lapply(m[, 3:6], function(x) x/m[, 2]))
m
}
LOOP <- function() {
for(i in 3:ncol(m)) {
m[ ,i] <- m[ , i]/m[ ,2]
}
m
}
SWEEP <- function(){
m[,3:6] <- sweep(m[,3:6],1,m[,2],"/")
m
}
LAPPLY2 <- function() {
lapply(3:6, function(i) {
m[, i] <<- m[, i]/m[, 2]
})
m
}
VECTORIZED <- function(){
m[,3:6]<-m[,3:6] / m[,2]
m
}
VECTORIZED2 <- function(){
m[,3:6]<-unlist(m[,3:6])/m[,2]
m
}
microbenchmark(
SWEEP(),
LAPPLY(),
LOOP(),
VECTORIZED(),
VECTORIZED2(),
LAPPLY2(),
times=1000L)
结果:
Unit: microseconds
expr min lq median uq max
1 LAPPLY() 7483.059 7577.758 7649.3655 7839.9290 41808.754
2 LAPPLY2() 563.061 602.713 618.3405 661.9585 7535.308
3 LOOP() 540.669 581.254 594.7820 626.5050 35505.929
4 SWEEP() 2544.735 2602.581 2645.9650 2735.5320 8335.814
5 VECTORIZED() 2409.452 2454.235 2494.5870 2585.5535 37313.134
6 VECTORIZED2() 8952.055 9063.081 9153.8150 9352.3085 45742.247
编辑:虽然我通过将索引传递给
lapply
并全局分配循环正在做什么来获得加速(lapply
是我认为的循环的包装器):
注意:LAPPLY2必须最后进行基准测试,因为它会对m进行全局更改(并且必须在运行LAPPLY2后重置m)。解释为什么全球任务可能是危险的。
我还重复了OP 100次(nrow x 100)的数据帧,作为解决方案的betetr模拟。
编辑37 partB:这是我的结果,没有重复数据框以及我如何复制数据框:
# Unit: microseconds
# expr min lq median uq max
# 1 LAPPLY() 428.710 451.5680 468.362 485.6220 1497.452
# 2 LAPPLY2() 331.212 355.9365 368.532 386.7260 1361.235
# 3 LOOP() 326.547 355.0040 369.465 383.9260 1361.235
# 4 SWEEP() 828.497 868.1490 890.541 924.5950 31512.726
# 5 VECTORIZED() 764.587 809.8370 828.497 859.9855 3042.486
# 6 VECTORIZED2() 374.596 394.6560 408.884 424.0460 1399.954
dfdup <- function(dataframe, repeats=10){
DF <- dataframe[rep(seq_len(nrow(dataframe)), repeats), ]
rownames(DF) <-NULL
DF
}
m&lt; - dfdup(m,100)
答案 2 :(得分:2)
我认为这是一个很好的,可读的替代方案:
df[,3:6]<-df[,3:6] / df[,2]
如果你想让它更具可读性,你可以做到
df[,paste0('Asset',2:5)]<-df[,paste0('Asset',2:5)] / df[,'Asset1']
我发现上面的函数很慢,因为它传递到Ops.data.frame
(我认为),这很慢。为了避免这种情况:
df[,3:6]<-unlist(df[,3:6])/df[,2]
但它只能与其他循环和lapply
版本一样快。
答案 3 :(得分:1)
这不是ddply
的意思,在这种情况下你不需要它。 ddply
适用于根据其中一列中的值将数据框拆分为行。通常,用于拆分数据框的列(在本例中为Year
)将具有多个具有相同值的行。
在这里,您只是将一列与另一列分开。您可以按如下方式执行此操作:
df$Asset2 <- df$Asset2/df$Asset1 #more human-readable
或
df[ ,3] <- df[ ,3]/df[ ,2] #numbered columns are useful in loops
我怀疑有一种矢量化方式可以做你想要的,但除非速度是一个主要问题,否则循环这个计算非常简单:
#[hide under desk to avoid vectorization police]
for(i in 3:ncol(df) {
df[ ,i] <- df[ , i]/df[ ,2]
}
IMO您可能希望重命名列,或者保留旧列并创建新列,以避免对列是否包含比率或原始值感到困惑。如果您想制作新列,请使用df[ ,ncol(df)+1] <- df[ , i]/df[ ,2]