在R中解释quantile()函数

81

我整天都被R分位数函数困惑着。

我对分位数的工作原理有直观的概念,并且拥有统计学硕士学位,但是文档对我来说令人困惑。

从文档中可以看到:

Q[i](p) = (1 - gamma) x[j] + gamma x[j+1],

到目前为止我还能跟上。对于类型i的分位数,它是基于一些神秘常量gamma在x[j]和x [j+1]之间的插值。

其中1 <= i <= 9,(j-m)/n <= p < (j-m+1)/ n,x[j]是第j个顺序 统计量,n是样本大小,m 是由样本分位数类型决定的常数。这里的gamma取决于g = np+m-j的小数部分。

那么如何计算j?m呢?

对于连续样本分位数类型(4至9),可以通过kth order statistic和p(k)之间的线性插值来获得样本分位数:

p(k) = (k - alpha) / (n - alpha - beta + 1), 其中α和β是由类型确定的常数。此外,m = alpha + p(1 - alpha - beta),gamma = g。

现在我真的迷失了。之前是一个常量的p,现在显然是一个函数。

对于第7类型分位数,默认情况下...

第7类型

p(k) = (k - 1) / (n - 1)。在这种情况下,p(k) = mode[F(x[k])]。S使用此方法。

有人能帮我吗?特别是我对p既是函数又是常量的符号表示感到困惑,不知道m是什么,以及如何计算某个特定p的j。

我希望根据这里的答案,我们可以提交一些修改后的文档,更好地解释这里正在发生的事情。

quantile.R源代码 或者输入:quantile.default

3个回答

68
您可能感到困惑。那份文档太糟糕了。我不得不回到它所基于的论文(Hyndman,R.J .; Fan,Y。(1996年11月)。"Sample Quantiles in Statistical Packages"。《美国统计学家》50(4):361-365.doi:10.2307/2684934)才能理解。让我们从第一个问题开始。
其中1 <= i <= 9,(j-m)/ n <= p <(j-m + 1)/ n,x [j]是第j个顺序统计量,n是样本大小,m是由样本分位数类型确定的常数。这里gamma取决于g = np + m-j的小数部分。
第一部分来自论文,但文档编写人省略了。这意味着仅取决于最接近成为排序观察值中< p >分数的两个顺序统计量。 (对于像我这样不熟悉术语的人,一系列观察结果的“顺序统计量”是排序后的系列。)
此外,最后一句话是错误的。它应该是:
这里gamma取决于np + m的小数部分,g = np + m-j。

对于m来说很简单。 m 取决于选择的9个算法中的哪一个。所以就像Q[i]是分位函数一样,m 应该被视为m[i]。对于算法1和2,m等于0,对于算法3,m等于-1/2,对于其他算法,在下一部分中介绍。

对于连续样本分位数类型(4到9),可以通过k次顺序统计量和p(k)之间的线性插值来获得样本分位数:

p(k) = (k - alpha) / (n - alpha - beta + 1),其中α和β是由类型确定的常量。此外,m = alpha + p(1 - alpha - beta),gamma = g。

这真的很令人困惑。文档中所谓的p(k)与之前的p不同。p(k)绘图位置。在论文中,作者将其写成pk,这很有帮助。特别是因为在m的表达式中,p是原始的p,而m = alpha + p * (1 - alpha - beta)。从概念上讲,对于算法4-9,点(pkx[k])被插值以获得解决方案(pQ[i](p))。每个算法仅在pk的算法上有所不同。
至于最后一部分,R只是陈述了S使用的内容。
原始论文提供了关于样本分位数函数的6个“理想属性列表”,并声明偏好#8,它满足所有除1外的属性。#5满足所有属性,但他们不喜欢它的其他方面(它更多地是现象学而不是基于原则的)。#2是非统计极客(比如我自己)认为的分位数,并且是维基百科中描述的内容。

顺便提一句,在回答dreeves answer时,Mathematica的处理方式明显不同。我认为我理解了这个映射。虽然Mathematica的方法更容易理解,但(a)使用无意义的参数容易导致自己出错,(b)它无法执行R的算法#2。(这里是Mathworld's Quantile page,它说明了Mathematica无法执行#2,但在四个参数的通用化方面给出了其他所有算法的简单推广。)


