如何通过 group_by 中的 group-number 对数据表进行编号/标记?

Posted

技术标签:

【中文标题】如何通过 group_by 中的 group-number 对数据表进行编号/标记?【英文标题】:How to number/label data-table by group-number from group_by? 【发布时间】:2014-05-26 09:49:07 【问题描述】:

我有一个 tbl_df,我想在其中 group_by(u, v) 为使用 (u, v) 观察到的每个不同的整数组合。


编辑:随后通过在 dplyr 0.4.0 中添加(现已弃用)group_indices() 解决了这个问题


a) 然后我想为每个不同的组分配一些任意不同的数字 label=1,2,3... 例如组合 (u,v)==(2,3) 可以得到标签 1,(1,3) 可以得到 2,依此类推。 如何使用 mutate() 完成此操作,而无需三步汇总和自加入?

dplyr 有一个简洁的函数n(),但它给出了组内的元素数量,而不是组的总数量。 In data.table this would simply be called .GRP.

b) 实际上我真正想要分配一个字符串/字符标签('A','B',...)。 但是按整数对组进行编号就足够了,因为我可以使用integer_to_label(i),如下所示。除非有一个聪明的方法来合并这两者?但不要为这部分出汗。

set.seed(1234)

# Helper fn for mapping integer 1..26 to character label
integer_to_label <- function(i)  substr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",i,i) 

df <- tibble::as_tibble(data.frame(u=sample.int(3,10,replace=T), v=sample.int(4,10,replace=T)))

# Want to label/number each distinct group of unique (u,v) combinations
df %>% group_by(u,v) %>% mutate(label = n()) # WRONG: n() is number of element within its group, not overall number of group

   u v
1  2 3
2  1 3
3  1 2
4  2 3
5  1 2
6  3 3
7  1 3
8  1 2
9  3 1
10 3 4

KLUDGE 1: could do df %>% group_by(u,v) %>% summarize(label = n()) , then self-join

【问题讨论】:

@Randy-Lai 和我都分别解决了它。 Randy's 是一个更简洁的习惯用法,适用于多个mutate/summarize(...) 操作。我找到了interaction(u,v, drop=T) 你需要这个做什么? @hadley:我的特殊原因如问题所述:我想为每个不同的(u,v)组分配一些任意(有序)编号=1,2,3 ...所以我最终可以为它们分配字符串标签'A','B','C'......(我的目的是随后在建模和绘图中通过速记来引用它们) @hadley:但总的来说这是一个有用的功能,data.table 包为此实现了.GRP。有没有机会我们可以在 dplyr 中找到一些东西? :) 下一个版本将有group_indices() 【参考方案1】:

dplyr 有一个 group_indices() 函数,您可以像这样使用它:

df %>% 
    mutate(label = group_indices(., u, v)) %>% 
    group_by(label) ...

【讨论】:

group_indices() 使用分组变量的(按字母顺序)排序,有没有办法使用它来保留表中的顺序,或应用您自己的顺序?【参考方案2】:

使用data.table 的另一种方法是

require(data.table)
setDT(df)[,label:=.GRP, by = c("u", "v")]

导致:

    u v label
 1: 2 1     1
 2: 1 3     2
 3: 2 1     1
 4: 3 4     3
 5: 3 1     4
 6: 1 1     5
 7: 3 2     6
 8: 2 3     7
 9: 3 2     6
10: 3 4     3

【讨论】:

【参考方案3】:

更新答案

get_group_number = function()
    i = 0
    function()
        i <<- i+1
        i
    

group_number = get_group_number()
df %>% group_by(u,v) %>% mutate(label = group_number())

你也可以考虑以下稍微不可读的版本

group_number = (function()i = 0; function() i <<- i+1 )()
df %>% group_by(u,v) %>% mutate(label = group_number())

使用iterators

library(iterators)

counter = icount()
df %>% group_by(u,v) %>% mutate(label = nextElem(counter))

【讨论】:

