Splicing/cleaning 数据放入整齐的数据框
Splicing/cleaning data into neat dataframe
我是 R 的新手(以及 Whosebug,因此项目符号仅代表新行)并且被指派从事一个项目,在该项目中我需要将 MEDLINE 数据清理成一个整洁的数据框。原始 .txt 文件的示例如下:
PMID- 28152974
OWN - NLM
IS - 1471-230X (Electronic)
IS - 1471-230X (Linking)
PMID- 28098115
OWN - NLM
IP - 1
VI - 28
等等
每个新的观察值都以PMID开头,并不是所有的变量都包含在每个观察值中,并且需要合并同一个观察值中具有相同列名的一些单元格(即IS)。最终数据框应如下所示:
PMID OWN IS VI
28152974 NLM 1471-230X (Electronic) 1471-230X (Linking) N/A
28098115 NLM N/A 28
等等
目前我以多种方式操纵我的数据。第一个是原始数据文件的格式,但在两列中,没有“-”。例如:
PMID 28152974
OWN NLM
IS 1471-230X (Electronic)
IS 1471-230X (Linking)
PMID 28098115
OWN NLM
IP 1
VI 28
等等
第二个是所有观察结果都在一行中,每个变量有数千列。例如:
PMID OWN IS IS PMID OWN
28152974 NLM 1471-230X (Electronic) 1471-230X (Linking) 28098115 NLM
等等
第三个与第二个类似,但它没有数千列,它仅具有与第一个 PMID 值不同的列类型。例如:
PMID OWN IS
28152974 28098115 NLM NLM 1471-230X (Electronic) 1471-230X (Linking)
等等
请帮忙。我不知道如何拼接我的数据,也不知道我应该使用哪种操作。
可重现的数据:
d <- c("PMID- 28152974", "OWN - NLM", "IS - 1471-230X (Electronic)",
"IS - 1471-230X (Linking)", "PMID- 28098115", "OWN - NLM", "IP - 1",
"VI - 28")
来自文件的输入:
d <- readLines('/path/to/file')
一个想法:
# split into records
i <- grepl("^PMID", d)
i <- cumsum(i)
d <- split(d, i)
# split into key-value pairs
d <- lapply(d, strsplit, "\ {0,2}-\ ")
d <- lapply(d, function (x) setNames(sapply(x, '[[', 2), sapply(x, '[[', 1)))
# merge IS variables
d <- lapply(d, function (x) {
i <- names(x) == "IS"
if (any(i))
x <- c(x[!i], IS = paste(x[i], collapse = " "))
return(x)
})
# merge records to data.frame
library(data.table)
d <- lapply(d, as.list)
d <- lapply(d, as.data.table)
d <- rbindlist(d, fill = T)
d <- as.data.frame(d)
不同种类的数据混合在数据文件的一列或两列中的情况并不少见。只要可以通过某种方式(例如通过正则表达式)识别不同类型的数据,就可以将行的内容移动到不同的列。
以下解决方案使用包readr
中的read_fwf()
从文本文件中读取固定宽度的数据(这里通过读取字符串来模拟)。 data.table
包中的 dcast()
用于从长格式重塑为宽格式,从而产生每行一条记录的 data.frame:
读取数据
library(data.table)
# read data
dt <- readr::read_fwf(
" PMID- 28152974
OWN - NLM
IS - 1471-230X (Electronic)
IS - 1471-230X (Linking)
PMID- 28098115
OWN - NLM
IP - 1
VI - 28 ",
readr::fwf_positions(c(2, 8), c(5, Inf), c("variable", "value")),
col_types = "cc")
# coerce tibble to data.table
setDT(dt)
从长格式重塑为宽格式
# create new column PMID with the record id
dt[variable == "PMID", PMID := value]
# fill missing values in subsequent rows to mark all rows belonging to one record
dt[, PMID := zoo::na.locf(PMID)]
dt
# variable value PMID
#1: PMID 28152974 28152974
#2: OWN NLM 28152974
#3: IS 1471-230X (Electronic) 28152974
#4: IS 1471-230X (Linking) 28152974
#5: PMID 28098115 28098115
#6: OWN NLM 28098115
#7: IP 1 28098115
#8: VI 28 28098115
# reshape from wide to long, thereby collapsing strings if necessary
dcast(dt[variable != "PMID"], PMID ~ ..., fun = paste, collapse = " ")
# PMID IP IS OWN VI
#1: 28098115 1 NLM 28
#2: 28152974 1471-230X (Electronic) 1471-230X (Linking) NLM
请注意,这种方法非常灵活,因为它折叠 所有重复的 数据字段,如果它们出现在数据中,无论它们如何命名,而不仅仅是 IS
列。
来自 base R 的 Thanks to G. Grothendieck, I learned about the read.dcf()
function 大大简化了这项任务。只需要进行一些小的调整。
# use connection to avoid warnings
con <- file("test.dat")
# read file row-wise and adjust for dcf format
dat <- sub("PMID", "\nPMID", sub("- ", ": ", trimws(readLines(con))))
# close connection to avoid warnings
close(con)
# re-read from variable using dcf format, collapse multiple entries
result <- read.dcf(textConnection(dat), all = TRUE)
result
PMID OWN IS IP VI
1 28152974 NLM 1471-230X (Electronic), 1471-230X (Linking) <NA> <NA>
2 28098115 NLM NA 1 28
我是 R 的新手(以及 Whosebug,因此项目符号仅代表新行)并且被指派从事一个项目,在该项目中我需要将 MEDLINE 数据清理成一个整洁的数据框。原始 .txt 文件的示例如下:
PMID- 28152974
OWN - NLM
IS - 1471-230X (Electronic)
IS - 1471-230X (Linking)
PMID- 28098115
OWN - NLM
IP - 1
VI - 28
等等
每个新的观察值都以PMID开头,并不是所有的变量都包含在每个观察值中,并且需要合并同一个观察值中具有相同列名的一些单元格(即IS)。最终数据框应如下所示:
PMID OWN IS VI
28152974 NLM 1471-230X (Electronic) 1471-230X (Linking) N/A
28098115 NLM N/A 28
等等
目前我以多种方式操纵我的数据。第一个是原始数据文件的格式,但在两列中,没有“-”。例如:
PMID 28152974
OWN NLM
IS 1471-230X (Electronic)
IS 1471-230X (Linking)
PMID 28098115
OWN NLM
IP 1
VI 28
等等
第二个是所有观察结果都在一行中,每个变量有数千列。例如:
PMID OWN IS IS PMID OWN
28152974 NLM 1471-230X (Electronic) 1471-230X (Linking) 28098115 NLM
等等
第三个与第二个类似,但它没有数千列,它仅具有与第一个 PMID 值不同的列类型。例如:
PMID OWN IS
28152974 28098115 NLM NLM 1471-230X (Electronic) 1471-230X (Linking)
等等
请帮忙。我不知道如何拼接我的数据,也不知道我应该使用哪种操作。
可重现的数据:
d <- c("PMID- 28152974", "OWN - NLM", "IS - 1471-230X (Electronic)",
"IS - 1471-230X (Linking)", "PMID- 28098115", "OWN - NLM", "IP - 1",
"VI - 28")
来自文件的输入:
d <- readLines('/path/to/file')
一个想法:
# split into records
i <- grepl("^PMID", d)
i <- cumsum(i)
d <- split(d, i)
# split into key-value pairs
d <- lapply(d, strsplit, "\ {0,2}-\ ")
d <- lapply(d, function (x) setNames(sapply(x, '[[', 2), sapply(x, '[[', 1)))
# merge IS variables
d <- lapply(d, function (x) {
i <- names(x) == "IS"
if (any(i))
x <- c(x[!i], IS = paste(x[i], collapse = " "))
return(x)
})
# merge records to data.frame
library(data.table)
d <- lapply(d, as.list)
d <- lapply(d, as.data.table)
d <- rbindlist(d, fill = T)
d <- as.data.frame(d)
不同种类的数据混合在数据文件的一列或两列中的情况并不少见。只要可以通过某种方式(例如通过正则表达式)识别不同类型的数据,就可以将行的内容移动到不同的列。
以下解决方案使用包readr
中的read_fwf()
从文本文件中读取固定宽度的数据(这里通过读取字符串来模拟)。 data.table
包中的 dcast()
用于从长格式重塑为宽格式,从而产生每行一条记录的 data.frame:
读取数据
library(data.table)
# read data
dt <- readr::read_fwf(
" PMID- 28152974
OWN - NLM
IS - 1471-230X (Electronic)
IS - 1471-230X (Linking)
PMID- 28098115
OWN - NLM
IP - 1
VI - 28 ",
readr::fwf_positions(c(2, 8), c(5, Inf), c("variable", "value")),
col_types = "cc")
# coerce tibble to data.table
setDT(dt)
从长格式重塑为宽格式
# create new column PMID with the record id
dt[variable == "PMID", PMID := value]
# fill missing values in subsequent rows to mark all rows belonging to one record
dt[, PMID := zoo::na.locf(PMID)]
dt
# variable value PMID
#1: PMID 28152974 28152974
#2: OWN NLM 28152974
#3: IS 1471-230X (Electronic) 28152974
#4: IS 1471-230X (Linking) 28152974
#5: PMID 28098115 28098115
#6: OWN NLM 28098115
#7: IP 1 28098115
#8: VI 28 28098115
# reshape from wide to long, thereby collapsing strings if necessary
dcast(dt[variable != "PMID"], PMID ~ ..., fun = paste, collapse = " ")
# PMID IP IS OWN VI
#1: 28098115 1 NLM 28
#2: 28152974 1471-230X (Electronic) 1471-230X (Linking) NLM
请注意,这种方法非常灵活,因为它折叠 所有重复的 数据字段,如果它们出现在数据中,无论它们如何命名,而不仅仅是 IS
列。
Thanks to G. Grothendieck, I learned about the read.dcf()
function 大大简化了这项任务。只需要进行一些小的调整。
# use connection to avoid warnings
con <- file("test.dat")
# read file row-wise and adjust for dcf format
dat <- sub("PMID", "\nPMID", sub("- ", ": ", trimws(readLines(con))))
# close connection to avoid warnings
close(con)
# re-read from variable using dcf format, collapse multiple entries
result <- read.dcf(textConnection(dat), all = TRUE)
result
PMID OWN IS IP VI 1 28152974 NLM 1471-230X (Electronic), 1471-230X (Linking) <NA> <NA> 2 28098115 NLM NA 1 28