【问题标题】:R - Find lowest/highest level in a hierarchy, within group_byR - 在 group_by 中查找层次结构中的最低/最高级别
【发布时间】:2020-07-13 22:40:50
【问题描述】:

假设我们有一张食品表:

product_id <- c(1, 1, 2, 2, 3, 3)
name <- c("Cheddar", "Cheddar", "Apple", "Apple", "Pizza", "Pizza")
category <- c("Dairy", "Cheese", "Food", "Fruit", "Food", NA)
products <- data.frame(product_id, name, category)

在不规则层次结构中设置类别:

level_1 <- c("Food", "Food", "Food", "Food", "Food")
level_2 <- c(NA, "Dairy", "Dairy", "Fruit", "Pizza")
level_3 <- c(NA, NA, "Cheese", NA, NA)
categories <- data.frame(level_1, level_2, level_3)

我的最终目标是删除重复的产品,保持最低层级(即更多细节)。

我不一定需要保留最详细的,只需保留标签即可。因此,我们也可以将最详细的类别名称应用于组中的所有行,然后我可以选择要删除的行。 但是请记住,可能存在错误:我们可能有一行 Pizza = FruitPizza = Pizza,应该忽略它们(这需要手动修复)。


编辑:到目前为止的答案都很好,感谢您的帮助。他们只缺少一件事:

在我的真实数据中,我在类别中有错误,因此我忽略了层次树不同部分中的重复项。想象一下这个层次结构的另一部分是clothing &gt; pants &gt; jeans。然后,如果我有这些产品重复:

+---------+----------+
| Product | Category | 
+---------+----------+
|  Apple  |   Food   |
+---------+----------+
|  Apple  |   Jeans  |
+---------+----------+

我不想保留“牛仔裤”,即使它是一个更具体的类别。

我能想到的唯一解决方案是这个(我不知道如何在 R 中实现它):

  • 将层次结构的每个级别放在产品表上,并根据类别填充
  • 按产品分组
  • 检查组中的所有行是否在 level_1 匹配
  • 如果是,检查 level_2,如果是,检查 level_3
  • 在每个阶段,如果不匹配是由于 NA 造成的,我们将有一个获胜者,并在该级别应用现有类别
  • 如果不匹配是由于不同的类别,留下它

或者,解决方案可以是“最高级别的公共类别”的新列,如果这样更容易考虑的话。


编辑 #2 - 新数据集

product_id <- c(1, 1, 2, 2, 3, 3, 2)
name <- c("Cheddar", "Cheddar", "Apple", "Apple", "Pizza", "Pizza", "Apple")
category <- c("Dairy", "Cheese", "Food", "Fruit", "Food", NA, "Jeans")
products <- data.frame(product_id, name, category)

level_1 <- c("Food", "Food", "Food", "Food", "Food","Clothing", "Clothing", "Clothing")
level_2 <- c(NA, "Dairy", "Dairy", "Fruit", "Pizza", NA, "Pants", "Pants")
level_3 <- c(NA, NA, "Cheese", NA, NA, NA, NA, "Jeans")
categories <- data.frame(level_1, level_2, level_3)

目标:

【问题讨论】:

  • 您能否分享一个数据样本,包括您最近编辑中提到的不匹配问题?
  • 其实没那么简单……我在想。

标签: r group-by dplyr hierarchy


【解决方案1】:

我们可以转为“长”格式,arrange 以正确的顺序排列行

library(dplyr)
library(tidyr)
newdat <- categories %>%
              pivot_longer(everything(), names_to = 'product_id',
                       values_drop_na = TRUE) %>% 
              distinct  %>%
              arrange(factor(product_id, levels = rev(names(categories))))

将其用于matchslice 按“product_id”、“名称”分组后的第二个数据集“产品”

products %>%
   group_by(product_id, name) %>% 
   slice(na.omit(match(newdat$value, category))[1])
# A tibble: 3 x 3
# Groups:   product_id, name [3]
#  product_id name    category
#       <dbl> <chr>   <chr>   
#1          1 Cheddar Cheese  
#2          2 Apple   Fruit   
#3          3 Pizza   Food    

【讨论】:

    【解决方案2】:

    另一个dplyr/tidyr 选项可能是

    products %>%
      mutate(level = case_when(category %in% level_1 ~ 1,
                               category %in% level_2 ~ 2,
                               category %in% level_3 ~ 3
                               )) %>%
      group_by(product_id) %>%
      drop_na() %>%
      slice_max(level)
    

    返回

    # A tibble: 3 x 4
    # Groups:   product_id [3]
      product_id name    category level
           <dbl> <chr>   <chr>    <dbl>
    1          1 Cheddar Cheese       3
    2          2 Apple   Fruit        2
    3          3 Pizza   Food         1
    

    【讨论】:

      猜你喜欢
      • 2021-09-15
      • 1970-01-01
      • 2013-10-28
      • 1970-01-01
      • 1970-01-01
      • 2015-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多