Вывод отношений родства третьих сторон из фрейма данных биунокальных отношений в R - PullRequest
1 голос
/ 26 апреля 2019

У меня есть таблица, показывающая двусторонние родственные связи.Другими словами, у меня может быть первый ряд, показывающий историю отца, у которого есть сын, а в следующей строке этот сын показывает свои отношения с отцом.Но у отца может быть еще один сын (сын2), поэтому у нас есть еще две записи в таблице, показывающие отца, связанного с его сыном2, и сына2, связанного с общим отцом.Проблема возникает, когда отношения между двумя сыновьями не указаны в таблице.Нет записи, показывающей, что son1 - брат son2, и мне нужно извлечь это из таблицы.Это моя цель, и у каждого человека в таблице есть уникальный идентификатор.

До сих пор я пытался сделать следующее:

a) Создать список всехвозможные роли родства (в моем упрощенном примере выше, было бы только 2 роли: отец и сын).

b) Разбить исходную таблицу на столько групп, сколько имеется ролей.Поскольку любой человек в моем примере может быть сыном и / или отцом, я создаю две группы данных.Эти группы являются элементами списка, поэтому, другими словами, я создал список путем подстановки ролей записей.В этом примере список имеет два элемента: один для записей, где второй человек является сыном первого, и другой элемент, показывающий записи, второй человек которых является отцом первого.

c) MyИдея заключалась также в том, чтобы заменить предыдущие (два) элемента списка идентификатором первого лица.Таким образом, моя цель состояла в том, чтобы создать группы каждого идентификатора со всеми связями, которые у них есть.Например, в нашем примере мы получили бы группу для одного идентификатора (отца), образованную двумя строками: записи son1 и son2.

d) Однажды здесь предыдущая подгруппа показывает, что son1 и son2Братья.Поскольку, опять же, об этом не сообщается в исходной таблице, моя цель состоит в том, чтобы получить его путем кодирования, создав две новые записи в исходной таблице, которые показали бы, что у son1 есть брат son2, а у son2 есть брат son1.

Учитывая, что мой фрейм данных - это df, а переменная роль второго человека в отношении - Role_2nd, шаг а) прост:

role <- unique(df$Role_2nd)

Шаг б) также прост:

newRoles <- lapply(role, function(x){subset(df, Role_2nd == x)})

После шага c) результат для первого элемента списка - роль, которую я поднастроил = "сын" - может выглядеть следующим образом:

ID_First  ID_Second    Role_1st      Role_2nd
569         571          father        son
590         592          father        son
587         597          father        son
597         596          father        son
597         598          father        son
603         604          father        son
603         607          father        son 

Как мы видим, идентификатор 597имеет двух сыновей (596 и 598).Это означает, что 596 и 598 являются братьями и сестрами, и мне нужно вывести это отношение, отсутствующее в исходной таблице.

После шага d) и поднабор по идентификатору:

ID_First  ID_Second    Role_1st      Role_2nd
569         571          father        son

ID_First  ID_Second    Role_1st      Role_2nd
590         592          father        son

ID_First  ID_Second    Role_1st      Role_2nd
587         597          father        son

ID_First  ID_Second    Role_1st      Role_2nd
597         596          father        son
597         598          father        son

ID_First  ID_Second    Role_1st      Role_2nd
603         604          father        son
603         607          father        son 

(возможно, шаг d) обязателен для моей конечной цели).

Следуя примеру, другой элемент списка - элемент для отца в роли Role_2nd - должен выглядеть примерно так:

Step c)

ID_First   ID_Second       Role_1st       Role_2nd
571          569              son          father
592          590              son          father
597          587              son          father
596          597              son          father
598          597              son          father
604          603              son          father
607          603              son          father


I skip showing step d) also for the second element.

Вот то, с чем я застрял.Если все вышеприведенное верно - вероятно, есть более простые способы решения проблемы - учитывая, что я хочу создать роль «брат», отсутствующую в моей исходной таблице, моя идея состоит в том, чтобы пропустить два элемента списка черезпетля.Начиная с первого, всякий раз, когда я замечаю повторный ID_First (что происходит для 597 и 603), это будет означать, что два человека, с которыми они связаны (с одной стороны -596 и 598, а с другой 604 &607-) должно быть "братья".

Но я не знаю, как это сделать.Я не знаю, как определить несколько одинаковых значений переменной в элементе списка (например, найти два или более одинаковых ID_First в первом элементе списка);Я не знаю, как их подгруппировать;Я не знаю, как запустить цикл для каждого компонента каждого элемента списка;Я не знаю, как я мог бы кодировать это более простым способом, чем выполнять бесконечный цикл (здесь я показываю только две разные роли, но величина ролей в моем случае очень велика - поскольку я работаюс «незападными» родственными отношениями -).

Подводя итог, окончательный результат для моей проблемы будет примерно таким:

ID_First   ID_Second       Role_1st       Role_2nd
596          597              son          father
598          597              son          father
596          598            brother        brother
598          596            brother        brother
604          603              son          father
607          603              son          father
604          607            brother        brother
607          604            brother        brother

