我正在处理青少年犯罪数据库,需要报告发病年龄。我目前的名字和年龄都在冒犯,许多科目都是屡次犯罪,我需要隔离最早的犯罪年龄。我可以逐行(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
答案 0 :(得分:2)
以下是一些方法:
# 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
使用基础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
列而不是创建新列,则可以使用现有列名Min_Age
替换每个解决方案中的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
因此,基本R ave
在此方案中表现相当不错,但结果可能会因实际数据中的组大小而有所变化。
更新:包含基准中的doBy
和lapply
版本。
答案 1 :(得分:1)
您可以使用summaryBy
包中的doBy
。
> 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
答案 2 :(得分:0)
您的数据是在具有2列的数据框中还是这些单独的字符串?
如果它是一个包含2列的数据框(假设列名称为“name”和“age”),则数据框的变量名称为“crime”:
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
这是一个非常透明和缓慢的版本。我确信它可以用更少的代码和更快的速度完成。对于您的数据帧大小,它应该可以正常工作。