【问题标题】:Using CUT and Quartile to generate breaks in R function使用 CUT 和四分位数在 R 函数中生成中断
【发布时间】:2012-07-28 12:42:25
【问题描述】:

some great advice from before 之后,我现在正在编写我的第二个 R 函数并使用类似的逻辑。但是,我正在尝试更多地自动化,并且可能对我自己的利益变得太聪明了。

我想根据订单数量将客户分成五等份。这是我的代码:

# sample data
clientID <- round(runif(200,min=2000, max=3000),0)
orders <- round(runif(200,min=1, max=50),0)

df <- df <- data.frame(cbind(clientID,orders))

#function to break them into quintiles
ApplyQuintiles <- function(x) {
  cut(x, breaks=c(quantile(df$orders, probs = seq(0, 1, by = 0.20))), 
      labels=c("0-20","20-40","40-60","60-80","80-100"))
}

#Add the quintile to the dataframe
df$Quintile <- sapply(df$orders, ApplyQuintiles)

table(df$Quintile)

0-20   20-40   40-60    60-80   80-100 
40     39      44       38      36

您会在这里看到,在我的样本数据中,我创建了 200 个观察值,但通过 table 列出的只有 197 个。剩下的 3 个是NA

现在,有些客户端 ID 的五分位数为“NA”。似乎如果它们处于最低中断,在本例中为 1,那么它们不包含在 cut 函数中。

有没有办法让cut 包含所有观察结果?

【问题讨论】:

  • 是的,您需要将include.lowest=TRUE 指定为cut() 的参数。见?cut

标签: r cut


【解决方案1】:

尝试以下方法:

set.seed(700)

clientID <- round(runif(200,min=2000, max=3000),0)
orders <- round(runif(200,min=1, max=50),0)

df <- df <- data.frame(cbind(clientID,orders))

ApplyQuintiles <- function(x) {
  cut(x, breaks=c(quantile(df$orders, probs = seq(0, 1, by = 0.20))), 
      labels=c("0-20","20-40","40-60","60-80","80-100"), include.lowest=TRUE)
}
df$Quintile <- sapply(df$orders, ApplyQuintiles)
table(df$Quintile)

0-20  20-40  40-60  60-80 80-100 
  40     41     39     40     40 

我在你的 cut 函数中包含了include.lowest=TRUE,这似乎使它起作用。详情请见?cut

【讨论】:

  • 第6行代码应该是x而不是df$orders?
  • 是的,应该是。这是一个改进的版本,允许您指定 splitPct 值:applyQuintiles &lt;- function(cVector, splitPct) { if(1 %% splitPct == 0) { labelTags &lt;- paste0("&lt; ",seq(0, 1, by = splitPct) * 100,"%")[-1] cut(cVector, breaks=c(quantile(cVector, probs = seq(0, 1, by = splitPct))), labels=labelTags, include.lowest=TRUE) } else {message("Your splitPct is not summing to 1 (100%).")} } 好的 stackoverflow 不允许我格式化,但复制粘贴应该可以工作
【解决方案2】:

在古老的 Hmisc 包中还有cut2。它会进行分位数切割。

来自帮助:

函数类似于 cut 但左端点是包容性的并且标签是 形式 [lower, upper),除了最后一个区间是 [lower,upper]。 如果给出了削减,默认情况下会确保削减包括整个 x 的范围。此外,如果未给出削减,则将 x 削减为分位数 组(g 给定)或具有给定最小观察次数的组 (米)。 cut 创建了一个类别对象,而 cut2 创建了一个因子 对象。

【讨论】:

    【解决方案3】:

    您可以很容易地使用OneR package 中的bin 函数中的content 方法自动完成此操作:

    library(OneR)
    set.seed(700)
    
    clientID <- round(runif(200, min = 2000, max = 3000), 0)
    orders <- round(runif(200, min = 1, max = 50), 0)
    df <- data.frame(cbind(clientID, orders))
    
    df$Quintiles <- bin(df$orders, method = "content")
    table(df$Quintile)
    ## 
    ## (0.952,9.8]    (9.8,19]   (19,31.4] (31.4,38.2]   (38.2,49] 
    ##          40          41          39          40          40
    

    (完全披露:我是这个包的作者)

    【讨论】:

      【解决方案4】:

      我对我的数据使用了类似的函数,但我很担心,因为我的五分之一箱有不同数量的观察:可以吗? 谢谢!

      jobs02.vq <- cut(meaneduc02v, breaks=c(quantile(meaneduc02v,  probs = seq(0,        1, by=0.20), 
                                na.rm=TRUE, names=TRUE, include.lowest=TRUE, right = TRUE, 
                                labels=c("1","2","3","4","5")))) # makes quintiles
      

      我得到的输出是:

       table(jobs02.vq, useNA='ifany')
       jobs02.vq
       [1.00,2.00) [2.00,2.51) [2.51,3.34) [3.34,4.45) [4.45,5.33]        <NA> 
           82          54          69          64          67         123 
      

      【讨论】:

        【解决方案5】:

        一个适用于所有数据的简单函数:

            cutD <- function(x,n) {
          cut(x, breaks=c(quantile(x, probs = seq(0, 1, by = 1/n),na.rm = T)), 
              include.lowest=TRUE)
        }
        

        【讨论】:

          【解决方案6】:

          我想要一些可以工作的东西 dplyrgroup_by;我需要切割标签来指定范围。这是我得到的

          Get.breaks <- function(f, cuts, digits = 2)
          {
            x <- round(quantile(f, probs = seq(1/cuts, 1 - 1/cuts, 1/cuts), names = F), digits)
            x <- sort(unique(c(0, x, Inf)))
            rm(f, cuts, digits)
            return(x)
          }
          
          df <- data.frame(cbind(clientID = round(runif(200,min=2000, max=3000),0),
                                 orders = round(runif(200,min=1, max=50),0)))
          
          cut <- df %>%
                  mutate(lower = cut(orders, right = F
                                     , breaks = Get.breaks(orders, cuts = 10, digits = 0)
                                     , labels = head(Get.breaks(orders, cuts = 10, digits = 0), -1)
                                     )
                         , lower = as.numeric(as.character(lower))
                         ) %>% 
                  group_by(lower) %>% 
                  summarise(.groups = "drop", N = n())
          

          【讨论】:

            【解决方案7】:

            来自 Hmisc 的cut2 确实有效(参数 g 定义了分位数组的数量)

            set.seed(700)
            
            clientID <- round(runif(200,min=2000, max=3000),0)
            orders <- round(runif(200,min=1, max=50),0)
            
            df <- data.frame(cbind(clientID,orders))
            
            library(Hmisc)
            df$Quintile <- cut2(df$orders, g =5)
            levels(df$Quintile) <-  c("0-20", "20-40", "40-60", "60-80", "80-100")
            
            table(df$Quintile)
            ##  0-20  20-40  40-60  60-80 80-100 
            ##    40     41     39     40     40 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-05-09
              • 2017-05-29
              • 1970-01-01
              • 2017-04-25
              • 2022-06-14
              • 2019-10-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多