获取多个字符串值R的最小值

时间:2014-07-31 13:50:50

标签: r string

我正在处理青少年犯罪数据库,需要报告发病年龄。我目前的名字和年龄都在冒犯,许多科目都是屡次犯罪,我需要隔离最早的犯罪年龄。我可以逐行(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

3 个答案:

答案 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在此方案中表现相当不错,但结果可能会因实际数据中的组大小而有所变化。

更新:包含基准中的doBylapply版本。

答案 1 :(得分:1)

您可以使用summaryBy包中的doBy

> library(doBY)
> summaryBy(V2~V1, data = dat, FUN = min, full.dimension = TRUE)

splitunsplit

的另一种方式
> 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

这是一个非常透明和缓慢的版本。我确信它可以用更少的代码和更快的速度完成。对于您的数据帧大小,它应该可以正常工作。