У меня есть 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'))
Большое спасибо за любую помощь!