我试图找到一种对行进行分组并为每个组分配索引而不使用循环的方法。困难在于分组变量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在使用循环时效率不是很高,因此我更希望没有循环的解决方案-如果有的话?任何提示都会有所帮助!
好的,这个答案可能可以缩短(可能很多),但是我认为使用igraph
apache可以使每一个都清晰可见,以便直观地检查组的数量。
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)
看起来我们有两个单独的小组可以合作。让我们找出哪个节点(编号)属于哪个组,并在原始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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句