【问题标题】:Function for median similar to "which.max" and "which.min" / Extracting median rows from a data.frame类似于“which.max”和“which.min”的中值函数/从data.frame中提取中值行
【发布时间】:2012-05-02 15:29:33
【问题描述】:

我偶尔需要根据其中一个变量的值从 data.frame 中提取特定行。 R 具有最大值 (which.max()) 和最小值 (which.min()) 的内置函数,可让我轻松提取这些行。

中位数是否有等价物?还是我最好只写自己的函数?

这是一个示例 data.frame 以及我将如何使用 which.max()which.min()

set.seed(1) # so you can reproduce this example
dat = data.frame(V1 = 1:10, V2 = rnorm(10), V3 = rnorm(10), 
                 V4 = sample(1:20, 10, replace=T))

# To return the first row, which contains the max value in V4
dat[which.max(dat$V4), ]
# To return the seventh row, which contains the min value in V4
dat[which.min(dat$V4), ]

对于这个特定示例,由于存在偶数个观察值,我需要返回两行,在本例中为第 2 行和第 10 行。

更新

这似乎没有内置函数。因此,以reply from Sacha 为起点,我编写了这个函数:

which.median = function(x) {
  if (length(x) %% 2 != 0) {
    which(x == median(x))
  } else if (length(x) %% 2 == 0) {
    a = sort(x)[c(length(x)/2, length(x)/2+1)]
    c(which(x == a[1]), which(x == a[2]))
  }
}

我可以按如下方式使用它:

# make one data.frame with an odd number of rows
dat2 = dat[-10, ]
# Median rows from 'dat' (even number of rows) and 'dat2' (odd number of rows)
dat[which.median(dat$V4), ]
dat2[which.median(dat2$V4), ]

有什么改进的建议吗?