不,这是错误的。我不是在组中寻找行号。我正在寻找 group-number(相当于data.table .GRP)。由于在这个例子中我们有 7 个独特的 (u,v) 组合,输出标签应该是 1:7(以任意顺序) 抱歉,没太注意你的问题。我已经用肮脏的解决方案更新了答案...... 不错,但它本质上只是一个返回递增整数的生成器函数......我们当然可以避免它吗? ^ R 不做生成器功能吗? (比如 Python yield?)无需在 fn 中手动保存状态? 你让我想起了iterators 包。我以前从未使用过它。 (并查看更新的解决方案)。但本质上和我原来的方法是等价的。【参考方案4】:

从 dplyr 版本 1.0.4 开始,函数 cur_group_id() 已替换旧函数 group_indices

在分组的 data.frame 上调用它:

df %>%
  group_by(u, v) %>%
  mutate(label = cur_group_id())

# A tibble: 10 x 3
# Groups:   u, v [6]
       u     v label
   <int> <int> <int>
 1     2     2     4
 2     2     2     4
 3     1     3     2
 4     3     2     6
 5     1     4     3
 6     1     2     1
 7     2     2     4
 8     2     4     5
 9     3     2     6
10     2     4     5

【讨论】:

【参考方案5】:

用三种不同的方式更新我的答案:

A) 使用 interaction(u,v) 的简洁非 dplyr 解决方案:

> df$label <- factor(interaction(df$u,df$v, drop=T))
 [1] 1.3 2.3 2.2 2.4 3.2 2.4 1.2 1.2 2.1 2.1
 Levels: 2.1 1.2 2.2 3.2 1.3 2.3 2.4

> match(df$label, levels(df$label)[ rank(unique(df$label)) ] )
 [1] 1 2 3 4 5 4 6 6 7 7

B) 使 Randy 简洁的又快又脏的生成器函数答案更紧凑:

get_next_integer = function()
  i = 0
  function(u,v) i <<- i+1 

get_integer = get_next_integer() 

df %>% group_by(u,v) %>% mutate(label = get_integer())

C) 这里还有一个使用生成器函数的单行代码,该函数滥用来自this 的全局变量赋值:

i <- 0
generate_integer <- function()  return(assign('i', i+1, envir = .GlobalEnv)) 

df %>% group_by(u,v) %>% mutate(label = generate_integer())

rm(i)

【讨论】:

我使用get_group_name的原因是为了避免使用全局变量。我认为在函数内部更改全局变量通常不是一个好主意……但它仍然有效。 我压缩了你的并将它放在我的答案的顶部。赋值计算为其 LHS 值,因此我们可以简单地说 function(u,v) i &lt;&lt;- i+1 我还用interaction(u,v) 找到了一个简洁的三行非dplyr 方式,并在顶部添加了它。 我还通过this subquestion 解决了interaction(... drop=T) 的增量订单问题【参考方案6】:

我没有足够的评论声誉,所以我发布了一个答案。

使用 factor() 的解决方案是一个很好的解决方案,但它的缺点是在 factor() 按字母顺序排列其级别之后分配组编号。 dplyr 的 group_indices() 也会发生同样的行为。也许您希望根据当前的组顺序将组编号从 1 分配到 n。在这种情况下,您可以使用:

my_tibble %>% mutate(group_num = as.integer(factor(group_var, levels = unique(.$group_var))) )

【讨论】:

谢谢。正如我在问题中指出的那样,这一切都通过在 2015 年在 dplyr 0.4.0 中添加 group_indices() 解决了

以上是关于如何通过 group_by 中的 group-number 对数据表进行编号/标记?的主要内容,如果未能解决你的问题,请参考以下文章

如何在group_by()中使用as_tbl_graph()?

Group_by,条件求和并替换R中的变量

Sparklyr:使用 group_by,然后连接组中行中的字符串

在 R 中使用 dplyr 在 group_by 之后应用自定义函数

Django 1.9 中的 query.group_by

使用 group_by、summary 和 max() 循环 R 中的字符向量