Объединение иерархических данных в другую таблицу в R - PullRequest
0 голосов
/ 24 мая 2019

У меня есть 2 фрейма данных (drug и class), к которым мне нужно присоединиться по последнему уровню кода классификации ATC, а также добавить 4 дополнительных столбца с соответствующими родительскими уровнями.

Я предложил 2 решения, но первое довольно многословно, а второе использует MS Access (чего я хочу избежать). Более того, если у меня будет больше уровней, код будет гораздо более подробным, чем этот. Есть ли более элегантное решение этой проблемы? Как я могу выполнить такое самостоятельное соединение в R, как я делал в Access? Я довольно новичок в R и SQL, поэтому небольшое объяснение будет оценено:)

Пример данных

  • 1011 * препарат *

    • столбцы: ID, ProductName, level5
    • каждая строка представляет собой один продукт (препарат) с уникальным идентификатором и кодом уровня 5 классификации ATC (см. Wiki-ATC )
  • класс

    • столбцы: classCode, className
    • classCode содержит все уровни классификации УВД в одном столбце, уровень1-уровень5 УВД
    • примечание: эту таблицу я могу только читать.

Краткое пояснение об этих классификациях и уровнях

В drug$level5 у нас есть Level5 classCode s:
Уровень 5 - A10BA02 (метформин). Он является членом уровня 4 - A10BA (бигуаниды), уровня 3 - A10B (противодиабетики, ex.insulins), уровня 2 - A10 (противодиабетики), уровня 1 - A (Пищеварительный тракт и обмен веществ)
Каждый уровень строго определяется его длиной (L1 = 1чар., L2 = 3 знака., L3 = 4 знака., L4 = 5 знаков., L5 = 7 знаков.)

| Level   | Code    | Name                            |
|---------|---------|---------------------------------|
| Level5* | A10BA02 | metformin                       |
| Level4  | A10BA   | biguanides                      |
| Level3  | A10B    | antidiabetics, ex. insulins     |
| level2  | A10     | antidiabetics                   |
| Level1  | A       | alimentary tract and metabolism |

Пример данных

drug <- data.frame(ID = 1:5,
                   ProductName = c('ABC', 'CDE', 'FGH', 'IJK', 'LMN'),
                   level5 = c('A10BA02', 'C01BA02', 'C03CA01', 'C03CA03', 'C01BA02'), 
                   stringsAsFactors = F)

class <- data.frame(code = c('A', 'A10', 'A10B', 'A10BA', 'A10BA02', 'C', 'C01', 'C01B', 'C01BA',
                            'C01BA02', 'C03', 'C03C', 'C03CA', 'C03CA01', 'C03CA03', 'C07', 'C07A',
                            'C07AA', 'C07AA03'),
                    className = c('Alimentary tract and metabolism',
                                  'Antidiabetics', 'Antidiabetics, except insulins',
                                  'Biguanides', 'Metformin', 'Cardiovascular system',
                                  'Cardiacs', 'Antiarythmics, grp I and III',
                                  'Antiarythmics, grp IA', 'Procainamide', 'Diuretics',
                                  'Diuretics strong', 'Sulfonamides', 'Furosemide',
                                  'Piretanide', 'Betablockers', 'Betablockers',
                                  'Non-selective betablockers', 'Pindolol'), 
                    stringsAsFactors = F)
# print
drug
head(class, 8)

Цель

Я хочу присоединить class к drug фрейму данных с результирующим df следующим образом: Результирующая таблица должна иметь дополнительные столбцы, каждый столбец для каждого уровня от 1 до 5. Цель состоит в том, чтобы создать иерархию фильтрации, в которой вы сначала фильтруете продукты по уровню 1, затем по уровню 2 и т. Д. *

+----+-------------+-------------------------------------+---------------------+---------------------------------------+-------------------------------+------------------------+
| ID | ProductName | L1                                  | L2                  | L3                                    | L4                            | L5                     |
+----+-------------+-------------------------------------+---------------------+---------------------------------------+-------------------------------+------------------------+
| 1  | ABC         | A - Alimentary tract and metabolism | A10 - Antidiabetics | A10B - Antidiabetics, except insulins | A10BA - Biguanides            | A10BA02 - Metformin    |
+----+-------------+-------------------------------------+---------------------+---------------------------------------+-------------------------------+------------------------+
| 2  | CDE         | C - Cardiovascular system           | C01 - Cardiacs      | C01B - Antiarythmics, grp I and III   | C01BA - Antiarythmics, grp IA | C01BA02 - Procainamide |
+----+-------------+-------------------------------------+---------------------+---------------------------------------+-------------------------------+------------------------+
...

