Как создавать подгруппы на основе критериев групповых отношений

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

Цель: я пытаюсь создать подгруппы внутри домохозяйств в соответствии с налоговыми единицами. Физические лица считаются единой налоговой единицей, если они являются (1) супругом или (2) ребенком-иждивенцем. Ребенком-иждивенцем считается ребенок до 18 лет или ребенок до 23 лет, являющийся студентом.

В пределах данного домохозяйства может существовать одна налоговая единица или несколько налоговых единиц. Другие супружеские пары или отдельные лица в каждом домохозяйстве, у которых нет детей-иждивенцев, образуют отдельные налоговые единицы.

Пример фрейма данных:

   household     name age student                r01         r02          r03               r04          r05
1          1     john  60       0               <NA>      spouse       parent            parent       parent
2          1     mary  56       0             spouse        <NA>       parent            parent       parent
3          1    fiona  25       0              child       child         <NA>           sibling      sibling
4          1      tim  20       1              child       child      sibling              <NA>      sibling
5          1     nora  16       0              child       child      sibling           sibling         <NA>
6          2 terrence  58       0               <NA>      spouse child-in-law step-child-in-law       parent
7          2  siobhan  57       0             spouse        <NA>        child        step-child       parent
8          2      jim  90       0      parent-in-law      parent         <NA>            spouse grand-parent
9          2    maire  87       0 step-parent-in-law step-parent       spouse              <NA>        other
10         2     eoin  21       1              child       child  grand-child             other         <NA>
11         3   ronald  50       0               <NA>        <NA>         <NA>              <NA>         <NA>

Код для воспроизведения:

df <- data.frame(household = c(rep(1,5), rep(2,5), 3),
           name = c("john", "mary", "fiona", "tim", "nora", "terrence", "siobhan", "jim", "maire", "eoin", "ronald"),
           age = c(60, 56, 25, 20, 16, 58, 57, 90, 87, 21, 50),
           student = c(0,0,0,1,0,0,0,0,0,1,0),
           r01 = c(NA, "spouse", rep("child",3), NA, "spouse", "parent-in-law", "step-parent-in-law", "child", NA),
           r02 = c("spouse", NA, rep("child", 3), "spouse", NA, "parent", "step-parent", "child", NA),
           r03 = c(rep("parent",2), NA, rep("sibling", 2), "child-in-law", "child", NA, "spouse", "grand-child", NA),
           r04 = c(rep("parent",2), "sibling", NA, "sibling", "step-child-in-law", "step-child", "spouse", NA, "other", NA),
           r05 = c(rep("parent", 2), rep("sibling",2), NA, rep("parent", 2), "grand-parent", "other", NA, NA))

Подход: Для начала я создал переменные для перечисления порядка членов семьи и для идентификации зависимого ребенка.

df <- df %>%
  group_by(household) %>%
  mutate(fam_mem = row_number(),
         dep_child = ifelse(age < 18 | (age < 23 & student == 1), 1, 0))

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

После этого я надеялся отсортировать по статусу зависимости и использовать lag, чтобы создать новое имя переменной домохозяйства, которое группируется в налоговые единицы, например. 1a, 2a, 2b, 3a.

Желаемый результат

   household name       age student r01                r02         r03          r04               r05          fam_mem dep_child household_tax_unit
       <dbl> <chr>    <dbl>   <dbl> <chr>              <chr>       <chr>        <chr>             <chr>          <int>     <dbl> <chr>             
 1         1 john        60       0 NA                 spouse      parent       parent            parent             1         0 1a                
 2         1 mary        56       0 spouse             NA          parent       parent            parent             2         0 1a                
 3         1 fiona       25       0 child              child       NA           sibling           sibling            3         0 1b                
 4         1 tim         20       1 child              child       sibling      NA                sibling            4         1 1a                
 5         1 nora        16       0 child              child       sibling      sibling           NA                 5         1 1a                
 6         2 terrence    58       0 NA                 spouse      child-in-law step-child-in-law parent             1         0 2a                
 7         2 siobhan     57       0 spouse             NA          child        step-child        parent             2         0 2a                
 8         2 jim         90       0 parent-in-law      parent      NA           spouse            grand-parent       3         0 2b                
 9         2 maire       87       0 step-parent-in-law step-parent spouse       NA                other              4         0 2b                
