中位数的函数类似于“which.max”和“which.min”/从data.frame中提取中值行

时间:2012-04-21 05:30:44

标签: r dataframe subset

我偶尔需要根据其中一个变量的值从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), ]

有什么建议可以改善这个吗?

6 个答案:

答案 0 :(得分:15)

虽然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

答案 1 :(得分:9)

我想:

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

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

修改

这是一个函数,它将为您提供中位数的元素或与中位数均值的第一个匹配项,类似于which.min()给出的第一个元素仅等于最小值:< / p>

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

例如:

> whichmedian(1:4)
[1] 2

答案 2 :(得分:2)

我写了一个更全面的功能来满足我的需求:

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

答案 3 :(得分:2)

假设您想要获得中位数的向量是x

如果which.min(x[x>=median(x)]),函数length(x)=2*n+1将给出length(x)=2*n的中位数或两个中间值中的较大者。如果你想得到两个中间值中较小的一个,你可以略微调整一下。

答案 4 :(得分:1)

基于Sacha和cbeleites给出的答案,这是一个获取包容的分位数索引的函数。与先前答案的不同之处在于,type参数是公开的,将产生稍有不同的分位数结果(请参见?quantile)。如果性能是一个问题,则可以用parallel包中的某个版本替换sapply-类似于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))
}

答案 5 :(得分:0)

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

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