41
我在2004年8月编写了quantile()函数和相关的帮助文件,并提交给R核心团队(替换了以前的版本)。我刚刚检查过,所有这些错误都是由于我的帮助文件在提交后被更改造成的。(尽管我对使用p和p[k]负责。)我之前并没有注意到这一点,因为我认为我的文件会被保持不变。我将尝试看看能否将帮助文件修复为R 2.10.0版本。 - Rob Hyndman
3
@AFoglia,我已经在http://www.robjhyndman.com/quantile.html上提供了一份新的帮助文件草案。在提交给Rcore之前,请给予评论。 - Rob Hyndman
3
新的版本更好了。我有一个小建议,即对第一到第三种方法添加伽马的定义,尽管这对于具备统计学知识的R用户可能并不必要。除此之外,看起来非常棒。 - AFoglia
10
为了完成这个讨论,新的帮助文件现在是Rv2.10.0基础部分的一部分。 - Rob Hyndman
8
每次我查看 R 中的“help(quantile)”时,我都对它的表现感到满意! - Gregg Lind
显示剩余2条评论

6
当您输入一个向量并且没有已知的CDF时,有多种计算分位数的方法。
考虑当您的观测值不完全落在分位数上时该怎么办。
“类型”只是确定如何做到这一点。因此,这些方法说:“在第k个顺序统计量和p(k)之间使用线性插值”。
那么,什么是p(k)? 一个人说:“好吧,我喜欢使用k/n”。另一个人说:“我喜欢使用(k-1)/(n-1)”等。每种方法都具有适用于一个问题或另一个问题的不同属性。
α和β只是参数化函数p的方式。在某些情况下,它们是1和1。在另一种情况下,它们是3/8和-1/4。我认为文档中的p从未是常数。它们只是没有始终明确地显示依赖关系。
看看在向量1:5和1:6中放入不同类型会发生什么。
(还请注意,即使您的观测值恰好落在分位数上,某些类型仍将使用线性插值)。

2

我认为在@RobHyndman的评论中提到的修订后,R帮助文档已经很清晰了,但我觉得有些令人不知所措。如果这篇答案能帮助某些人快速了解选项及其假设,我会很高兴。

为了理解quantile(x, probs=probs),我想查看源代码。但在R中,这比我预期的要棘手,所以我实际上只是从一个看起来足够新的github repo中获取了它。我对默认(类型7)行为感兴趣,因此我注释了一些内容,但没有为每个选项都这样做。

你可以看到“类型7”方法如何逐步插值,代码中也可以看到,我还添加了几行代码以打印一些重要值。