10         2 eoin        21       1 child              child       grand-child  other             NA                 5         1 2a                
11         3 ronald      50       0 NA                 NA          NA           NA                NA                 1         0 3a  

Джон и Мэри делят налоговую единицу со своими детьми-иждивенцами Тимом и Норой, а Фионе предоставляется собственная налоговая единица, поскольку она не является ребенком-иждивенцем.

Терренс и Шивобхан женаты и делят налоговую единицу со своим ребенком-иждивенцем Эоином, в то время как Джим и Мэр используют другую налоговую единицу, поскольку их ребенок / приемный ребенок не является иждивенцем и они женаты.

Рональд живет один, поэтому является единицей единого налога.

пожалуйста, покажите желаемый результат

langtang 12.07.2024 15:45

спасибо, я добавил желаемый результат выше

ravinglooper 12.07.2024 15:58
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Функция memb создает матрицу инцидентности mm из столбцов r и dep_child. Строка с логическим условием, отмеченным ##, является ключевой строкой, а следующая строка обеспечивает симметричность mm. Теперь дано mm, сформируем граф-граф g. Наконец, создайте вектор принадлежности как результат функции memb.

library(dplyr)
library(igraph)

memb <- function(dep_child, r) {
  m <- as.matrix(r)[, 1:nrow(r), drop = FALSE]
  m[is.na(m)] <- ""
  mm <- m == "spouse" | (m == "child" & dep_child)  ##
  mm <- mm | t(mm)
  g <- graph_from_adjacency_matrix(mm, mode = "undirected")
  components(g)$membership
}

df %>%
  mutate(unit = row_number(),
         dep_child = +(age < 18 | (age < 23 & student == 1)),
         memb = memb(dep_child, pick(starts_with("r"))), 
         memb = paste0(household, letters[memb]), .by = household)

предоставление

   household     name age student                r01         r02          r03
1          1     john  60       0               <NA>      spouse       parent
2          1     mary  56       0             spouse        <NA>       parent
3          1    fiona  25       0              child       child         <NA>
4          1      tim  20       1              child       child      sibling
5          1     nora  16       0              child       child      sibling
6          2 terrence  58       0               <NA>      spouse child-in-law
7          2  siobhan  57       0             spouse        <NA>        child
8          2      jim  90       0      parent-in-law      parent         <NA>
9          2    maire  87       0 step-parent-in-law step-parent       spouse
10         2     eoin  21       1              child       child  grand-child
11         3   ronald  50       0               <NA>        <NA>         <NA>
                 r04          r05 unit dep_child memb
1             parent       parent    1         0   1a
2             parent       parent    2         0   1a
3            sibling      sibling    3         0   1b
4               <NA>      sibling    4         1   1a
5            sibling         <NA>    5         1   1a
6  step-child-in-law       parent    1         0   2a
7         step-child       parent    2         0   2a
8             spouse grand-parent    3         0   2b
9               <NA>        other    4         0   2b
10             other         <NA>    5         1   2a
11              <NA>         <NA>    1         0   3a

Возможно, просто рассмотреть student also? Например, измените строку мутации на: memb = memb(if_else(age<23 & student==1, 17,age), pick(starts_with("r"))),

langtang 12.07.2024 18:47

Обновлено, чтобы исправить ошибки и привести выходные значения в соответствие с недавно опубликованными желаемыми выходными данными.

G. Grothendieck 12.07.2024 18:59

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

