У меня есть набор данных в R с беспорядочными данными, где информация о тестах каждого человека хранится в отдельных соответствующих столбцах: имя_теста, результат_теста и статус_теста. Все три столбца содержат значения, разделенные запятыми, для каждого теста, если было выполнено более одного теста. Я хочу извлечь отдельные тесты, соответствующие им результаты и статусы для каждого человека наиболее эффективным способом. Моя конечная цель — иметь возможность проводить различные тесты, их результаты и статус для каждого человека. Это образец данных.
dput(tb_sample_10)
structure(list(test_name = c("TB Direct PCR & RIF Status,AFB Smear",
"AFB Smear,Sensitivity,TB Direct PCR & RIF Status,TB Indirect PCR & RIF Status,Culture",
"Culture,AFB Smear,Sensitivity,TB Direct PCR & RIF Status,TB Indirect PCR & RIF Status",
"AFB Smear", "Culture,AFB Smear,Sensitivity,TB Direct PCR & RIF Status,TB Indirect PCR & RIF Status",
"Culture,AFB Smear,Sensitivity,TB Direct PCR & RIF Status,TB Indirect PCR & RIF Status",
"AFB Smear,TB Direct PCR & RIF Status", "Culture,AFB Smear,Sensitivity,TB Direct PCR & RIF Status,TB Indirect PCR & RIF Status",
"AFB Smear,TB Direct PCR & RIF Status", "TB Direct PCR & RIF Status,Culture"
), test_result = c("MTB Detected / RIF Resistance Not Detected,No AFB Seen",
"OTHER,Not Required,MTB Detected / RIF Resistance Not Detected,Not Required,No Growth",
"Positive,No AFB Seen,Processed,Rejected,MTB Detected / RIF Resistance Not Detected (MTBC)",
"AFB+", "Positive,OTHER,Processed,MTB Not Detected,Not Required",
"Positive,AFB+,Not Required,MTB Detected / RIF Resistance Not Detected,Not Required",
"No AFB Seen,MTB Detected / RIF Resistance Not Detected", "No Growth,No AFB Seen,Not Required,MTB Detected / RIF Resistance Not Detected,Not Required",
"No AFB Seen,MTB Not Detected", "MTB Detected / RIF Resistance Not Detected,Positive"
), test_status = c("Final,Final", "Final,Final,Final,Final,Final",
"Final,Final,Final,Final,Final", "Final", "Final,Final,Final,Final,Final",
"Final,Final,Final,Final,Final", "Final,Final", "Final,Final,Final,Final,Final",
"Final,Final", "Final,Final")), row.names = c(NA, -10L), class = c("tbl_df",
"tbl", "data.frame"))