quantile.default <-function(x, probs = seq(0, 1, 0.25), na.rm = FALSE, names = TRUE
         , type = 7, ...){
    if(is.factor(x)) { #worry about non-numeric data
        if(!is.ordered(x) || ! type %in% c(1L, 3L))
            stop("factors are not allowed")
        lx <- levels(x)
    } else lx <- NULL
    if (na.rm){
        x <- x[!is.na(x)]
    } else if (anyNA(x)){
        stop("missing values and NaN's not allowed if 'na.rm' is FALSE")
        }
    eps <- 100*.Machine$double.eps #this is to deal with rounding things sensibly
    if (any((p.ok <- !is.na(probs)) & (probs < -eps | probs > 1+eps)))
        stop("'probs' outside [0,1]")

    #####################################
    # here is where terms really used in default type==7 situation get defined

    n <- length(x) #how many observations are in sample?

    if(na.p <- any(!p.ok)) { # set aside NA & NaN
        o.pr <- probs
        probs <- probs[p.ok]
        probs <- pmax(0, pmin(1, probs)) # allow for slight overshoot
    }

    np <- length(probs) #how many quantiles are you computing?

    if (n > 0 && np > 0) { #have positive observations and # quantiles to compute
        if(type == 7) { # be completely back-compatible

            index <- 1 + (n - 1) * probs #this gives the order statistic of the quantiles
            lo <- floor(index)  #this is the observed order statistic just below each quantile
            hi <- ceiling(index) #above
            x <- sort(x, partial = unique(c(lo, hi))) #the partial thing is to reduce time to sort, 
            #and it only guarantees that sorting is "right" at these order statistics, important for large vectors 
            #ties are not broken and tied elements just stay in their original order
            qs <- x[lo] #the values associated with the "floor" order statistics
            i <- which(index > lo) #which of the order statistics for the quantiles do not land on an order statistic for an observed value

            #this is the difference between the order statistic and the available ranks, i think
            h <- (index - lo)[i] # > 0  by construction 
            ##      qs[i] <- qs[i] + .minus(x[hi[i]], x[lo[i]]) * (index[i] - lo[i])
            ##      qs[i] <- ifelse(h == 0, qs[i], (1 - h) * qs[i] + h * x[hi[i]])
            qs[i] <- (1 - h) * qs[i] + h * x[hi[i]] # This is the interpolation step: assemble the estimated quantile by removing h*low and adding back in h*high. 
            # h is the arithmetic difference between the desired order statistic amd the available ranks
            #interpolation only occurs if the desired order statistic is not observed, e.g. .5 quantile is the actual observed median if n is odd. 
            # This means having a more extreme 99th observation doesn't matter when computing the .75 quantile


            ###################################
            # print all of these things

            cat("floor pos=", c(lo))
            cat("\nceiling pos=", c(hi))
            cat("\nfloor values= ", c(x[lo]))
            cat( "\nwhich floors not targets? ", c(i))
            cat("\ninterpolate between ", c(x[lo[i]]), ";", c(x[hi[i]]))
            cat( "\nadjustment values= ", c(h))
            cat("\nquantile estimates:")

    }else if (type <= 3){## Types 1, 2 and 3 are discontinuous sample qs.
                nppm <- if (type == 3){ n * probs - .5 # n * probs + m; m = -0.5
                } else {n * probs} # m = 0

                j <- floor(nppm)
                h <- switch(type,
                            (nppm > j),     # type 1
                            ((nppm > j) + 1)/2, # type 2
                            (nppm != j) | ((j %% 2L) == 1L)) # type 3

                } else{
                ## Types 4 through 9 are continuous sample qs.
                switch(type - 3,
                       {a <- 0; b <- 1},    # type 4
                       a <- b <- 0.5,   # type 5
                       a <- b <- 0,     # type 6
                       a <- b <- 1,     # type 7 (unused here)
                       a <- b <- 1 / 3, # type 8
                       a <- b <- 3 / 8) # type 9
                ## need to watch for rounding errors here
                fuzz <- 4 * .Machine$double.eps
                nppm <- a + probs * (n + 1 - a - b) # n*probs + m
                j <- floor(nppm + fuzz) # m = a + probs*(1 - a - b)
                h <- nppm - j

                if(any(sml <- abs(h) < fuzz)) h[sml] <- 0

            x <- sort(x, partial =
                          unique(c(1, j[j>0L & j<=n], (j+1)[j>0L & j<n], n))
            )
            x <- c(x[1L], x[1L], x, x[n], x[n])
            ## h can be zero or one (types 1 to 3), and infinities matter
            ####        qs <- (1 - h) * x[j + 2] + h * x[j + 3]
            ## also h*x might be invalid ... e.g. Dates and ordered factors
            qs <- x[j+2L]
            qs[h == 1] <- x[j+3L][h == 1]
            other <- (0 < h) & (h < 1)
            if(any(other)) qs[other] <- ((1-h)*x[j+2L] + h*x[j+3L])[other]

            } 
    } else {
        qs <- rep(NA_real_, np)}

    if(is.character(lx)){
        qs <- factor(qs, levels = seq_along(lx), labels = lx, ordered = TRUE)}
    if(names && np > 0L) {
        names(qs) <- format_perc(probs)
    }
    if(na.p) { # do this more elegantly (?!)
        o.pr[p.ok] <- qs
        names(o.pr) <- rep("", length(o.pr)) # suppress <NA> names
        names(o.pr)[p.ok] <- names(qs)
        o.pr
    } else qs
}

####################

# fake data
x<-c(1,2,2,2,3,3,3,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,7,99)
y<-c(1,2,2,2,3,3,3,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,7,9)
z<-c(1,2,2,2,3,3,3,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,7)

#quantiles "of interest"
probs<-c(0.5, 0.75, 0.95, 0.975)

# a tiny bit of illustrative behavior
quantile.default(x,probs=probs, names=F)
quantile.default(y,probs=probs, names=F) #only difference is .975 quantile since that is driven by highest 2 observations
quantile.default(z,probs=probs, names=F) # This shifts everything b/c now none of the quantiles fall on an observation (and of course the distribution changed...)... but 
#.75 quantile is stil 5.0 b/c the observations just above and below the order statistic for that quantile are still 5. However, it got there for a different reason.

#how does rescaling affect quantile estimates?
sqrt(quantile.default(x^2, probs=probs, names=F))
exp(quantile.default(log(x), probs=probs, names=F))

quantile.default()源代码的详细解释非常有用,非常感谢。 - Lampard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接