# create test data
df <- read.table(text = "household     name age student                r01         r02          r03               r04          r05
1          1     john  60       0               <NA>      spouse       parent            parent       parent
2          1     mary  56       0             spouse        <NA>       parent            parent       parent
3          1    fiona  25       0              child       child         <NA>           sibling      sibling
4          1      tim  20       1              child       child      sibling              <NA>      sibling
5          1     nora  16       0              child       child      sibling           sibling         <NA>
6          2 terrence  58       0               <NA>      spouse child-in-law step-child-in-law       parent
7          2  siobhan  57       0             spouse        <NA>        child        step-child       parent
8          2      jim  90       0      parent-in-law      parent         <NA>            spouse grand-parent
9          2    maire  87       0 step-parent-in-law step-parent       spouse              <NA>        other
10         2     eoin  21       1              child       child  grand-child             other         <NA>
11         3   ronald  50       0               <NA>        <NA>         <NA>              <NA>         <NA>
")

library(tidyverse)
library(igraph)

# add a person identifier to the data
df <- df %>% 
  mutate(person = row_number(), .by = household)

# pivot wider to define relationships between person and r_person
# filter to keep only required relationships
df_pivot <- df %>%
  pivot_longer(starts_with("r"), names_to = "r_person")%>%
  mutate(person = paste0(household, "-", person)) %>%
  mutate(r_person = as.numeric(substring(r_person, 2), .by = household)) %>%
  mutate(r_person = paste0(household, "-", r_person)) %>%
  filter(r_person != person & (value %in% c("spouse") | (value == "child" & (age <= 23 | student == 1)))) 

# function to define groups using igraph. The result is a data.frame which can be
# joined to the original data
make_groups <- function(data) {
  # create a graph from the pivoted data.frame
  rel_graph <- graph_from_data_frame(select(data, r_person, person)) 
  
  # The membership component is a named vector of group identifier for each
  # connected group
  group_vector <- components(rel_graph)$membership
  
  # return a data frame with household, person (as per orignal data frame) along
  # with group_id
  data %>%
    mutate(
      group_id = group_vector[person]
    ) %>%
    distinct(person, group_id) %>%
    separate_wider_delim(person, names = c("household", "person"), delim = "-") %>%
    mutate(person = as.integer(person), household = as.integer(household))
  
}

# use this function to create the lookup with the group_id variable
lookup_groups <- make_groups(df_pivot)

# join this with the original data, then re-calculate group_id for the ungrouped
# members of each household, and recode to household_id + letter format
want <- df %>%
  left_join(lookup_groups, by = c("household", "person")) %>%
  mutate(
    group_id = if_else(is.na(group_id), 1000000000 + row_number(), group_id),
    group_id = paste0(household, letters[as.factor(group_id)]),
    .by = household
  )

want
#>    household     name age student                r01         r02          r03
#> 1          1     john  60       0               <NA>      spouse       parent
#> 2          1     mary  56       0             spouse        <NA>       parent
#> 3          1    fiona  25       0              child       child         <NA>
#> 4          1      tim  20       1              child       child      sibling
#> 5          1     nora  16       0              child       child      sibling
#> 6          2 terrence  58       0               <NA>      spouse child-in-law
#> 7          2  siobhan  57       0             spouse        <NA>        child
#> 8          2      jim  90       0      parent-in-law      parent         <NA>
#> 9          2    maire  87       0 step-parent-in-law step-parent       spouse
#> 10         2     eoin  21       1              child       child  grand-child
#> 11         3   ronald  50       0               <NA>        <NA>         <NA>
#>                  r04          r05 person group_id
#> 1             parent       parent      1       1a
#> 2             parent       parent      2       1a
#> 3            sibling      sibling      3       1b
#> 4               <NA>      sibling      4       1a
#> 5            sibling         <NA>      5       1a
#> 6  step-child-in-law       parent      1       2a
#> 7         step-child       parent      2       2a
#> 8             spouse grand-parent      3       2b
#> 9               <NA>        other      4       2b
#> 10             other         <NA>      5       2a
#> 11              <NA>         <NA>      1       3a

Created on 2024-07-12 with reprex v2.1.0

Другие вопросы по теме