Как добавить вес к элементам, которые зависят от размера группы, в которой они находятся - PullRequest
2 голосов
/ 01 мая 2020

У меня есть набор данных, который выглядит следующим образом

product  material
product1 wheat
product1 water
product1 tomato
product2 milk
product3 basil
product3 garlic

И у меня есть это правило, которое гласит, что если продукт состоит из 1 материала, этот материал составляет 100% продукта. Если он состоит из 2 материалов, первый материал составляет 60% от общего количества, а второй материал - 40% от общего количества. Если продукт состоит из 3 материалов, первый материал составляет 70%, второй 20% и последние 10%. Я хочу добавить столбец этих весов, но я не знаю, как это решить. Есть больше правил до 5 материалов, и вес должен быть легко изменить. В конце я хочу, чтобы приведенные выше данные выглядели так:

product  material weight
product1 wheat    0.7
product1 water    0.2
product1 tomato   0.1
product2 milk     1
product3 basil    0.6
product3 pizza    0.4

. Вот как я начал

df = tribble(
  ~product, ~material_type,
  "product1", "wheat",
  "product1", "Water",
  "product1", "tomato",
  "product2", "milk",
  "product3", "basil",
  "product3", "garlic")

df %>% 
  group_by(product) %>% 
  mutate(n = n())

  product  material_type     n
  <chr>    <chr>         <int>
1 product1 wheat             3
2 product1 Water             3
3 product1 tomato            3
4 product2 milk              1
5 product3 basil             2
6 product3 garlic            2

. Что дает мне количество материалов в каждой группе и из здесь я не уверен, что делать дальше, и надеюсь, у кого-то есть хорошая идея, которая может мне помочь.

edit:

Я попытался добавить case_when, но это не сработало:

df %>% 
  group_by(product) %>% 
  mutate(n = n()) %>% 
  mutate(weight = case_when(n == 5 ~ c(.4, .3, .1, .1, .1),
                            n== 4 ~ c(.5, .3, .1, .1),
                            n ==3 ~ c(.7, .2, .1),
                            n == 2 ~ c(0.6, 0.4),
                            TRUE ~ 1))

Error: `n == 5 ~ c(0.4, 0.3, 0.1, 0.1, 0.1)`, `n == 4 ~ c(0.5, 0.3, 0.1, 0.1)`, `n == 2 ~ c(0.6, 0.4)` must be length 3 or one, not 5, 4, 2
Call `rlang::last_error()` to see a backtrace.

1 Ответ

3 голосов
/ 02 мая 2020

Мы могли бы создать ключ / значение list, а затем использовать n() для извлечения на основе name s list

library(dplyr)
library(tidyr)
lst1 <- list(`5` =  c(.4, .3, .1, .1, .1), `4` = c(.5, .3, .1, .1),
        `3` = c(.7, .2, .1), `2` =c(0.6, 0.4), `1` = 1 )

df %>%
     group_by(product) %>%
     summarise(weight = list(lst1[[as.character(n())]])) %>%   
     unnest(c(weight)) %>%
     select(-product) %>% 
     bind_cols(df, .)
# A tibble: 6 x 3
#  product  material_type weight
#* <chr>    <chr>         <dbl>
#1 product1 wheat           0.7
#2 product1 Water           0.2
#3 product1 tomato          0.1
#4 product2 milk            1  
#5 product3 basil           0.6
#6 product3 garlic          0.4

или unnest и 'material_type' и 'weight' и избегайте bind_cols

df %>% 
   group_by(product) %>% 
   summarise(material_type = list(material_type),
            weight = list(lst1[[as.character(n())]])) %>% 
   unnest(c(material_type, weight))

Или другой вариант if/else

df %>%
   group_by(product) %>%
   mutate(weight = if(n() == 5)  c(.4, .3, .1, .1, .1)
            else if(n() == 4)  c(.5, .3, .1, .1)
            else if(n() == 3) c(.7, .2, .1) 
            else if(n() == 2) c(0.6, 0.4)
            else 1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...