我有一个非常大的数据集,包含许多列,从应用程序导出。问题是该文件是“空字符”分开。使用readLines读取文件会生成一个字符串列表,每个字符串具有相同的字符数。
确定列位置的一种可能方法是检查每个字符串(在位置5处)是否具有空字符。因此,可以继续从向量1开始搜索,直到找到非空字符。
d <- data.frame("V1" = c(" f ggh", "aa hh", "a qq" ), stringsAsFactors =
F)
first.char <- function(col){
current <- 0
j <- 1
while(j <= length(d)){
tmp <- substr(d[j], col, col)
if(!grepl("^\\s*$", tmp)){
current <- 1
break}
j <- j+1
}
return(current)
}
row_dummies <- lapply( c(1:6), first.char) %>% unlist
这种方法有效但扩展时非常慢(有一百万个字符串的列表,每个字符长1500个字符)。我还试图将每个向量转换为data.table然后使用str split(Split text string in a data.table columns),但这似乎更低效,因为在大多数情况下,没有必要检查所有行。
有什么建议或意见吗?
更新:上面的例子太微不足道了。这个更好一点:
text <- c("df ggh a a h h a qq",
" aa hh ab qt",
" fggh aa hh a ")
期望的输出是
list( c("df ggh", "a a", "h h", "a", "qq"),
c(NA, "aa", "hh", "ab", "qq"),
c(" fggh", "aa", "hh", "a", NA)
)
str_locate_all运行良好,因为它指示拆分字符串的位置:
cuts_in <- sapply(text, function(x) x %>% str_locate_all(. , "\\s") )
cuts_in <- lapply(cuts_in, data.table) # to data.table
cuts_in <- rbindlist(cuts_in)
cuts_in <- cuts_in[, .N, by=start]
cuts_in[ N==3 ,"start"]
start
1: 7
2: 11
3: 15
4: 18
但是,可能不是最有效的方法(有15个文件,每个文件100万行,每行有1500个字符)。例如,假设第1行位置1不是空格,则不需要检查第2行和第3行中第1位的字符。 read_table2似乎也不是解决方案:
read_table2(text, col_names = FALSE)
X1 X2 X3 X4 X5 X6 X7 X8
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 df ggh a a h h a qq
2 aa hh ab qt NA NA NA NA
3 fggh aa hh a NA NA NA NA
您实际上遇到的情况是您需要读取固定宽度的文件,而不知道列的位置,这是我之前不了解的。您可以尝试使用readr::read_fwf
。 fwf_empty
将查看某些行,默认为100,并尝试找出相交列的位置。根据您希望在1500个字符中包含的列数,您可能需要增加n才能获得正确的输出。
library(tidyverse)
text <- c("df ggh a a h h a qq",
" aa hh ab qt",
" fggh aa hh a ")
read_fwf(text, fwf_empty(text, n = 100))
#> # A tibble: 3 x 5
#> X1 X2 X3 X4 X5
#> <chr> <chr> <chr> <chr> <chr>
#> 1 df ggh a a h h a qq
#> 2 <NA> aa hh ab qt
#> 3 fggh aa hh a <NA>
或者,如果您已经在使用str_locate_all
并想要查看所有线条,您可以将结果位置转换为宽度以与fwf_widths
一起使用,方法是添加起点和终点并获取差异。请注意,您不需要将sapply
与str_locate_all
一起使用,它已经被矢量化了。这可能会慢,因为它检查每一行,如果你没有得到正确的输出,我会先尝试增加n
。
locations <- text %>%
str_locate_all("\\s") %>%
map(~.[, 1]) %>%
reduce(intersect)
widths <- c(1, locations, str_length(text[1])) %>% diff()
read_fwf(text, fwf_widths(widths))
#> # A tibble: 3 x 5
#> X1 X2 X3 X4 X5
#> <chr> <chr> <chr> <chr> <chr>
#> 1 df ggh a a h h a q
#> 2 <NA> aa hh ab q
#> 3 fggh aa hh a <NA>
由reprex package创建于2019-04-18(v0.2.1)
来自str_locate_all
的stringr
怎么样:
library(stringr)
d <- data.frame("V1" = c(" f ggh", "aa hh", "a qq" ), stringsAsFactors =
F)
str_locate_all(d$V1, "\\s")
[[1]]
start end
[1,] 1 1
[2,] 3 3
[[2]]
start end
[1,] 3 3
[[3]]
start end
[1,] 2 2
[2,] 3 3
但是如果你想把它分成不同的列,你可以使用dplyr
和tidyr
的组合来同时完成所有操作。
library(tidyverse)
d %>%
mutate(V1 = str_trim(V1, side = "both")) %>%
separate(V1, c("string_1", "string_2"), sep = "\\s+")
string_1 string_2
1 f ggh
2 aa hh
3 a qq