ЕслиЛюбой может дать любые советы, они будут очень рады.

Спасибо за миллион!

1 Ответ

0 голосов
/ 28 апреля 2019

Подумайте об использовании реляционной базы данных, которая должным образом нормализует ваши отношения с интегрированным отношением Семейства , где желаемый результат - просто запрос self-join * для Лица .

В частности, создайте следующие отношения с отношением один-ко-многим между Семействами и Лицами каждый на гранулярном уровне в реляционной базе данных, такой как SQLite (бесплатно, открыто-source, file-level), к которому R может подключиться с помощью RSQLite.Ниже в роли Персоны показана абсолютная (не относительная) роль в контексте всей семьи, например, идентифицируемого деда .

CREATE TABLE Families (
  ID INTEGER,
  FAMILY_NAME VARCHAR(50)
);

CREATE TABLE Persons (
  ID INTEGER,
  FAM_ID INTEGER,
  Role VARCHAR(50),
  FOREIGN KEY(FAM_ID) REFERENCES Family(ID)  
);


INSERT INTO Families
VALUES (1, 'Alpha'), (2, 'Bravo'), (3, 'Charlie'), (4, 'Delta');

INSERT INTO Persons
VALUES(569, 1, 'father'),
      (590, 2, 'father'),
      (587, 3, 'grandfather'),
      (597, 3, 'father'),
      (603, 4, 'father'),
      (571, 1, 'son'),
      (592, 2, 'son'),
      (597, 3, 'son'),
      (596, 3, 'son'),
      (598, 3, 'son'),
      (604, 4, 'son'),
      (607, 4, 'son');

Затем выполните следующий запросс условной логикой для присваивания брат :

SELECT p1.ID AS ID1, p2.ID AS ID2,
       CASE 
            WHEN p1.Role = 'son' AND p2.Role = 'son'
            THEN 'brother'
            ELSE p1.Role
       END As Role1,

       CASE 
            WHEN p1.Role = 'son' AND p2.Role = 'son'
            THEN 'brother'
            ELSE p2.Role
       END As Role2

FROM Persons p1
INNER JOIN Persons p2 ON p1.FAM_ID = p2.FAM_ID AND p1.ID > p2.ID

ORDER BY p1.ID, p2.ID;

-- | ID1 | ID2 | Role1   | Role2       |
-- | --- | --- | ------- | ----------- |
-- | 571 | 569 | son     | father      |
-- | 592 | 590 | son     | father      |
-- | 596 | 587 | son     | grandfather |
-- | 597 | 587 | father  | grandfather |
-- | 597 | 587 | son     | grandfather |
-- | 597 | 596 | father  | son         |
-- | 597 | 596 | brother | brother     |
-- | 598 | 587 | son     | grandfather |
-- | 598 | 596 | brother | brother     |
-- | 598 | 597 | son     | father      |
-- | 598 | 597 | brother | brother     |
-- | 604 | 603 | son     | father      |
-- | 607 | 603 | son     | father      |
-- | 607 | 604 | brother | brother     |

Просмотр на БД Fiddle (обязательно нажмите Run для результатов)


Если вы хотите полностью остаться в R, рассмотрите возможность создания таких соответствующих Семей и Персонов фреймов данных и запускайте ту же логику слияния и условную логику:

Build

Families <- data.frame(ID = seq(1, 4),
                       Family_Name = c("Alpha", "Bravo", "Charlie", "Delta"))

Persons <- data.frame(ID = c(569, 590, 587, 597, 603, 571, 592, 597, 596, 598, 604, 607),
                      Fam_ID = c(1, 2, 3, 3, 4, 1, 2, 3, 3, 3, 4, 4),
                      Role = c("father", "father", "grandfather", "father", "father",
                               "son", "son", "son", "son", "son", "son", "son"))

Запрос

# SELF JOIN, AVOIDING REVERSE DUPLICATES
merge_raw <- subset(merge(Persons, Persons, by="Fam_ID", suffixes = c("_1", "_2")), 
                    ID_1 > ID_2) 

# CONDITIONALLY ASSIGN "brother"
final_df <- within(merge_raw, {    
        ID_1 <- ifelse(ID_1 == "son" & ID_2 == "son", "brother", ID_1)
        ID_2 <- ifelse(ID_1 == "son" & ID_2 == "son", "brother", ID_2)
    })

# RE-ORDER ROWS AND COLUMNS
final_df <- with(final_df, data.frame(final_df[order(ID_1, ID_2), 
                                               c("ID_1", "ID_2", "Role_1", "Role_2")],
                                      row.names = NULL))

final_df

#    ID_1 ID_2 Role_1      Role_2
# 1   571  569    son      father
# 2   592  590    son      father
# 3   596  587    son grandfather
# 4   597  587 father grandfather
# 5   597  587    son grandfather
# 6   597  596 father         son
# 7   597  596    son         son
# 8   598  587    son grandfather
# 9   598  596    son         son
# 10  598  597    son      father
# 11  598  597    son         son
# 12  604  603    son      father
# 13  607  603    son      father
# 14  607  604    son         son

Просмотр на Rextester

...