我有一个大小为30000 x 50的数据帧数据。我还有一个单独的列表,其中包含来自此数据帧的行分组,例如,
rows <- list(c("34", "36", "39"), c("45", "46"))
这表示具有rownames(不是数字行indeces,但是字符rownames(dat))“34”,“36”,“39”的数据帧行构成一个分组,而“45”,“46”构成另一个分组。
现在我想将数据框中的分组拉出到并行列表中,但我的代码(下面)非常非常慢。我怎样才能加快速度?
> system.time(lapply(rows, function(r) {dat[r, ]}))
user system elapsed
246.09 0.01 247.23
这是在非常快的计算机上,R 2.14.1 x64。
答案 0 :(得分:17)
其中一个主要问题是行名称的匹配 - [.data.frame
中的默认值是行名称的部分匹配,您可能不希望这样,所以最好使用{{1} }。为了进一步加快速度,如果需要,可以使用match
中的fmatch
。这是一个小修改,有一些加速:
fastmatch
如果您的# naive
> system.time(res1 <- lapply(rows,function(r) dat[r,]))
user system elapsed
69.207 5.545 74.787
# match
> rn <- rownames(dat)
> system.time(res1 <- lapply(rows,function(r) dat[match(r,rn),]))
user system elapsed
36.810 10.003 47.082
# fastmatch
> rn <- rownames(dat)
> system.time(res1 <- lapply(rows,function(r) dat[fmatch(r,rn),]))
user system elapsed
19.145 3.012 22.226
不重叠,则可以不使用[
(数据帧速度慢),而是分割数据框(使用split
),从而进一步加快速度覆盖所有行(因此您可以将每行映射到行中的一个条目)。
根据您的实际数据,您可能会更好地使用具有更快的子集运算符的矩阵,因为它们是原生的。
答案 1 :(得分:5)
我的原帖是从这个错误的陈述开始的:
Simon在评论中指出,R显然使用哈希表进行索引。抱歉错误。通过
rownames
和colnames
建立索引的问题就在于此 正在为每个元素运行矢量/线性扫描,例如。你在打猎 通过每一行来查看哪个名为“36”,然后从中开始 开始为“34”再做一次。
请注意,此答案中的建议假设您有非重叠的数据子集。
如果你想保留你的列表查找策略,我建议存储实际的行索引而不是字符串名称。
另一种方法是将您的“群组”信息作为另一列存储到data.frame
,然后split
将其data.frame
存储在其群组中,例如。让我们说你的重新编码data.frame
看起来像这样:
dat <- data.frame(a=sample(100, 10),
b=rnorm(10),
group=sample(c('a', 'b', 'c'), 10, replace=TRUE))
然后你可以这样做:
split(dat, dat$group)
$a
a b group
2 66 -0.08721261 a
9 62 -1.34114792 a
$b
a b group
1 32 0.9719442 b
5 79 -1.0204179 b
6 83 -1.7645829 b
7 73 0.4261097 b
10 44 -0.1160913 b
$c
a b group
3 77 0.2313654 c
4 74 -0.8637770 c
8 29 1.0046095 c
或者,根据您对“拆分”的真实想法,您可以将data.frame
转换为data.table
并将其密钥设置为新的group
列:< / p>
library(data.table)
dat <- data.table(dat, key="group")
现在执行您的列表操作 - 这将为您提供与上面的split
x <- lapply(unique(dat$group), function(g) dat[J(g),])
但是你可能想要“解决你的唾液”,你可以内联,例如:
ans <- dat[, {
## do some code over the data in each split
## and return a list of results, eg:
list(nrow=length(a), mean.a=mean(a), mean.b=mean(b))
}, by="group"]
ans
group nrow mean.a mean.b
[1,] a 2 64.0 -0.7141803
[2,] b 5 62.2 -0.3006076
[3,] c 3 60.0 0.1240660
您可以使用plyr
以“类似方式”执行最后一步,例如:
library(plyr)
ddply(dat, "group", summarize, nrow=length(a), mean.a=mean(a),
mean.b=mean(b))
group nrow mean.a mean.b
1 a 2 64.0 -0.7141803
2 b 5 62.2 -0.3006076
3 c 3 60.0 0.1240660
但是既然你提到你的数据集非常大,我想你会想要提速data.table
。
答案 2 :(得分:4)
这是加速的一次尝试 - 它取决于查找行索引比查找行名更快的事实,因此尝试在dat
中将rowname映射到rownumber
首先创建一些与您相同大小的数据并指定一些数字rownames:
> dat <- data.frame(matrix(runif(30000*50),ncol=50))
> rownames(dat) <- as.character(sample.int(nrow(dat)))
> rownames(dat)[1:5]
[1] "21889" "3050" "22570" "28140" "9576"
现在生成一个包含15000个元素的随机rows
,每个元素包含1到30000的50个随机数(在这种情况下为行*名*):
# 15000 groups of up to 50 rows each
> rows <- sapply(1:15000, function(i) as.character(sample.int(30000,size=sample.int(50,size=1))))
出于计时目的,请尝试问题中的方法( ouch!):
# method 1
> system.time((res1 <- lapply(rows,function(r) dat[r,])))
user system elapsed
182.306 0.877 188.362
现在,尝试从行名到行号进行映射。 map[i]
应该为行号指定名称i
。
第一如果您的行名称是1:nrow(dat)
的排列,那么您很幸运!您所要做的就是对rownames进行排序,并返回索引:
> map <- sort(as.numeric(rownames(dat)), index.return=T)$ix
# NOTE: map[ as.numeric(rowname) ] -> rownumber into dat for that rowname.
现在查找行索引而不是行名称:
> system.time((res2 <- lapply(rows,function(r) dat[map[as.numeric(r)],])))
user system elapsed
32.424 0.060 33.050
检查我们没有搞砸任何东西(注意它与rownames匹配就足够了,因为rownames在R中是唯一的):
> all(rownames(res1)==rownames(res2))
[1] TRUE
所以,速度加快了~6倍。虽然还不令人惊讶......
SECOND 如果你运气不好且你的rownames与nrow(dat)
完全无关,那么你可以试试这个,但是仅当max(as.numeric(rownames(dat)))
不比nrow(dat)
大得多时。它基本上使map
map[rowname]
给出行号,但由于rownames不一定是连续的,map
中可能有大量的空白浪费了一点内存:
map <- rep(-1,max(as.numeric(rownames(dat))))
obj <- sort(as.numeric(rownames(dat)), index.return=T)
map[obj$x] <- obj$ix
然后像以前一样使用map
(dat[map[as.numeric(r),]]
)。
答案 3 :(得分:2)
你可以试试这个修改:
system.time(lapply(rows, function(r) {dat[ rownames(dat) %in% r, ]}))
答案 4 :(得分:1)
我同意数学咖啡,我也很快得到这个。
不知道是否可能,但通过将其列为矢量然后转换为数字,您可以获得速度提升。
dat <- data.frame(matrix(rnorm(30000*50), 30000, 50 ))
rows <- as.numeric(unlist(list(c("34", "36", "39"), c("45", "46"))))
system.time(lapply(rows, function(r) {dat[r, ]}))
编辑:
dat$observ <- rownames(dat)
rownames(dat) <- 1:nrow(dat)