Мой грязный раствор N.1, использующий только R

Я придумал не очень красивое и довольно подробное решение, в котором я мутировал drug$level5 с substr() для каждого уровня. Затем выполните left_join() и после unite() столбцы.

library(tidyr)
library(dplyr)

sol1 <- drug %>%
  mutate(level1 = substr(level5, 1, 1),
         level2 = substr(level5, 1, 3),
         level3 = substr(level5, 1, 4),
         level4 = substr(level5, 1, 5)) %>%
  left_join(class, by = c('level1' = 'code')) %>%
  left_join(class, by = c('level2' = 'code')) %>%
  left_join(class, by = c('level3' = 'code')) %>%
  left_join(class, by = c('level4' = 'code')) %>%
  left_join(class, by = c('level5' = 'code')) %>%
  select(ID:level4, 
         level1name = className.x,
         level2name = className.y,
         level3name = className.x.x,
         level4name = className.y.y,
         level5name = className
         ) %>%
  unite(L1, level1, level1name, sep = ' - ') %>%
  unite(L2, level2, level2name, sep = ' - ') %>%
  unite(L3, level3, level3name, sep = ' - ') %>%
  unite(L4, level4, level4name, sep = ' - ') %>%
  unite(L5, level5, level5name, sep = ' - ') 

Мое решение N.2, использующее Access self join

Другим решением было изменить class таблицу в MS Access с помощью self join a создать дополнительные столбцы для каждого уровня, а затем просто оставить присоединение к этой таблице на drug df в R.

--- sqlReshapedTable
SELECT A.code AS L5,
       A.className AS className,

       L1.code + ' ' + L1.Name AS L1,
       L2.code + ' ' + L2.Name AS L2,
       L3.code + ' ' + L3.Name AS L3,
       L4.code + ' ' + L4.Name AS L4
FROM 
(((class AS A
INNER JOIN class AS L1 ON L1.code = LEFT(A.code, 1))
INNER JOIN class AS L2 ON L2.code = LEFT(A.code, 3))
INNER JOIN class AS L3 ON L3.code = LEFT(A.code, 4))
INNER JOIN class AS L4 ON L4.code = LEFT(A.code, 5);
sol2 <- drug %>% 
  left_join(sqlReshapedTable, by = c('level5' = 'Code'))

Большое спасибо за любую помощь!

1 Ответ

0 голосов
/ 24 мая 2019

Возможно, не самое лучшее решение, но, похоже, оно работает в вашем случае (я называю ваш фрейм данных class на dclass):

library(tidyverse)

drug %>%
  group_by(ID, ProductName) %>%
  summarise(
    code = list(map_chr(c(1, 3:5, 7), ~ gsub(sprintf('(^.{%s}).*', .x), '\\1', level5)))
    ) %>%
  unnest %>%
  left_join(dclass, by = 'code') %>% 
  rename_all(tolower) %>%
  mutate(
    key       = paste('L', str_count(code, '\\D|\\d+'), sep = ''),
    val       = paste(code, classname, sep = ' - '),
    classname = NULL,
    code      = NULL
    ) %>%
  spread(key, val) %>%
  ungroup() %>%
  arrange(L5) %>%
  rename('ID' = id, 'Product Name' = productname) 

Какие выходы:

# A tibble: 5 x 7
#     ID `Product Name` L1                   L2           L3                    L4                L5           
#  <int> <chr>          <chr>                <chr>        <chr>                 <chr>             <chr>        
#1     1 ABC            A - Alimentary trac… A10 - Antid… A10B - Antidiabetics… A10BA - Biguanid… A10BA02 - Me…
#2     2 CDE            C - Cardiovascular … C01 - Cardi… C01B - Antiarythmics… C01BA - Antiaryt… C01BA02 - Pr…
#3     5 LMN            C - Cardiovascular … C01 - Cardi… C01B - Antiarythmics… C01BA - Antiaryt… C01BA02 - Pr…
#4     3 FGH            C - Cardiovascular … C03 - Diure… C03C - Diuretics str… C03CA - Sulfonam… C03CA01 - Fu…
#5     4 IJK            C - Cardiovascular … C03 - Diure… C03C - Diuretics str… C03CA - Sulfonam… C03CA03 - Pi…
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...