Creating an “other” field

前端 未结 3 653
醉酒成梦
醉酒成梦 2020-12-17 23:27

Right now, I have the following data.frame which was created by original.df %.% group_by(Category) %.% tally() %.% arrange(desc(n)).

DF <- st         


        
相关标签:
3条回答
  • 2020-12-18 00:09

    This is another approach, assuming that each category (of the top 5 at least) only occurs once:

    df %.% 
      arrange(desc(n)) %.%       #you could skip this step since you arranged the input df already according to your question
      mutate(Category = ifelse(1:n() > 5, "Other", Category)) %.%
      group_by(Category) %.%
      summarize(n = sum(n))
    
    #  Category      n
    #1        E 163051
    #2        I  49701
    #3        K 127133
    #4        L  64868
    #5        M 106680
    #6    Other 217022
    

    Edit:

    I just noticed that my output is not order by decreasing n any more. After running the code again, I found out that the order is kept until after the group_by(Category) but when I run the summarize afterwards, the order is gone (or rather, it seems to be ordered by Category). Is that supposed to be like that?

    Here are three more ways:

    m <- 5    #number of top results to show in final table (excl. "Other")
    n <- m+1
    
    #preserves the order (or better: reesatblishes it by index)
    df <- arrange(df, desc(n)) %.%    #this could be skipped if data already ordered 
      mutate(idx = 1:n(), Category = ifelse(idx > m, "Other", Category)) %.%
      group_by(Category) %.%
      summarize(n = sum(n), idx = first(idx)) %.%
      arrange(idx) %.%
      select(-idx)
    
    #doesnt preserve the order (same result as in first dplyr solution, ordered by Category)
    df[order(df$n, decreasing=T),]     #this could be skipped if data already ordered 
    df[n:nrow(df),1] <- "Other"
    df <- aggregate(n ~ Category, data = df, FUN = "sum")
    
    #preserves the order (without extra index)
    df[order(df$n, decreasing=T),]     #this could be skipped if data already ordered 
    df[n:nrow(df),1] <- "Other"
    df[n,2] <- sum(df$n[df$Category == "Other"]) 
    df <- df[1:n,]
    
    0 讨论(0)
  • 2020-12-18 00:17

    Different package/different syntax version:

    library(data.table)
    
    dt = as.data.table(DF)
    
    dt[order(-n), # your data is already sorted, so this does nothing for it
       if (.BY[[1]]) .SD else list("Other", sum(n)),
       by = 1:nrow(dt) <= 5][, !"nrow", with = F]
    #   Category      n
    #1:        E 163051
    #2:        K 127133
    #3:        M 106680
    #4:        L  64868
    #5:        I  49701
    #6:    Other 217022
    
    0 讨论(0)
  • 2020-12-18 00:18

    This function modifies a column, replacing the infrequent entries with Other, either by specifying a minimum frequency, or by specifying the resultant number of categories intended.

    #' @title Group infrequent entries into 'Other category'
    #' @description Useful when you want to constrain the number of unique values in a column.
    #' @param .data Data containing variable.
    #' @param var Variable containing infrequent entries, to be collapsed into "Other". 
    #' @param n Threshold for total number of categories above "Other".
    #' @param count Threshold for total count of observations before "Other".
    #' @param by Extra variables to group by when calculating \code{n} or \code{count}.
    #' @param copy Should \code{.data} be copied? Currently only \code{TRUE} is supported.
    #' @param other.category Value that infrequent entries are to be collapsed into. Defaults to \code{"Other"}.
    #' @return \code{.data} but with \code{var} changed to be grouped into smaller categories.
    #' @export 
    mutate_other <- function(.data, var, n = 5, count, by = NULL, copy = TRUE, other.category = "Other"){
      stopifnot(is.data.table(.data), 
                is.character(other.category), 
                identical(length(other.category), 1L))
    
      had.key <- haskey(.data)
    
      if (!isTRUE(copy)){
        stop("copy must be TRUE")
      }
    
      out <- copy(.data)
    
      if (had.key){
        orig_key <- key(out)
      } else {
        orig_key <- "_order"
        out[, "_order" := 1:.N]
        setkeyv(out, "_order")
      }
    
      if (is.character(.data[[var]])){
        stopifnot(!("nvar" %in% names(.data)),
                  var %in% names(.data))
    
        N <- .rank <- NULL
        n_by_var <-
          out %>%
          .[, .N, keyby = c(var, by)] %>%
          .[, .rank := rank(-N)]
    
        out <- merge(out, n_by_var, by = c(var, by))
    
        if (missing(count)){
          out[, (var) := dplyr::if_else(.rank <= n, out[[var]], other.category)]
        } else {
          out[, (var) := dplyr::if_else(N >= count, out[[var]], other.category)]
        }
        out <- 
          out %>%
          .[, N := NULL] %>%
          .[, .rank := NULL] 
    
        setkeyv(out, orig_key)
    
        if (!had.key){
          out[, (orig_key) := NULL]
          setkey(out, NULL)
        }
        out
    
      } else {
        warning("Attempted to use by = on a non-character vector. Aborting.")
        return(.data)
      }
    }
    

    https://github.com/HughParsonage/hutils/blob/master/R/mutate_other.R

    0 讨论(0)
提交回复
热议问题