我在一个青少年犯罪数据库工作,需要报告开始犯罪的年龄。我目前有犯罪的姓名和年龄,许多对象都是惯犯,我需要隔离最早的犯罪年龄。我可以逐行浏览(31,000+行),但我希望有更简单的方法来实现这一点。
我所拥有的
Subject A 15
Subject A 17
Subject A 17
Subject B 11
Subject B 12
Subject B 15
Subject B 17我需要的是
Subject A 15
Subject A 15
Subject A 15
Subject B 11
Subject B 11
Subject B 11
Subject B 11发布于 2014-07-31 22:00:41
以下是几种实现此目的的方法:
# your sample data
df <- read.table(header=F, text="
Subject_A 15
Subject_A 17
Subject_A 17
Subject_B 11
Subject_B 12
Subject_B 15
Subject_B 17", stringsAsFactors = FALSE)
names(df) <- c("Subject", "Age") # add some column names使用base R ave
df$Min_Age <- ave(df$Age, df$Subject, FUN = min)或者使用dplyr
library(dplyr)
df <- df %>%
group_by(Subject) %>%
mutate(Min_Age = min(Age))或者使用data.table
library(data.table)
setDT(df)[, Min_Age := min(Age), by = Subject]如果您只想用最小值替换Age列,而不想创建新列,则可以用现有的列名Age替换每个解决方案中的Min_Age条目。
编辑:这里有一个小的基准测试(不包括for循环,因为我不认为应该这样做)。
df <- data.frame(Subject = sample(LETTERS, 1e4, TRUE),
Age = sample(10:99, 1e4, TRUE))
dt <- as.data.table(df)
library(microbenchmark)
library(doBy)
microbenchmark(
ave1 = {ave(df$Age, df$Subject, FUN = min)},
ave2 = {with(df, ave(Age, Subject, FUN = min))},
dplyr1 = {df %>% group_by(Subject) %>% mutate(Min_Age = min(Age))},
dplyr2 = {df%>% group_by(Subject) %>% arrange(Subject,Age) %>% mutate(Min_Age=Age[1])},
data.table = {dt[, Min_Age := min(Age), by = Subject]},
doBy = {summaryBy(Age ~ Subject, df, FUN = min, full.dimension = TRUE)},
lapply = {with(df, unsplit(lapply(split(Age, Subject), min), df[[1]]))},
unit = "relative",
times = 100)
# Unit: relative
# expr min lq median uq max neval
#ave1 1.022080 1.015667 1.029203 1.040314 3.017348 100
#ave2 1.000000 1.000000 1.000000 1.000000 1.000000 100
#dplyr1 1.158047 1.168557 1.207314 1.229463 1.075171 100
#dplyr2 4.452059 4.408963 4.424622 4.374746 3.692858 100
#data.table 1.143520 1.212317 1.265719 1.280680 3.265307 100
#doBy 18.047627 17.584799 17.609035 17.470075 19.118029 100
#lapply 1.164438 1.120205 1.117074 1.116633 3.186735 100因此,base R的ave在这种情况下执行得很好,尽管结果可能会根据实际数据中的组大小而变化。
更新:基准测试中包含doBy和lapply版本。
发布于 2014-07-31 22:58:24
您可以使用doBy包中的summaryBy。
> library(doBY)
> summaryBy(V2~V1, data = dat, FUN = min, full.dimension = TRUE)或者使用split和unsplit的另一种方式
> s <- with(dat, split(V2, V1))
> dat$V2 <- unsplit(lapply(s, min), dat$V1)另一个是使用ddply
> library(plyr)
> ddply(dat, .(V1), summarize, min = rep(min(V2), length(V2)))dat在哪里
dat <- read.table(text = "SubjectA 15
SubjectA 17
SubjectA 17
SubjectB 11
SubjectB 12
SubjectB 15
SubjectB 17")因为我们要对所有东西进行基准测试,所以R是我的三个中最快的。
> f <- function(){
dat$V2 <- with(dat, unsplit(lapply(split(V2, V1), min), dat[[1]]) )
dat
}
> microbenchmark(f())
# Unit: microseconds
# expr min lq median uq max neval
# f() 108.788 110.4575 111.0665 112.108 251.813 100发布于 2014-07-31 22:00:17
您的数据是在包含2列的数据帧中,还是在这些单独的字符串中?
如果它是一个有2列的数据帧(假设列名是" name“和"age"),并且该数据帧的变量名是”column“:
split.crime <- split(crime, name)
result.df <- data.frame()
for (sub.df in split.crime) {
min.age <- min(df$age)
new.sub.df <- data.frame(name = sub.df$name, age = min.age)
result.df <- rbind(result.df, new.sub.df) }
rm(split.crime)
result.df这是一个非常透明和缓慢的版本。我确信它可以用更少的代码和更快的速度来完成。对于您的数据帧大小,它应该工作得很好。
https://stackoverflow.com/questions/25060629
复制相似问题