Определение относительного размера перекрывающихся групп на основе информации в двух векторах

Я работаю с очень беспорядочными семейными данными, поскольку дети могут быть сгруппированы с несколькими семьями. Данные структурированы следующим образом:

famid <- c("A","A","B","C","C","D","D")
kidid <- c("1","2","1","3","4","4","5")
df <- as.data.frame(cbind(famid, kidid))

Я хочу определить, какие семьи я могу исключить, исходя из того критерия, что все дети в этой семье сгруппированы вместе в другой, большей семье.

Например, семья A содержит ребенка 1 и ребенка 2. Семья B содержит ребенка 1. Поскольку семья B полностью содержится в семье A, я хочу исключить семью B.

В качестве альтернативы, семейство С содержит Ребенка 3 и Ребенка 4. Семейство D содержит Ребенка 4 и Ребенка 5. Ни одно из семейств не содержится полностью внутри другого, поэтому я пока не хочу отбрасывать ни одно из них.

В моих данных может быть до 6 семей на ребенка и до 8 детей на семью. Там тысячи семей и тысячи детей.

Я попытался решить эту проблему, создав очень широкий data.frame с одной строкой на каждого учащегося, со столбцами для каждой семьи, с которой связан ребенок, каждым родным братом в каждой семье, с которой связан ребенок, и дополнительным столбцом (sibgrp) для каждая связанная семья, которая объединяет всех братьев и сестер вместе. Но когда я попытался найти отдельных братьев и сестер в объединенной строке, я обнаружил, что не знаю, как это сделать — grepl не будет принимать вектор в качестве аргумента шаблона.

Затем я начал изучать пересечение и подобные функции, но они сравнивают целые векторы друг с другом, а не наблюдения внутри вектора с другими наблюдениями внутри этого вектора. (Это означает, что я не могу искать пересечения между строкой символов df[1,2] и строкой символов df[1,3]. Вместо этого Intersect идентифицирует пересечения между df[2] и df[3]).

Я попытался изменить свое мышление, чтобы приспособиться к этому подходу, чтобы я мог сравнивать векторы братьев и сестер друг с другом, предполагая, что я уже знаю, что по крайней мере один брат и сестра являются общими. Я не мог понять, как вообще начать это делать, учитывая, сколько существует разных семей, и сколько из них не связаны друг с другом даже одним общим ребенком.

Что мне здесь не хватает? Был бы очень признателен за любую обратную связь. Благодарю вас!


person szw    schedule 02.04.2016    source источник


Ответы (2)


Эту функцию также можно использовать для выполнения задания. Он возвращает вектор символов, содержащий имена семейств, которые можно удалить.

test_function <- function(dataset){

## split the kidid on the basis of famid
kids_family <- split.default(dataset[['kidid']],f = dataset[['famid']])

family <- names(kids_family)

## This function generates all the possible combinations if we select any two families from family
combn_family <- combn(family,2)

family_removed <- character(0)
apply(combn_family,MARGIN = 2, function(x){

  if (length(setdiff(kids_family[[x[1]]],kids_family[[x[2]]])) == 0)
    family_removed <<- c(family_removed,x[1])
  else if (length(setdiff(kids_family[[x[2]]],kids_family[[x[1]]])) == 0)
    family_removed <<- c(family_removed,x[2])

})

return (family_removed)
}
> df <- data.frame(famid = c("A","A","B","C","C","D","D", "E", "E", "E", "F", "F"),
+                  kidid = c(1, 2, 1, 3, 4, 4, 5, 7, 8, 9, 7, 9))
> test_function(df)
[1] "B" "F"
person Kunal Puri    schedule 02.04.2016
comment
Не могли бы вы подробно описать ваше решение? - person Vincent Bonhomme; 02.04.2016
comment
@VincentBonhomme Это то, о чем я просил. - person Kunal Puri; 02.04.2016
comment
Спасибо, @KunalPuri. На выходных у меня выходной, но завтра первым делом попробую. Это выглядит очень элегантным решением, хотя я должен признать, что не совсем понимаю его внутреннюю работу. - person szw; 03.04.2016
comment
@szw Вы всегда можете вернуться и поделиться своими вопросами в этом посте. - person Kunal Puri; 04.04.2016

Я пробовал около setdiff без шансов. Я пришел и опубликовал это трудоемкое решение в надежде, что есть лучший способ.

# dependencies for melting tables and handling data.frames
require(reshape2)
require(dplyr)


# I have added two more cases to your data.frame
# kidid is passed as numeric (with quoted would have been changed to vector by default)
df <- data.frame(famid = c("A","A","B","C","C","D","D", "E", "E", "E", "F", "F"),
                 kidid = c(1, 2, 1, 3, 4, 4, 5, 7, 8, 9, 7, 9))

# let's have a look to it
df
famid kidid
1      A     1
2      A     2
3      B     1
4      C     3
5      C     4
6      D     4
7      D     5
8      E     7
9      E     8
10     E     9
11     F     7
12     F     9

# we build a contingency table
m <- table(df$famid, df$kidid)

# a family A only contains a family B, if A has all the elements of B, 
# and at least one that B doesnt have
m

  1 2 3 4 5 7 8 9
A 1 1 0 0 0 0 0 0
B 1 0 0 0 0 0 0 0
C 0 0 1 1 0 0 0 0
D 0 0 0 1 1 0 0 0
E 0 0 0 0 0 1 1 1
F 0 0 0 0 0 1 0 1

# an helper function to implement that and return a friendly data.frame
family_contained <- function(m){
  res <- list()
  for (i in 1:nrow(m))
    # for each line in m, we calculate the difference to all other lines
    res[[i]] <- t(apply(m[-i, ], 1, function(row) m[i, ] - row))
  # here we test if all values are 0+ (ie if the selected family has all element of the other)
  # and if at least one is >=1 (ie if the selected family has at least one element that the other doesnt have)
  tab <- sapply(res, function(m) apply(m, 1,  function(x) all(x>=0) & any(x>=1)))
  # we format it as a table to have nice names
  tab %>% as.table() %>% 
    # we melt it into a data.frame
    melt()  %>% 
    # only select TRUE and get rid of this column
    filter(value) %>% select(-value) %>% 
    # to make things clear we name columns
    `colnames<-`(c("this_family_is_contained", "this_family_contains"))
}

family_contained(m)
# this_family_is_contained this_family_contains
# 1           B               A
# 2           F               E

# finally you can filter them with
filter(df, !(famid %in% family_contained(m)$this_family_is_contained))
person Vincent Bonhomme    schedule 02.04.2016
comment
Спасибо, что так тщательно изучили это, @VincentBonhomme - person szw; 03.04.2016