Использование функций tidyverse согласно тегу вопроса:
library(dplyr)
library(tidyr)
tb_sample_10 %>%
mutate(ID = row_number()) %>%
rowwise() %>%
transmute(ID = ID,
names = list(strsplit(test_name, ",")),
results = list(strsplit(test_result, ",")),
statuses = list(strsplit(test_status, ","))) %>%
unnest(cols = c(names, results, statuses)) %>%
unnest(cols = c(names, results, statuses))
# A tibble: 34 × 4
ID names results statuses
<int> <chr> <chr> <chr>
1 1 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
2 1 AFB Smear No AFB Seen Final
3 2 AFB Smear OTHER Final
4 2 Sensitivity Not Required Final
5 2 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
6 2 TB Indirect PCR & RIF Status Not Required Final
7 2 Culture No Growth Final
8 3 Culture Positive Final
9 3 AFB Smear No AFB Seen Final
10 3 Sensitivity Processed Final
11 3 TB Direct PCR & RIF Status Rejected Final
12 3 TB Indirect PCR & RIF Status MTB Detected / RIF Resistance Not Detected (MTBC) Final
13 4 AFB Smear AFB+ Final
14 5 Culture Positive Final
15 5 AFB Smear OTHER Final
16 5 Sensitivity Processed Final
17 5 TB Direct PCR & RIF Status MTB Not Detected Final
18 5 TB Indirect PCR & RIF Status Not Required Final
19 6 Culture Positive Final
20 6 AFB Smear AFB+ Final
# ℹ 14 more rows
Обратите внимание: я создал столбец ID просто для того, чтобы следить за происходящим. Возможно, он уже есть в ваших данных, или вы все равно можете обойтись без него.
Вы можете использовать str_split, enframe и unnest, чтобы разделить три объекта. Затем объедините их, используя inner_join.
library(dplyr); library(tidyr); library(stringr)
tests <- str_split(tb_sample_10$test_name, pattern = ",") |>
enframe(name = "id", value = "test") |>
unnest(test) |>
mutate(seq=row_number(), .by=id) |>
select(id, seq, test)
results <- str_split(tb_sample_10$test_result, pattern = ",") |>
enframe(name = "id", value = "result") |>
unnest(result) |>
mutate(seq=row_number(), .by=id)
statuses <- str_split(tb_sample_10$test_status, pattern = ",") |>
enframe(name = "id", value = "status") |>
unnest(status) |>
mutate(seq=row_number(), .by=id)
Reduce(inner_join, list(tests, results, statuses))
# A tibble: 34 x 5
id seq test result status
<int> <int> <chr> <chr> <chr>
1 1 1 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
2 1 2 AFB Smear No AFB Seen Final
3 2 1 AFB Smear OTHER Final
4 2 2 Sensitivity Not Required Final
5 2 3 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
6 2 4 TB Indirect PCR & RIF Status Not Required Final
7 2 5 Culture No Growth Final
8 3 1 Culture Positive Final
9 3 2 AFB Smear No AFB Seen Final
10 3 3 Sensitivity Processed Final
# i 24 more rows
Вы можете сделать это с помощью tidyr::separate_longer_delim() . Это было выпущено в tidyr 1.3.0 (январь 2023 г.):
Самая большая особенность этого выпуска — новое экспериментальное семейство функций для разделения строковых столбцов.
Эта функция отмечена жизненным циклом экспериментальная, что означает, что долгосрочная стабильность не обещается. Тем не менее, он существует уже больше года и, похоже, создан именно для вашего случая.
Я также добавил столбец id, который не нужен для разделения строк, но может быть полезен для отслеживания того, к какой из исходных 10 строк относится каждая из теперь 34 строк.
tb_sample_10 |>
dplyr::mutate(id = dplyr::row_number(), .before = test_name) |>
tidyr::separate_longer_delim(
cols = dplyr::starts_with("test"),
delim = ","
)
# # A tibble: 34 × 4
# id test_name test_result test_status
# <int> <chr> <chr> <chr>
# 1 1 TB Direct PCR & RIF Status MTB Detected / RIF Resistance… Final
# 2 1 AFB Smear No AFB Seen Final
# 3 2 AFB Smear OTHER Final
# 4 2 Sensitivity Not Required Final
# 5 2 TB Direct PCR & RIF Status MTB Detected / RIF Resistance… Final
# 6 2 TB Indirect PCR & RIF Status Not Required Final
# 7 2 Culture No Growth Final
# 8 3 Culture Positive Final
# 9 3 AFB Smear No AFB Seen Final
# 10 3 Sensitivity Processed Final
# # ℹ 24 more rows
# # ℹ Use `print(n = ...)` to see more rows
Используя data.table, разделите столбцы 1, 2 и 3 запятыми и добавьте новую строку. Обратите внимание, что я добавляю столбец I, чтобы отслеживать идентификаторы строк.
tb_sample_10[, lapply(.SD, \(i) unlist(tstrsplit(i, ","))), by = .I, .SDcols = 1:3 ]
# I test_name test_result test_status
# <int> <char> <char> <char>
# 1: 1 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
# 2: 1 AFB Smear No AFB Seen Final
# 3: 2 AFB Smear OTHER Final
# 4: 2 Sensitivity Not Required Final
# 5: 2 TB Direct PCR & RIF Status MTB Detected / RIF Resistance Not Detected Final
# 6: 2 TB Indirect PCR & RIF Status Not Required Final
# 7: 2 Culture No Growth Final
# 8: 3 Culture Positive Final
# 9: 3 AFB Smear No AFB Seen Final
#10: 3 Sensitivity Processed Final