由网友(心若向阳无谓悲伤)分享简介:如何有效地从数据框中提取按组排列的常量列?我在下面包含了一个plyr实现,以精确地说明我想要做的事情,但速度很慢。我怎样才能尽可能高效地做这件事呢?(理想情况下,根本不分割数据帧)。base <- data.frame(group = 1:1000, a = sample(1000), b = sample(1000)...如何有效地从数据框中提取按组排列的常量列?我在下面包含了一个plyr实现,以精确地说明我想要做的事情,但速度很慢。我怎样才能尽可能高效地做这件事呢?(理想情况下,根本不分割数据帧)。
base <- data.frame(group = 1:1000, a = sample(1000), b = sample(1000))
df <- data.frame(
base[rep(seq_len(nrow(base)), length = 1e6), ],
c = runif(1e6),
d = runif(1e6)
)
is.constant <- function(x) length(unique(x)) == 1
constant_cols <- function(x) head(Filter(is.constant, x), 1)
system.time(constant <- ddply(df, "group", constant_cols))
# user system elapsed
# 20.531 1.670 22.378
stopifnot(identical(names(constant), c("group", "a", "b")))
stopifnot(nrow(constant) == 1000)
在我的实际用例中(深入ggplot2),可能有任意数量的常量和非常量列。该示例中的数据大小大约是正确的数量级。
推荐答案
受@joran回答的启发,这里有一个类似的策略,速度更快一些(在我的机器上,1秒比1.5秒)
changed <- function(x) c(TRUE, x[-1] != x[-n])
constant_cols2 <- function(df,grp){
df <- df[order(df[,grp]),]
n <- nrow(df)
changes <- lapply(df, changed)
vapply(changes[-1], identical, changes[[1]], FUN.VALUE = logical(1))
}
system.time(cols <- constant_cols2(df, "group")) # about 1 s
system.time(constant <- df[changed(df$group), cols])
# user system elapsed
# 1.057 0.230 1.314
stopifnot(identical(names(constant), c("group", "a", "b")))
stopifnot(nrow(constant) == 1000)
它有相同的缺陷,因为它不会检测相邻组具有相同值的列(例如df$f <- 1
)
多思考一下,再加上@David的想法:
constant_cols3 <- function(df, grp) {
# If col == TRUE and group == FALSE, not constant
matching_breaks <- function(group, col) {
!any(col & !group)
}
n <- nrow(df)
changed <- function(x) c(TRUE, x[-1] != x[-n])
df <- df[order(df[,grp]),]
changes <- lapply(df, changed)
vapply(changes[-1], matching_breaks, group = changes[[1]],
FUN.VALUE = logical(1))
}
system.time(x <- constant_cols3(df, "group"))
# user system elapsed
# 1.086 0.221 1.413
这会给出正确的结果。
相关推荐
最新文章