【问题标题】:Summarise tibble to multiple rows of output总结 tibble 到多行输出
【发布时间】:2019-12-20 08:09:51
【问题描述】:

假设我在 R 中有以下小标题:

activation_date | country | campaign | revenue | users
======================================================
1               | 1       | 1        | R_1     | U_1
2               | 1       | 1        | R_2     | U_2
3               | 1       | 1        | R_3     | U_3
1               | 1       | 2        | R_4     | U_4
2               | 1       | 2        | R_5     | U_5
3               | 1       | 2        | R_6     | U_6
1               | 2       | 3        | R_7     | U_7
2               | 2       | 3        | R_8     | U_8
3               | 2       | 3        | R_9     | U_9

我想按国家/地区对这个 tibble 进行分组并汇总其数据以将此 tibble 作为其输出:

country | campaign | ltv
==========================
1       | 1        | ltv_1
1       | 2        | ltv_2
2       | 3        | ltv_3

但是,我希望ltv_1 ltv_2 使用所有R_1R_6U_1U_6 来联合计算,并且ltv_3使用R_7R_9U_7U_9 进行计算。

我不能 group_by "country" 和 summarise,因为这会删除我想保留的 "campaign" 列,但我不能 group_by "country" 和 "campaign"要么是因为那样我将无法使用前三行来帮助计算ltv_2,也无法使用后三行来帮助计算ltv_1

一种可能的方法是按“国家”分组并使用group_modify 函数生成分组的输出小标题。但是,该功能处于“实验”阶段,因此我不想过分依赖它。有没有其他既定的方法来做到这一点?


一个示例输入小标题是:

# A tibble: 9 x 5
  activation_date country campaign revenue users
            <dbl>   <dbl>    <dbl>   <dbl> <dbl>
1               1       1        1       1    11
2               2       1        1       2    12
3               3       1        1       3    13
4               1       1        2       4    14
5               2       1        2       5    15
6               3       1        2       6    16
7               1       2        3       7    17
8               2       2        3       8    18
9               3       2        3       9    19

它的输出是:

# A tibble: 3 x 3
  country campaign   ltv
    <dbl>    <dbl> <dbl>
1       1        1 0.213
2       1        2 0.296
3       2        3 0.444

使用生成它的代码,使用group_modify 函数,是:

test_tibble = tribble (~ activation_date, ~ country, ~ campaign, ~ revenue, ~ users,
                       1, 1, 1, 1, 11,
                       2, 1, 1, 2, 12,
                       3, 1, 1, 3, 13,
                       1, 1, 2, 4, 14,
                       2, 1, 2, 5, 15,
                       3, 1, 2, 6, 16,
                       1, 2, 3, 7, 17,
                       2, 2, 3, 8, 18,
                       3, 2, 3, 9, 19)

test_function = function (activation_date, campaign, revenue, users) {
    total_ltv = sum (revenue) / sum (users)
    campaign_ltv = double (0)
    campaign_names = unique (campaign)
    for (c in campaign_names) {
        campaign_ltv = c (campaign_ltv, sum (revenue [campaign == c]) / sum (users [campaign == c]))
    }

    return (tibble (campaign = campaign_names,
                    ltv = campaign_ltv / 2 + total_ltv / 2))
}


test_tibble %>%
    group_by (country) %>%
    group_modify (~ test_function (.x$activation_date, .x$campaign, .x$revenue, .x$users)) %>%
    ungroup

【问题讨论】:

  • ltv 计算是什么?此外,如果您为revenue 输入一些数字会更好。理想情况下,您的示例数据结构应该代表您的实际数据结构。
  • 我可以输入一些虚构的数字,但为什么实际值或实际计算很重要? ltv_1 必须是 R_1U_6 的函数,ltv_2 也必须是 R_1U_6 的函数,它们可能是也可能不是相同的函数,所以让我们假设 w.l.o.g.他们不是,我该怎么做?
  • 这很重要,因为结果更容易验证。
  • 很公平,我在问题中添加了一个示例。
  • 我建议,如果您不想使用 group_modify(我同意这是这里的自然选择),您可以使用 splitpurrr::map_dfr

