当向量中的至少一个对象与其他向量中的至少一个对象匹配时,对列进行分组和索引 r dplyr

时间:2021-02-22 20:03:40

标签: r dplyr group-by

我试图找到一种方法来对行进行分组,并在不使用循环的情况下为每个组分配一个索引。难点在于分组变量num没有唯一标识符; num 是一个数字向量(定义为字符)。我想对至少与向量中的任何数字匹配的所有行进行分组。向量具有不同的长度,最多包含 20 个数字。 我举个例子:

我有一个这样的数据框:

df <- data.frame(id = c(1:5), num = c('111;222', '333;111;444', '000;88888;1', '9999;111', '1'))

我用分隔符 ; 分割 num 使得每一行都变成一个向量:

library(dplyr)
df <- df %>%
  mutate(num = str_split(num, ';'))

我想索引向量中至少有一个数字与相同索引匹配的所有行。结果应如下所示:

id     num               group_index
1  c('111','222')             1
2  c('333','111','444')       1
3  c('000','88888','1')       2
4  c('9999','444')            1
5  '1'                        2

该示例说明了另一个困难:第 1 组由“111”和“444”标识,即使第 1 行是第 1 组的一部分并且 num 中不包含“444”。

如果 num 只是一个字符串,我会执行以下操作

df <- group_by(num) %>%
  mutate(group_index = group_indices(.,num))

现在,我想我或许应该从识别群体开始。第一种不起作用的方法是:

df <- df %>%
 group_by_if(num, any(num, str_c(num, collapse = '|')) == T)

我知道我可以从写一个循环开始。但是,R 对循环不是很有效,所以我更喜欢没有循环的解决方案——如果有的话?任何提示都会有所帮助!

1 个答案:

答案 0 :(得分:1)

好的,这个答案可能会被缩短(可能很多),但我认为使用 igraph 可以保持每一个很好的和可见的组数的目视检查。< /p>

library( data.table )
library( igraph )
#make df a data.table
setDT(df)
#split num-column to v1, v2, ... ,vn
df[, paste0("v", 1:length( tstrsplit(df$num, ";"))) := tstrsplit( num, ";")]
#    id         num   v1    v2   v3
# 1:  1     111;222  111   222 <NA>
# 2:  2 333;111;444  333   111  444
# 3:  3 000;88888;1  000 88888    1
# 4:  4    9999;111 9999   111 <NA>
# 5:  5           1    1  <NA> <NA>

#now melt to long format
df.melt <- melt(df, id.vars = "id", measure.vars = patterns("^v[0-9]"), value.name = "from" )
#create links
df.melt[, to := shift( from, type = "lead"), by = .(id)][]
#drop inomplete rows
df.melt <- df.melt[ complete.cases(df.melt), ]
#    id variable  from    to
# 1:  1       v1   111   222
# 2:  2       v1   333   111
# 3:  3       v1   000 88888
# 4:  4       v1  9999   111
# 5:  2       v2   111   444
# 6:  3       v2 88888     1

g = graph_from_data_frame( df.melt[ , .(from, to)])
# plot(g)

enter image description here

看起来我们有两个独立的小组要合作。让我们找出哪个节点(编号)属于哪个组,并在原始 df 上使用此信息

dt.lookup <- as.data.table( components(g)$membership, keep.rownames = TRUE )
#       V1 V2
# 1:   111  1
# 2:   333  1
# 3:   000  2
# 4:  9999  1
# 5: 88888  2
# 6:   222  1
# 7:   444  1
# 8:     1  2


#go back to the molten data of the original df
df.melt <- melt(df, id.vars = "id", measure.vars = patterns("^v[0-9]"))
df.melt <- df.melt[ complete.cases(df.melt), ]
#perform update join to get the groupnumber
df.melt[ dt.lookup, group := i.V2, on = .(value = V1) ]
#    id variable value group
# 1:  1       v1   111     1
# 2:  2       v1   333     1
# 3:  3       v1   000     2
# 4:  4       v1  9999     1
# 5:  5       v1     1     2
# 6:  1       v2   222     1
# 7:  2       v2   111     1
# 8:  3       v2 88888     2
# 9:  4       v2   111     1
# 10: 2       v3   444     1
# 11: 3       v3     1     2

#summarise to go back to oroiginal df form
df.melt[, .(num = paste0( value, collapse = ";"),
            group = paste0( unique(group), collapse = ",")),
        by = .(id) ][]

最终输出

#    id         num group
# 1:  1     111;222     1
# 2:  2 333;111;444     1
# 3:  3 000;88888;1     2
# 4:  4    9999;111     1
# 5:  5           1     2
相关问题