在分位数不是唯一的R中分配分位数

时间:2019-07-13 02:24:10

标签: r quantile

x是数字非负数据(主要是<10)和qx <- quantile(x, probs = pq)的向量,其中length(pq)通常> length(x) * (3/4)。 我需要一个qx的索引向量,称为q_i,其中x[i]属于分位数qx[q_i[i]]

顾名思义,要注意的是qx中可能存在非唯一值,例如如果x为零,则返回多个0值分位数,并可能有其他重复值。我想通过(a)回收这些等效分位数的索引序列,或(b)随机分配等效分位数的索引来处理这些情况。我想我更喜欢选项(a),但是对任何一个解决方案都会有用。

我在想有一种有效的方法-我本质上是使用for循环来完成此操作的,但是我正在寻找矢量化方法。

我开始尝试使用cut(),这当然不允许非唯一中断。我发现this question here有什么帮助,因为我发现了.bincode()函数,该函数确实允许非唯一中断。但是,它没有“分配”索引的规则-它只会使用每个重复的分位数的第一个的索引。

此问题的一些示例代码:

x <- c(5.8,  0.0, 16.1,  5.8,  3.5, 13.8,  6.9,  5.8, 11.5,  9.2, 11.5,
       3.5,  0.0,  8.1,  0.0,  4.6,  5.8,  3.5,  0.0, 10.3,  0.0,  0.0,
       3.5, 6.9, 3.5)
pq <- seq(0, 1, length.out = 20)
qx <- quantile(x, pq)

# quantiles for reference, rounded for readability
round(as.numeric(qx), 2)
[1]  0.00  0.00  0.00  0.00  0.18  3.50  3.50  3.50  3.62  5.04  5.80 5.80  5.97
[14] 6.90  7.72  9.14 10.55 11.50 13.19 16.10

q_i <- .bincode(x, qx, include.lowest = TRUE)
q_i
[1] 10  1 19 10  5 19 13 10 17 16 17  5  1 15  1  9 10  5  1 16  1  1  5 13 5

如果.bincode()很神奇,这就是我想要的结果,我可以说服它做我需要做的事情:

在上述情况(a)下:

q_i
[1] 10 1 19 11 5 19 13 10 17 16 17 6 2 15 3 9 11 7 1 16 2 3 5 13 6

在情况(b)下,它看起来与上面正好一样的可能性很小。或类似的东西:

q_i
[1] 10 1 19 10 6 19 13 11 17 16 17 5 3 15 2 9 11 6 2 16 1 3 5 13 7

谢谢!

2 个答案:

答案 0 :(得分:0)

好的,我有一些代码可以在场景a(回收)下从您的代码继续到最后的q_i。我希望它更漂亮,但还是希望能有所帮助。

注意:
-假设length(x)> length(qx)> length(x)/2
-在代码下面的解释中,q_i指问题的结尾处的值,在发生任何值的回收或替换之前。

## Start off with the code provided in the question...
#  1. For each distinct q_i, calculate the number of occurrances, and how far we can recycle it
df <- data.frame(lower=sort(unique(q_i)), freq=as.integer(table(q_i)))
df$upper <- c(df$lower[-1] - df$lower[-nrow(df)], 1) + df$lower - 1
df$upper <- df$upper - as.numeric(df$upper > df$lower & qx[df$upper] < qx[df$upper + 1])

#  2. Identify when there's a (single) number we can't recycle, and identify which position it's in
#     e.g. is it the third time q_i == 10?
df$special_case <- rep(NA, nrow(df))
df$special_case[df$lower < df$upper] <- sapply(df$lower[df$lower < df$upper], function(low) {
                                        bin <- x[q_i==low]
                                        if(length(unique(bin)) > 1) {
                                          return(match(min(bin), bin))} 
                                        else return(NA)})

# 3. For each row of df, get a vector of (possibly recycled) numbers
recycled <- apply(df, 1, function(x) {
  out <- rep(x["lower"]:x["upper"], length.out=x["freq"])

  # This part modifies the vector created to handle the 'special case'
  if(!is.na(x["special_case"])) {
    out[x["special_case"]] <- x["lower"]
    if(x["special_case"] < x["freq"]) {
      out[(x["special_case"]+1):x["freq"]] <- out[x["special_case"]:(x["freq"]-1)]
    }
  }
  return(out)
})

# 3b. Make this follow the same order as q_i
q_i_final <- unlist(recycled)[order(order(q_i))]

q_i_final
[1] 10  1 19 11  5 19 13 10 17 16 17  6  2 15  3  9 11  7  1 16  2  3  5 13  6

基本概念是什么?
对于q_i的每个值,我们可以很容易地计算出应该回收的数量(如果我们要回收的话)。通常,我们最多可以回收比下一个最大值q_i小一个。然后,我们可以使用rep创建一个回收向量来替换q_i中的内容,例如用10替换四个10 11 10 11

还有什么要考虑的?
这个基本思想假设,对于q_i的每个值,x的对应值可以全部回收,也可以全部不回收。通常是这种情况,但是您也可以使用q_i的值,其中所有小节一个可以回收,即一个k使得x[k] <{qx[q_i[k]+1],但一个或多个j,其中q_i[j] = q_i[k]以及x[j] = qx[q_i[j]+1]

应该识别这种“特殊”情况(尽管问题数据中不存在),并且必须注意不要将此值也回收。


特殊情况下更详细

  1. 我们可以对问题数据进行一些简单的更改以创建这种情况(请参见下面的代码)。请注意,x[5]> x[12],但是q_i[5] = q_i[12] = 4。现在,在上述“基本概念”下,q_i = 4的所有值都将被回收,因此我们将拥有q_i_final[12] = 5。这是一个问题,因为我们希望x[12]qx[q_i_final[12]]qx[q_i_final[12]+1]之间,但事实并非如此,因为它严格都小于两者。事实证明,我们可以回收q_i = 4的所有值,x[12]除外。

新代码:

# Code copied from question, changes as follows:
# x[12] changed from 3.5 to 3.4
# x[13] and x[21] changed from 0.0 to 10.0
x <- c(5.8,  0.0, 16.1,  5.8,  3.5, 13.8,  6.9,  5.8, 11.5,  9.2, 11.5,
       3.4,  10.0,  8.1,  0.0,  4.6,  5.8,  3.5,  0.0, 10.3,  10.0,  0.0,
       3.5, 6.9, 3.5)
pq <- seq(0, 1, length.out = 20)
qx <- quantile(x, pq)
q_i <- .bincode(x, qx, include.lowest = T, right=T)

q_i
[1]  8  1 19  8  4 19 12  8 17 14 17  4 15 13  1  8  8  4  1 16 15  1  4 12  4

答案 1 :(得分:0)

此代码基于@hodgenovice的答案,但未考虑特殊情况。

它还有一个附加条件,可以正确回收第一个重复分位数序列的值。这是我这个问题的错误,我最初从期望的答案中省略了q_i中的4,但它应该是为分配了q_i的数据值回收的索引之一由1的{​​{1}}中获得。

.bincode()