【问题讨论】:

    标签: r dataframe subset


    【解决方案1】:

    我们只需要一个通过近似匹配返回值位置的函数:

    match.approx <- function(x, y) {
        ## Purpose: Match Approximately for Numerical Data
        ## Arguments:
        ##   "x":  a vector of numeric values.
        ##   "y":  a vector of numeric values. 
        ## RETURN:
        ##   The index in "y" that indicates the closest y value to each of "x" value. 
        ## ________________________________________________
        
        sapply(x, function(x0) which.min(abs(x0 - y)))
    }
    if (F) {
      match.approx(c(4.2, 1.2, 15), 1:10)                #  4  1 10
    }
    

    这是一个查找分位数位置的示例:

    set.seed(1)
    a <- rnorm(100)
    match.approx(quantile(a), a)
    # 0%  25%  50%  75% 100% 
    # 14   29   23   63   61
    

    【讨论】:

      【解决方案2】:

      在 Sacha 和 cbeleites 给出的答案的基础上,这是一个获取包容性分位数索引的函数。与先前答案的一个区别是 type 参数已公开,并将产生略有不同的分位数结果(请参阅 ?quantile)。如果性能是一个问题,可以将 sapply 替换为 parallel 包中的版本 - 类似于 unlist(mclapply(...))

      # Extract indices corresponding to inclusive quantiles
      # EXAMPLE:
      #
      #   x <- c(2.34, 5.83, NA, 9.34, 8.53, 6.42, NA, 8.07, NA, 0.77)
      #   probs <- c(0, .23, .5, .6, 1)
      #   which.quantile(x, probs, na.rm = TRUE)
      #
      # OUTPUT: 10  1  6  8  4
      #
      #   x[ which.quantile(x, probs, na.rm = TRUE) ]
      #
      # OUTPUT: 0.77 2.34 6.42 8.07 9.34
      #
      #   x <- c(2, 1, 3)
      #   p <- c(0.5)
      #   x[ which.quantile(x, p) ]
      #
      # OUTPUT: 2
      which.quantile <- function (x,
                                  probs,
                                  na.rm = FALSE,
                                  type = 7) {
        stopifnot(all(probs >= 0.0))
        stopifnot(all(probs <= 1.0))
        quants = quantile(x,
                          probs = probs,
                          na.rm = na.rm,
                          type = type)
        which.nearest <- function(quant) {
          return(which.min(abs(x - quant)))
        }
        return(sapply(X = quants, FUN = which.nearest))
      }
      

      【讨论】:

        【解决方案3】:

        假设您要从中获取中位数的向量是x

        如果length(x)=2*n+1,函数which.min(x[x&gt;=median(x)]) 将给出中位数,如果length(x)=2*n,则给出两个中间值中较大的一个。如果您想获得两个中间值中较小的一个,您可以稍微调整一下。

        【讨论】:

        • 这类似于@Sacha Epskamp 的想法。只是在这里我通过 TRUE/FALSE 处理索引
        【解决方案4】:

        虽然 Sacha 的解决方案非常笼统,但中位数(或其他分位数)是顺序统计数据,因此您可以从 order (x)(而不是 sort (x) 的分位数值)计算相应的索引。

        查看quantile,可以使用类型 1 或 3,所有其他类型在某些情况下会导致两个值的(加权)平均值。

        我选择了类型 3,然后从 quantile 复制和粘贴一点导致:

        which.quantile <- function (x, probs, na.rm = FALSE){
          if (! na.rm & any (is.na (x)))
          return (rep (NA_integer_, length (probs)))
        
          o <- order (x)
          n <- sum (! is.na (x))
          o <- o [seq_len (n)]
        
          nppm <- n * probs - 0.5
          j <- floor(nppm)
          h <- ifelse((nppm == j) & ((j%%2L) == 0L), 0, 1)
          j <- j + h
        
          j [j == 0] <- 1
          o[j]
        }
        

        一个小测试:

        > x <-c (2.34, 5.83, NA, 9.34, 8.53, 6.42, NA, 8.07, NA, 0.77)
        > probs <- c (0, .23, .5, .6, 1)
        > which.quantile (x, probs, na.rm = TRUE)
        [1] 10  1  6  6  4
        > x [which.quantile (x, probs, na.rm = TRUE)] == quantile (x, probs, na.rm = TRUE, type = 3)
        
          0%  23%  50%  60% 100% 
        TRUE TRUE TRUE TRUE TRUE 
        

        这是你的例子:

        > dat [which.quantile (dat$V4, c (0, .5, 1)),]
          V1         V2          V3 V4
        7  7  0.4874291 -0.01619026  1
        2  2  0.1836433  0.38984324 13
        1  1 -0.6264538  1.51178117 17
        

        【讨论】:

          【解决方案5】:

          我写了一个更全面的函数来满足我的需要:

          row.extractor = function(data, extract.by, what) {
          # data = your data.frame
          # extract.by = the variable that you are extracting by, either
          #              as its index number or by name
          # what = either "min", "max", "median", or "all", with quotes
            if (is.numeric(extract.by) == 1) {
              extract.by = extract.by
            } else if (is.numeric(extract.by) != 0) {
              extract.by = which(colnames(dat) %in% "extract.by")
            } 
            which.median = function(data, extract.by) {
              a = data[, extract.by]
              if (length(a) %% 2 != 0) {
                which(a == median(a))
              } else if (length(a) %% 2 == 0) {
                b = sort(a)[c(length(a)/2, length(a)/2+1)]
                c(max(which(a == b[1])), min(which(a == b[2])))
              }
            }
            X1 = data[which(data[extract.by] == min(data[extract.by])), ] 
            X2 = data[which(data[extract.by] == max(data[extract.by])), ]
            X3 = data[which.median(data, extract.by), ]
            if (what == "min") {
              X1
            } else if (what == "max") {
              X2
            } else if (what == "median") {
              X3
            } else if (what == "all") {
              rbind(X1, X3, X2)
            }
          }
          

          一些示例用法:

          > row.extractor(dat, "V4", "max")
            V1         V2       V3 V4
          1  1 -0.6264538 1.511781 17
          > row.extractor(dat, 4, "min")
            V1        V2          V3 V4
          7  7 0.4874291 -0.01619026  1
          > row.extractor(dat, "V4", "all")
             V1         V2          V3 V4
          7   7  0.4874291 -0.01619026  1
          2   2  0.1836433  0.38984324 13
          10 10 -0.3053884  0.59390132 14
          4   1 -0.6264538  1.51178117 17
          

          【讨论】:

            【解决方案6】:

            我认为只是:

            which(dat$V4 == median(dat$V4))
            

            但是要小心,因为如果没有一个中间数字,中位数取两个数字的平均值。例如。 median(1:4) 给出的 2.5 与任何元素都不匹配。

            编辑

            这是一个函数,它将为您提供中值的元素或第一个匹配中值的平均值,类似于 which.min() 为您提供的第一个元素仅等于最小值:

            whichmedian <- function(x) which.min(abs(x - median(x)))
            

            例如:

            > whichmedian(1:4)
            [1] 2
            

            【讨论】:

              猜你喜欢
              • 2012-01-18
              • 1970-01-01
              • 2015-02-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-10-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多