Исследование этой проблемы было забавным. Я узнал, что это разновидность задачи о сетевом покрытии и NP Complete .
Это поможет понять масштабы вашей проблемы. Если мы говорим о десятках кластеров, мы могли бы использовать грубую силу. Если это тысячи кластеров, нам нужно использовать приближение.
Я узнал, что есть реализация R жадного алгоритма в пакете RcppGreedySetCover
.
Сначала нам нужно преобразовать форму в два столбца. Мы можем использовать dplyr
.
library(tidyverse)
longx <- x %>%
mutate(splitgroups = strsplit(as.character(groups), " ")) %>%
unnest(splitgroups) %>% select(cluster, splitgroups)
Затем мы можем использовать greedySetCover
для аппроксимации наименьшего набора, охватывающего все элементы.
library(RcppGreedySetCover)
greedySetCover(longx)
#100% covered by 3 sets.
# cluster splitgroups
# 1: 2 20000127
# 2: 3 20000129
# 3: 3 20000130
# 4: 3 20000131
# 5: 3 20000132
# 6: 5 20000128
# 7: 5 20000133
# 8: 5 20000134
# 9: 5 20000135
#10: 5 20000136
Это предполагает набор из 2,3, а 5 охватывает все. Но это не полностью отвечает на ваш вопрос, потому что, как вы знаете, есть набор кластеров, который короче.
Однако, как мы узнали, минимальный набор составляет 3 кластера. Теперь мы можем протестировать все комбинации из 3 кластеров.
set.size <- length(unique(greedySetCover(longx)$cluster))
binary.matrix <- table(longx)
combinations <- combn(unique(x$cluster),set.size)
total.lengths <- apply(combinations,2,function(x){
if(sum(as.logical(colSums(binary.matrix[x,]))) == ncol(binary.matrix))
{sum(rowSums(binary.matrix[x,]))}
else {NA}})
min.length <- min(total.lengths,na.rm = TRUE)
min.set <- combinations[,which(total.lengths == min.length)]
x[min.set,]
# cluster groups chr
#1 1 20000127 20000128 17
#3 3 20000129 20000130 20000131 20000132 35
#4 4 20000133 20000134 20000135 20000136 35
Данные
x <- data.frame(cluster=c(1,2,3,4,5),
groups=c('20000127 20000128',
'20000127 20000128 20000134',
'20000129 20000130 20000131 20000132',
'20000133 20000134 20000135 20000136',
'20000128 20000133 20000134 20000135 20000136'),
chr=c(17,26,35,35,44), stringsAsFactors=FALSE)