标签: r dplyr tibble


【解决方案1】:

选项 1 -

有点冗长但透明的方法是使用joins。但是,考虑到test_function 中的代码,这并不是那么冗长。 -

test_tibble %>% 
  group_by(country, campaign) %>% 
  summarize(campaign_ltv = sum(revenue)/sum(users)) %>% 
  inner_join(
    test_tibble %>% 
      group_by(country) %>% 
      summarise(total_ltv = sum(revenue)/sum(users)),
    by = "country"
  ) %>% 
  mutate(ltv = (total_ltv + campaign_ltv)/2) %>% 
  ungroup()

# A tibble: 3 x 5
  country campaign campaign_ltv total_ltv   ltv
    <dbl>    <dbl>        <dbl>     <dbl> <dbl>
1       1        1        0.167     0.259 0.213
2       1        2        0.333     0.259 0.296
3       2        3        0.444     0.444 0.444

选项 2) -

test_function 输出包装在list 中以作为嵌套的tibble 并使用unnest

test_tibble %>%
  group_by (country) %>%
  mutate(
    ltv = list(test_function(activation_date, campaign, revenue, users))
  ) %>%
  select(country, ltv) %>% 
  filter(row_number() == 1) %>% 
  unnest() %>% 
  ungroup()

# A tibble: 3 x 3
  country campaign   ltv
    <dbl>    <dbl> <dbl>
1       1        1 0.213
2       1        2 0.296
3       2        3 0.444

选项 3) -

df %>% 
  group_by(country) %>% 
  tidyr::complete(nesting(country, campaign), nesting(revenue, users)) %>% 
  group_by(campaign, add = TRUE)
  # now you have all revenue and users for each country-campaign
  # for total_ltv: use revenue and users as is
  # for campaign_ltv: use revenue and users where activation_date is not NA

# A tibble: 15 x 5
# Groups:   country, campaign [3]
   country campaign revenue users activation_date
     <int>    <int> <chr>   <chr>           <int>
 1       1        1 R_1     U_1                 1
 2       1        1 R_2     U_2                 2
 3       1        1 R_3     U_3                 3
 4       1        1 R_4     U_4                NA
 5       1        1 R_5     U_5                NA
 6       1        1 R_6     U_6                NA
 7       1        2 R_1     U_1                NA
 8       1        2 R_2     U_2                NA
 9       1        2 R_3     U_3                NA
10       1        2 R_4     U_4                 1
11       1        2 R_5     U_5                 2
12       1        2 R_6     U_6                 3
13       2        3 R_7     U_7                 1
14       2        3 R_8     U_8                 2
15       2        3 R_9     U_9                 3

test_tibble 演示 -

test_tibble %>% 
  group_by(country) %>% 
  tidyr::complete(nesting(country, campaign), nesting(revenue, users)) %>% 
  group_by(campaign, add = TRUE) %>% 
  summarise(
    ltv = sum(revenue)/sum(users)/2 + 
      sum(revenue[!is.na(activation_date)])/sum(users[!is.na(activation_date)])/2
  ) %>% 
  ungroup()

# A tibble: 3 x 3
  country campaign   ltv
    <dbl>    <dbl> <dbl>
1       1        1 0.213
2       1        2 0.296
3       2        3 0.444

【讨论】:

  • 所以问题在于它不够通用;它解决了我在问题中使用的 specific 示例函数的问题,但这只是一个示例,我想要应用的函数远没有那么具体和简单。
  • 函数的复杂性无关紧要。只要每组输出 1 个值(对于countrycountry-campaign 组合),此代码就可以工作。如果函数输出长度大于 1,那么您始终可以将其包装在一个列表中并使用 unnest()
  • 是的,事实上我感兴趣的函数并不是每组输出一个值。我要应用的函数实际上是一个国家内活动 LTV 时间向量上的卡尔曼滤波器,不能简化为这种情况。
猜你喜欢
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
  • 1970-01-01
  • 2020-06-06
  • 2019-03-29
  • 2020-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多