【问题标题】:dplyr: summarize data.frame to get the highest and lowest valuesdplyr:汇总 data.frame 以获得最高和最低值
【发布时间】:2017-11-20 00:42:47
【问题描述】:

我有一个 data.frame,我想汇总它以获得每列的最高(5 个值)和最低(5 个值)。我使用了iris 作为可重现的示例。

iris中所有变量的最高5个值可以使用

df_h <-  iris %>% 
  dplyr::select(Species, everything()) %>% 
  tidyr::gather("id", "value", 2:5) %>% 
  dplyr::arrange(Species, id, desc(value)) %>% 
  dplyr::group_by(Species, id ) %>% 
  top_n(n = 5) %>% 
  dplyr::mutate(category = "high")

对于最低的 5 个值,我使用了相同的值,但 top_n(n = -5) 除外。

df_l <-  iris %>% 
  dplyr::select(Species, everything()) %>% 
  tidyr::gather("id", "value", 2:5) %>% 
  dplyr::arrange(Species, id, desc(value)) %>% 
  dplyr::group_by(Species, id ) %>% 
  top_n(n = -5) %>% 
  dplyr::mutate(category = "low")

然后,我将两个 data.frame 连接在一起 df_h(最高 5 个值)和 df_l(最低 5 个值)。

df_fin <-  df_h %>% bind_rows(., df_l)

我正在寻找一种高效/更短的方法来获得相同的结果,而无需创建两个 data.frame 并加入它们。任何建议将不胜感激。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    如果只想提取极值,可以将top_n 的两个应用与filter 中的复合条件结合起来(注意top_n 只是filter 使用min_rank 的快捷方式) :

        library(tidyverse)
    
        iris %>% 
              gather("dims", "value", -Species) %>%
              group_by(Species, dims) %>%
              filter( min_rank(desc(value)) <= 5 | 
                        min_rank(value) <= 5 ) -> df_hi_lo
    

    但是,这不包括高/低分类。

    更灵活的解决方案是使用返回这些类别名称之一或空字符串的函数:

    hilo <- function(x, n) {
      hi_rk <- min_rank(desc(x))  # change rank function as needed
      lo_rk <- min_rank(x)
      paste0(ifelse(hi_rk <= n, "high", ""),
                    ifelse(lo_rk <= n, "low",""))
    

    我在这里使用了min_rank 函数,它复制了top_n 的行为,但您也应该考虑将其替换为dense_rank

    这允许您为所有行添加类别,然后过滤到高/低值:

    iris %>% gather("dims", "value", -Species) %>%
      group_by(Species, dims) %>%
      mutate(category = hilo(value, 5) ) %>%
      filter(category != "") -> df_hl
    

    【讨论】:

      【解决方案2】:

      如果我正确理解您的问题,我认为您可以使用排名函数以编程方式在单个数据框中完成此任务。

      我使用iris 数据集(如下)汇总了一个示例。基本上,它在您最初的起点之外做了三件事:

      1. 创建一个名为rank 的临时变量,它计算并确定值在其值列中的位置。我使用来自 dplyr 的 dense_rank 作为概念证明,但您可能需要不同的排名函数,具体取决于您的目的。

      2. 计算high_category_lower_bound,这是该组中排名第五的值。它还设置了low_category_upper_bound,这很适合以编程方式控制它,但在此示例中硬编码为 5。

      3. 过滤数据框以仅包含小于或等于low_category_upper_bound 或大于或等于high_category_lower_bound 的值,然后执行ifelse 查找以创建类别。

      注意事项:

      • 我还在每个列/值对上强制使用 distinct。我这样做是为了使解决方案更简单。如果您想要非不同的值,您可能需要调整排名函数。密集的等级赋予它们相同的等级,因此它们会被多次返回而不会首先区分。

      • 我将 2:5 替换为 1:ncol(.),但实际使用的数据集可能会有所不同。

      • 如果您关心记录包含在数据集中的原因,您可以保留我创建的一些临时列,然后从最终结果中删除。

      • 根据您实际使用的数据集的规模,此解决方案可能比您想要的效果或多或少。该解决方案的缺点是它必须对所有值进行排名,这在大规模数据集上可能会很昂贵。我个人很喜欢使用该解决方案并查看它是否可以满足您的需求,但这是需要注意的事情。在iris 数据集上,它返回得非常快。在几百万行上,可能需要更长的时间。


      library(dplyr)
      library(tidyr)
      
      df_all <- 
          iris %>%
          # gather all columns
          gather("column", "value", 1:ncol(.)) %>% 
          # filter to only values which can be evaluated as high/low;
          # you could expand this to include factor variables, but 
          # that's beyond the scope of this question and you'd have to
          # redefine the factor levels before this step
          filter(!is.na(as.numeric(value))) %>%
          # get distinct values - optional but probably helpful
          distinct(column, value) %>%
          # group by and perform set operations on 
          group_by(column) %>%
          # create ranking sequence
          mutate(
              rank = dense_rank(value),
              low_category_upper_bound = 5,
              high_rank = max(rank),
              high_category_lower_bound = high_rank - 4 
          ) %>%
          # retain only top and bottom values
          # filter and create category label
          filter(
              rank <= low_category_upper_bound | 
              rank >= high_category_lower_bound
          ) %>%
          mutate(
              category = ifelse(rank >= high_category_lower_bound, "high", ""),
              category = ifelse(rank <= low_category_upper_bound, "low", category)
          ) %>%
          # select columns of interest
          select(column, value, category)
      

      【讨论】:

      • 非常感谢您的宝贵时间和帮助
      猜你喜欢
      • 2021-02-17
      • 2020-12-31
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 2013-12-05
      • 1970-01-01
      • 2019-07-17
      • 2014-03-06
      相关资源
      最近更新 更多