【问题标题】:How to find max and min within sequence of values in a column in R?如何在R中的列中的值序列中找到最大值和最小值?
【发布时间】:2016-06-16 20:02:02
【问题描述】:

这个问题可能是微不足道的,但我发现很难解决。请指导我。

数据

以下是样本数据:

structure(list(Vehicle.ID2 = c("39-25", "39-25", "39-25", "39-25", 
"39-25", "39-25", "39-25", "39-25", "39-25", "39-25", "39-25", 
"39-25", "39-25", "39-25", "39-25", "39-25", "39-25", "39-25", 
"39-25", "39-25", "39-25", "39-25", "39-25", "39-25", "39-25", 
"39-25", "39-25", "39-25", "39-25", "39-25", "39-25", "39-25", 
"39-25", "39-25", "39-25", "39-25", "39-25", "39-25", "39-25"
), OC_DV = c(".", ".", ".", ".", ".", "CLDV", ".", ".", ".", 
".", ".", ".", ".", ".", ".", "OPDV", ".", ".", ".", ".", ".", 
".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", 
".", "CLDV", ".", ".", "."), frspacing = c(35.83373, 35.75742, 
35.70391, 35.67694, 35.67792, 35.70669, 35.7619, 35.84096, 35.93962, 
36.05109, 36.16704, 36.28056, 36.3861, 36.47762, 36.5485, 36.59359, 
36.61402, 36.61791, 36.61383, 36.60651, 36.59694, 36.58372, 36.56525, 
36.54044, 36.50771, 36.46458, 36.40831, 36.33713, 36.25086, 36.15089, 
36.04004, 35.92236, 35.80322, 35.68935, 35.58883, 35.51032, 35.4618, 
35.4492, 35.47479)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-39L), .Names = c("Vehicle.ID2", "OC_DV", "frspacing"))  

我想做什么

我想在 frspacingOC_DV 中的标签 CLDVOPDV 之间找到一组值的最大值和最小值。那我想找出他们的不同。

期望的输出

以下是最大值和最小值:

  Group      Max    Min
1 CLDV-OPDV 36.54   35.70
2 OPDV-CLDV 36.62   35.59  

以下是绝对差异(第一组的最大值 - 第二组的最小值,反之亦然):

1 0.95
2 0.92

我没有任何代码来显示我的尝试,因为老实说我不知道​​如何解决这个问题。显然简单的maxmin 按列是行不通的。我正在使用dplyr,但没有找到任何相关信息。

【问题讨论】:

  • 您能否澄清您问题的第二部分?例如,如果您的结果 df 中有两个以上的组,并且您想获得第 1 行的差异,您是在寻找每隔一行的最小值,还是只找到第二行的最小值?

标签: r


【解决方案1】:
 library(zoo) # for na.locf
 library(dplyr)

 df[df=="."] = NA
 df$group = paste((na.locf(df$OC_DV, na.rm = FALSE)), lead(na.locf(df$OC_DV, na.rm = FALSE, fromLast = TRUE)), sep = "-")

 df %>% group_by(group) %>% 
   summarise(Max = max(frspacing), Min = min(frspacing)) %>% 
   filter(!grepl("NA",group ))

Source: local data frame [2 x 3]

      group      Max      Min
      (chr)    (dbl)    (dbl)
1 CLDV-OPDV 36.54850 35.70669
2 OPDV-CLDV 36.61791 35.58883

如果有多个值,我会计算变化并将其用作另一个分组变量: (我复制了这个例子中的数据)

df$group2 = NA
df$group2[which(df$group != lag(df$group))] = 1:length(which(df$group != lag(df$group)))
df$group2 = na.locf(df$group2, na.rm = FALSE)

df %>% group_by(group, group2) %>% 
  summarise(Max = max(frspacing), Min = min(frspacing)) %>% 
   filter(!grepl("NA",group ))

Source: local data frame [5 x 4]
Groups: group [3]

      group group2      Max      Min
      (chr)  (int)    (dbl)    (dbl)
1 CLDV-CLDV      3 38.09082 34.30454
2 CLDV-OPDV      1 36.54850 35.70669
3 CLDV-OPDV      4 38.90356 34.08951
4 OPDV-CLDV      2 36.61791 35.58883
5 OPDV-CLDV      5 38.18983 34.27874

但如果OC_DV 的组合在每个Vehicle.ID2 中都是不同的,您可以简单地将ID 粘贴到组中...

