R 将行分隔为由开始和结束指定的列
R separate lines into columns specified by start and end
我想将一个由字符串组成的数据集拆分成由开始和结束指定的列。
我的数据集看起来像这样:
>head(templines,3)
[1] "201801 1 78"
[2] "201801 2 67"
[3] "201801 1 13"
我想通过使用数据字典指定我的列来拆分它:
>dictionary
col_name col_start col_end
year 1 4
week 5 6
gender 8 8
age 11 12
所以它变成:
year week gender age
2018 01 1 78
2018 01 2 67
2018 01 1 13
实际上,数据来自长时间的 运行 调查,某些列之间的空白代表不再收集的变量。它有很多变量,所以我需要一个可以扩展的解决方案。
在 tidyr::separate
中,您似乎只能通过指定要分割的位置来分割,而不是开始和结束位置。有没有办法使用开始/结束?
我想用 read_fwf
来做这个,但我似乎无法在我已经加载的数据集上使用它。我只是设法让它工作,首先导出为 txt,然后从这个 .txt 中读取:
write_lines(templines,"t1.txt")
read_fwf("t1.txt",
fwf_positions(start = dictionary$col_start,
end = dictionary$col_end,
col_names = dictionary$col_name)
是否可以在已加载的数据集上使用 read_fwf
?
这个怎么样?
data.frame(year=substr(templines,1,4),
week=substr(templines,5,6),
gender=substr(templines,7,8),
age=substr(templines,11,13))
这是一个显式函数,它似乎按照您想要的方式工作。
split_func<-function(char,ref,name,start,end){
res<-data.table("ID" = 1:length(char))
for(i in 1:nrow(ref)){
res[,ref[[name]][i] := substr(x = char,start = ref[[start]][i],stop = ref[[end]][i])]
}
return(res)
}
我创建了与您相同的输入文件:
templines<-c("201801 1 78","201801 2 67","201801 1 13")
dictionary<-data.table("col_name" = c("year","week","gender","age"),"col_start" = c(1,5,8,11),
"col_end" = c(4,6,8,13))
# col_name col_start col_end
#1: year 1 4
#2: week 5 6
#3: gender 8 8
#4: age 11 13
至于参数,
char
- 包含要拆分的值的字符向量
ref
- 参考文献table 或字典
name
- 引用中的列号 table 包含您想要的列名
start
- 参考中的列号 table 包含起始点
end
- 参考中的列号 table 包含停止点
如果我将此函数与这些输入一起使用,我会得到以下结果:
out<-split_func(char = templines,ref = dictionary,name = 1,start = 2,end = 3)
#>out
# ID year week gender age
#1: 1 2018 01 1 78
#2: 2 2018 01 2 67
#3: 3 2018 01 1 13
我必须包含一个 "ID" 列来启动数据 table 并使这更容易。如果你想稍后删除它,你可以使用:
out[,ID := NULL]
希望这更接近您正在寻找的解决方案。
使用基数 R:
m = list(`attr<-`(dat$col_start,"match.length",dat$col_end-dat$col_start+1))
d = do.call(rbind,regmatches(x,rep(m,length(x))))
setNames(data.frame(d),dat$col_name)
year week gender age
1 2018 01 1 78
2 2018 01 2 67
3 2018 01 1 13
使用的数据:
x = c("201801 1 78", "201801 2 67", "201801 1 13")
dat=read.table(text="col_name col_start col_end
year 1 4
week 5 6
gender 8 8
age 11 13 ",h=T)
substring
的解决方案:
library(data.table)
x <- transpose(lapply(templines, substring, dictionary$col_start, dictionary$col_end))
setDT(x)
setnames(x, dictionary$col_name)
# > x
# year week gender age
# 1: 2018 01 1 78
# 2: 2018 01 2 67
# 3: 2018 01 1 13
直接回答您的问题:是的,可以对已加载的数据使用 read_fwf
。文档的相关部分是关于参数 file
:
的部分
Either a path to a file, a connection, or literal data (either a single string or a raw vector).
...
Literal data is most useful for examples and tests.
It must contain at least one new line to be recognised as data (instead of a path).
因此,您可以简单地折叠数据,然后使用 read_fwf
:
templines %>%
paste(collapse = "\n") %>%
read_fwf(., fwf_positions(start = dictionary$col_start,
end = dictionary$col_end,
col_names = dictionary$col_name))
这应该扩展到多列,并且对于多行来说速度很快(在我的机器上,100 万行和四列大约半秒)。
有一些关于解析失败的警告,但它们来自您的字典。如果将最后一行更改为 age, 11, 12
,它将按预期工作。
我们可以使用 tidyverse
中的 separate
library(tidyverse)
data.frame(Col = templines) %>%
separate(Col, into = dictionary$col_name, sep= head(dictionary$col_end, -1))
# year week gender age
#1 2018 01 1 78
#2 2018 01 2 67
#3 2018 01 1 13
convert = TRUE
参数也可以与 separate
一起使用以将数字列作为输出
tibble(Col = templines) %>%
separate(Col, into = dictionary$col_name,
sep= head(dictionary$col_end, -1), convert = TRUE)
# A tibble: 3 x 4
# year week gender age
# <int> <int> <int> <int>
#1 2018 1 1 78
#2 2018 1 2 67
#3 2018 1 1 13
数据
dictionary <- structure(list(col_name = c("year", "week", "gender", "age"),
col_start = c(1L, 5L, 8L, 11L), col_end = c(4L, 6L, 8L, 13L
)), .Names = c("col_name", "col_start", "col_end"),
class = "data.frame", row.names = c(NA, -4L))
templines <- c("201801 1 78", "201801 2 67", "201801 1 13")
我想将一个由字符串组成的数据集拆分成由开始和结束指定的列。
我的数据集看起来像这样:
>head(templines,3)
[1] "201801 1 78"
[2] "201801 2 67"
[3] "201801 1 13"
我想通过使用数据字典指定我的列来拆分它:
>dictionary
col_name col_start col_end
year 1 4
week 5 6
gender 8 8
age 11 12
所以它变成:
year week gender age
2018 01 1 78
2018 01 2 67
2018 01 1 13
实际上,数据来自长时间的 运行 调查,某些列之间的空白代表不再收集的变量。它有很多变量,所以我需要一个可以扩展的解决方案。
在 tidyr::separate
中,您似乎只能通过指定要分割的位置来分割,而不是开始和结束位置。有没有办法使用开始/结束?
我想用 read_fwf
来做这个,但我似乎无法在我已经加载的数据集上使用它。我只是设法让它工作,首先导出为 txt,然后从这个 .txt 中读取:
write_lines(templines,"t1.txt")
read_fwf("t1.txt",
fwf_positions(start = dictionary$col_start,
end = dictionary$col_end,
col_names = dictionary$col_name)
是否可以在已加载的数据集上使用 read_fwf
?
这个怎么样?
data.frame(year=substr(templines,1,4),
week=substr(templines,5,6),
gender=substr(templines,7,8),
age=substr(templines,11,13))
这是一个显式函数,它似乎按照您想要的方式工作。
split_func<-function(char,ref,name,start,end){
res<-data.table("ID" = 1:length(char))
for(i in 1:nrow(ref)){
res[,ref[[name]][i] := substr(x = char,start = ref[[start]][i],stop = ref[[end]][i])]
}
return(res)
}
我创建了与您相同的输入文件:
templines<-c("201801 1 78","201801 2 67","201801 1 13")
dictionary<-data.table("col_name" = c("year","week","gender","age"),"col_start" = c(1,5,8,11),
"col_end" = c(4,6,8,13))
# col_name col_start col_end
#1: year 1 4
#2: week 5 6
#3: gender 8 8
#4: age 11 13
至于参数,
char
- 包含要拆分的值的字符向量
ref
- 参考文献table 或字典
name
- 引用中的列号 table 包含您想要的列名
start
- 参考中的列号 table 包含起始点
end
- 参考中的列号 table 包含停止点
如果我将此函数与这些输入一起使用,我会得到以下结果:
out<-split_func(char = templines,ref = dictionary,name = 1,start = 2,end = 3)
#>out
# ID year week gender age
#1: 1 2018 01 1 78
#2: 2 2018 01 2 67
#3: 3 2018 01 1 13
我必须包含一个 "ID" 列来启动数据 table 并使这更容易。如果你想稍后删除它,你可以使用:
out[,ID := NULL]
希望这更接近您正在寻找的解决方案。
使用基数 R:
m = list(`attr<-`(dat$col_start,"match.length",dat$col_end-dat$col_start+1))
d = do.call(rbind,regmatches(x,rep(m,length(x))))
setNames(data.frame(d),dat$col_name)
year week gender age
1 2018 01 1 78
2 2018 01 2 67
3 2018 01 1 13
使用的数据:
x = c("201801 1 78", "201801 2 67", "201801 1 13")
dat=read.table(text="col_name col_start col_end
year 1 4
week 5 6
gender 8 8
age 11 13 ",h=T)
substring
的解决方案:
library(data.table)
x <- transpose(lapply(templines, substring, dictionary$col_start, dictionary$col_end))
setDT(x)
setnames(x, dictionary$col_name)
# > x
# year week gender age
# 1: 2018 01 1 78
# 2: 2018 01 2 67
# 3: 2018 01 1 13
直接回答您的问题:是的,可以对已加载的数据使用 read_fwf
。文档的相关部分是关于参数 file
:
Either a path to a file, a connection, or literal data (either a single string or a raw vector).
...
Literal data is most useful for examples and tests.
It must contain at least one new line to be recognised as data (instead of a path).
因此,您可以简单地折叠数据,然后使用 read_fwf
:
templines %>%
paste(collapse = "\n") %>%
read_fwf(., fwf_positions(start = dictionary$col_start,
end = dictionary$col_end,
col_names = dictionary$col_name))
这应该扩展到多列,并且对于多行来说速度很快(在我的机器上,100 万行和四列大约半秒)。
有一些关于解析失败的警告,但它们来自您的字典。如果将最后一行更改为 age, 11, 12
,它将按预期工作。
我们可以使用 tidyverse
separate
library(tidyverse)
data.frame(Col = templines) %>%
separate(Col, into = dictionary$col_name, sep= head(dictionary$col_end, -1))
# year week gender age
#1 2018 01 1 78
#2 2018 01 2 67
#3 2018 01 1 13
convert = TRUE
参数也可以与 separate
一起使用以将数字列作为输出
tibble(Col = templines) %>%
separate(Col, into = dictionary$col_name,
sep= head(dictionary$col_end, -1), convert = TRUE)
# A tibble: 3 x 4
# year week gender age
# <int> <int> <int> <int>
#1 2018 1 1 78
#2 2018 1 2 67
#3 2018 1 1 13
数据
dictionary <- structure(list(col_name = c("year", "week", "gender", "age"),
col_start = c(1L, 5L, 8L, 11L), col_end = c(4L, 6L, 8L, 13L
)), .Names = c("col_name", "col_start", "col_end"),
class = "data.frame", row.names = c(NA, -4L))
templines <- c("201801 1 78", "201801 2 67", "201801 1 13")