我有一组相当大的分隔数据,列出了与这些合成数据类似的行中的个人特征:
id; ICD_Codes
1; F10.10; F11.21; F31.81
2; G89.29; M54.5; F31.4; F11.21; F17.200; F43.10; Z72.0
3; F14.10; F17.200; F31.81; F31.32; F10.21
(是的,每行的“列”数不同)
我想从中提取一系列虚拟代码,每个
ICD_Codes
值一个,并用给定的个人是否具有该值来填充这些虚拟变量,即:
id | F10.10 | F10.21 | F11.21 | F14.10 | F17.200 | F31.32 | F31.4 | F31.81 | F43.10 | G89.29 | M54.5 | Z72.0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
3 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
或者,作为 R 数据框:
df <- data.frame(id = c(1, 2, 3),
F10.10 = c(1, 0, 0),
F10.21 = c(0, 0, 1),
F11.21 = c(1, 1, 0),
F14.10 = c(0, 0, 1),
F17.200 = c(0, 1, 1),
F31.32 = c(0, 0, 1),
F31.4 = c(0, 1, 0),
F31.81 = c(1, 0, 1),
F43.10 = c(0, 1, 0),
G89.29 = c(0, 1, 0),
M54.5 = c(0, 1, 0),
Z72.0 = c(0, 1, 0)
)
我想不出简单的方法来做到这一点(任何地方,包括在 R 中),但肯定有办法!
如果有任何帮助,我将不胜感激。
假设您还没有将数据导入 R,我们可以使用
read.table
将数据作为单列读取(选择一个您确定不会出现在数据中的随机 sep
符号)。
然后将
id
和ICD_Codes
分成两列,并使用fastDummies::dummy_cols()
创建虚拟变量。最后rename
删除列前缀以适应您想要的输出。
library(dplyr)
library(tidyr)
library(fastDummies)
df <- read.table(text = "id; ICD_Codes
1; F10.10; F11.21; F31.81
2; G89.29; M54.5; F31.4; F11.21; F17.200; F43.10; Z72.0
3; F14.10; F17.200; F31.81; F31.32; F10.21", sep = "@", header = T)
df %>%
separate_wider_delim("id..ICD_Codes", names = c("id", "ICD_Codes"), delim = "; ", too_many = "merge") %>%
dummy_cols("ICD_Codes", remove_selected_columns = T, split = ";") %>%
rename_with(~sub("ICD_Codes_", "", .x))
# A tibble: 3 × 13
id F10.10 F11.21 F31.81 F14.10 F17.200 F31.32 F10.21 G89.29 M54.5 F31.4 F43.10 Z72.0
<chr> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 1 1 1 1 0 0 0 0 0 0 0 0 0
2 2 0 1 0 0 1 0 0 1 1 1 1 1
3 3 0 0 1 1 1 1 1 0 0 0 0 0
无需使用额外的包,您可以使用
readLines
和strsplit
。然后使用 grepl
交叉 outer
。
read <- readLines(textConnection('id; ICD_Codes
1; F10.10; F11.21; F31.81
2; G89.29; M54.5; F31.4; F11.21; F17.200; F43.10; Z72.0
3; F14.10; F17.200; F31.81; F31.32; F10.21'))
read <- lapply(read, \(x) el(strsplit(x, split=';\\s*')))
icd <- sort(unique(unlist(lapply(read[-1], \(x) x[-1]))))
o <- t(outer(icd, read[-1], Vectorize(\(sprintf('^%s$', x), y) any(grepl(x, y)))))
data.frame(id=sapply(read[-1], `[`, 1), `colnames<-`(+o, icd))
# id F10.10 F10.21 F11.21 F14.10 F17.200 F31.32 F31.4 F31.81 F43.10 G89.29 M54.5 Z72.0
# 1 1 1 0 1 0 0 0 0 1 0 0 0 0
# 2 2 0 0 1 0 1 0 1 0 1 1 1 1
# 3 3 0 1 0 1 1 1 0 1 0 0 0 0
注意: 对于您的真实数据,您可能需要使用
readLines(<path>)
.
证据表明它比 tidyverse 解决方案快得多:
# Unit: microseconds
# expr min lq mean median uq max neval cld
# jay.sf 965.285 994.9295 1264.239 1089.907 1124.969 16915.81 100 a
# benson23 17309.835 17533.8145 20036.727 17822.965 18763.764 159236.18 100 b
只需使用
mtabulate
library(qdapTools)
cbind(id = sub(";.*", "", df[[1]]),
mtabulate(strsplit(sub("^\\d+;\\s+", "", df[[1]]), ";\\s*")))
-输出
id F10.10 F10.21 F11.21 F14.10 F17.200 F31.32 F31.4 F31.81 F43.10 G89.29 M54.5 Z72.0
1 1 1 0 1 0 0 0 0 1 0 0 0 0
2 2 0 0 1 0 1 0 1 0 1 1 1 1
3 3 0 1 0 1 1 1 0 1 0 0 0 0