【讨论】:

  • 谢谢!我对完整数据进行了尝试,效果很好。只有一件事,如果有多个CLDV-OPDVOPDV-CLDV 组,我怎样才能找到每个maxmin?我尝试在您的paste 命令中添加frspacing,但随后每一行都是一个单独的组。
  • 如果值不同,您可以粘贴 Vehicle.ID2 而不是 frspacing。如果不是这样,请参阅我的更新。
  • @Cabana,我无法复制您的底部示例,您能否提供您如何制作 df 的代码?您说您复制了示例中的数据,但是,max(df$frspacing = 36.62min(df$frspacing=35.4492 而您返回 38.934.27,这两者都超出了原始 df 的范围。谢谢,很好的答案! +1
  • @Cabana,我猜你做了类似df <- rbind(df, df) 然后df$frspacing[40:78] <- runif(39, 34, 39)
  • @Cabana,非常感谢!
【解决方案2】:
d <- your_dput
# Build your subsetted dataframes
e <- d[grep("CLDV", d$OC_DV)[1]: grep("OPDV", d$OC_DV),]
f <- d[(grep("OPDV", d$OC_DV): grep("CLDV", d$OC_DV)[2]),]
# Make the diff() calls
diff(c(max(e$frspacing), min(f$frspacing)))
diff(c(max(f$frspacing), min(e$frspacing)))

我的值和你的不一样,你可以根据你想如何处理边界包含/排除手动调整 grep 值。

【讨论】:

  • 感谢您的回答。如果我事先不知道OC_DV 中有多少OPDVs 和CLDVs 怎么办?
  • 只是猜测,但那些“打开”和“关闭”指标?如果是这样,它们总是配对吗?
  • 是的,它们是“跟车”过程中的“打开”和“关闭”指标。有时它们是配对的,有时不是。 CLDV 可以出现在OPDV 之前或之后。对于每个唯一的Vehicle.ID2,它们的显示方式不同。我只复制了一小部分数据。
  • 我比我的想法更喜欢@Cabana 的 zoo::na.locf() 解决方案
【解决方案3】:

下面是一个基本的 R 解决方案:

MaxMinSeq <- function(df) {
    myInd <- which(df$OC_DV != ".")
    myVals <- df$frspacing
    myTitles <- df$OC_DV[myInd]
    myLen <- length(myInd)-1L
    NewDf <- as.data.frame(t(sapply(1:myLen, function(x) {
               list(Group = paste(c(myTitles[x],"-",myTitles[x+1L]), collapse = ""),
                   Max = max(myVals[myInd[x]:(myInd[x+1L]-1L)]),
                   Min = min(myVals[myInd[x]:(myInd[x+1L]-1L)]))})))
    for (i in 1:3) {NewDf[,i] <- unlist(NewDf[,i])}
    NewDf
}

df2 <- MaxMinSeq(df)
df2
      Group      Max      Min
1 CLDV-OPDV 36.54850 35.70669
2 OPDV-CLDV 36.61791 35.58883

这比上面发布的dplyr 解决方案要快一点。观察:

TestDplyr <- function(df) {
    df[df=="."] <- NA
    df$group <- paste((na.locf(df$OC_DV, na.rm = FALSE)), lead(na.locf(df$OC_DV, na.rm = FALSE, fromLast = TRUE)), sep = "-")

    df$group2 <- NA
    df$group2[which(df$group != lag(df$group))] <- 1:length(which(df$group != lag(df$group)))
    df$group2 <- na.locf(df$group2, na.rm = FALSE)

    df %>% group_by(group, group2) %>% 
        summarise(Max = max(frspacing), Min = min(frspacing)) %>% 
        filter(!grepl("NA",group ))
}

microbenchmark(Joseph = MaxMinSeq(df), Cabana = TestDplyr(df))
Unit: microseconds
expr      min        lq      mean    median       uq      max neval
Joseph  338.671  377.6695  405.0257  405.9945  429.188  496.718   100
Cabana 2622.336 2698.2810 2890.5430 2765.6045 2977.427 7772.180   100

这是一个非常大的例子:

myDfs <- lapply(1:10000, function(x) df)
bigDf <- do.call(rbind, myDfs)
bigDf$frspacing[40:nrow(bigDf)] <- runif((nrow(bigDf)-39), 10, 100)

a <- MaxMinSeq(bigDf)
b <- TestDplyr(bigDf)
b <- b[order(b$group2),]

identical(a$Max, b$Max)
[1] TRUE
identical(a$Min, b$Min)
[1] TRUE

system.time(TestDplyr(bigDf))
 user  system elapsed 
 1.54    0.00    1.54 
system.time(MaxMinSeq(bigDf))
 user  system elapsed 
  0.3     0.0     0.3

至于问题的第二部分,我不确定 OP 希望得到的答案有多笼统,尤其是当有两个以上不同的最终配对时。例如,OP 是否想要找到一行的最大值并将其与所有行的最小值进行比较,还是我们只是比较邻居?下面的函数采用第一种方法(即通用方法)。

GetDiff <- function(df) {
    df2 <- cbind(df, t(sapply(1:nrow(df), function(x) {
                        c(rowMin = min(df[x,2:3]),
                          rowMax = max(df[x,2:3]))})))
    myRows <- 1:nrow(df)
    sapply(myRows, function(x) df2$rowMax[x] - min(df2$rowMin[-x]))
}

GetDiff(df2)   ## df2 comes from above
[1] 0.95967 0.91122

【讨论】:

    猜你喜欢
    • 2023-03-08
    • 2017-01-10
    • 2020-12-02
    • 1970-01-01
    • 2015-06-29
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多