在具有复杂字符串条目的文本文件中设置什么分隔符(包括所有标准分隔符)

问题描述 投票:0回答:1

我有一个大型数据集(36 亿行,20 列,我正在处理 100 万行的可管理块)。我的单元格可以包含类似以下字符串的条目:

"sometext  "some other text"... ; , "a" some more text \t . ;"

这是一个单元格。 fread() 以某种方式设法理解下一列的开始位置,这真是太神奇了。我想将我的块保存为 txt 文件,以便任何未来的程序(实际上是箭头包)在处理此类情况时不如 fread() 更好,它将理解列的结束和开始位置,但我不想更改外部“...”内的文本。我使用不同的命令遇到很多错误,我认为这是因为所有常用的分隔符都作为字符包含在我的字符串中,但也包含“...”。我想我可以设置一个无意义的分隔符,例如“;-.-;”或者类似的东西永远不会出现在我的 3.6bnx20 细胞中。但显然它需要是一个角色。然而,每个可以想象到的字符都会在某个时刻出现在我的 36 亿行中的某个位置。如何在这样的数据集中设置分隔符?

p.s.:我猜一个恰当的例子是 stackoverflows 着色方案无法识别上面的单元格应该是一个字符串;)

r fread
1个回答
0
投票

您的文字可能被破坏了

我不会认为仅仅因为列位于正确的位置,读取和写入 csv 就不会破坏文本。让我们创建一些包含两列的示例数据。第一个是您添加了一些随机 Unicode 字符和表情符号的字符串,第二个是对这些字符进行随机采样:

library(data.table)
str <- '"sometext \U002 \U003 😊 🙄 "some other text"... ; , "a" some more text \t . ;"'
set.seed(2024); N_ROWS <- 100
dat <- data.frame(
    col1 = rep(str, N_ROWS),
    col2 = sapply(seq(N_ROWS), \(., x = str) paste0(sample(strsplit(x, "")[[1]], nchar(x)), collapse = ""))
)

如果我们将其写入文件并将其读回,看起来可能没问题:

fwrite(dat, "./tmp.csv")
dat_dt <- fread("./tmp.csv", data.table = FALSE)
names(dat) # [1] "col1" "col2"
names(dat_dt) # [1] "col1" "col2"

但是,虽然列数是正确的,但使用默认的

fread()
fwrite()
选项,引号现在加倍:

print(dat$col1[1], quote = FALSE)
# [1] "sometext \001 \002 \003 😊 🙄 "some other text"... ; , "a" some more text \t . ;"
print(dat_dt$col1[1], quote = FALSE)
# [1] ""sometext \001 \002 \003 😊 🙄 ""some other text""... ; , ""a"" some more text \t . ;""
identical(dat_dt, dat) # FALSE

找到您可以使用的分隔符

解决此问题的最佳方法是使用 csv 以外的格式,例如 Parquet。但是,如果您必须使用纯文本格式,那么您需要一个分隔符,该分隔符要么不在文本中,要么可以在文本中替换。

在此示例中,我将使用

\U001
。这是标题开始字符,它是不可打印且已过时,因此不太可能出现在您的数据中,如果存在,则删除它应该没有问题。万一您需要此字符,Unicode 有大约一百万其他字符您可以选择用于相同目的。

这里有一个函数,可以从数据中删除

"\U001"
,然后使用它作为分隔符保存文件:

save_clean <- function(df, outfile = "./tmp.csv", sep = "\U001", repl = "") {
    char_cols <- names(df)[sapply(df, is.character)]
    df[char_cols] <- lapply(df[char_cols], \(x) gsub(sep, repl, x))
    fwrite(df, outfile, sep = sep, quote = FALSE)
}

这将允许您保存和读取文件:

save_clean(dat)
dat_dt <- fread("./tmp.csv", sep = "\U001", data.table = FALSE, quote = "", strip.white = FALSE)
identical(dat, dat_dt) # TRUE

这也适用于基础 R 和

readr
:

dat_base <- read.csv("./tmp.csv", sep = "\U001", quote = "")
dat_readr <- readr::read_delim("./tmp.csv", delim = "\U001", quote = "") |>
    data.frame()
identical(dat, dat_base) # [1] TRUE
identical(dat, dat_readr) # [1] TRUE

如果您的文本可能以您想要保留的空格开头,那么这样做很重要

quote = ""
strip.white=FALSE

角色选择注意事项

我认为这可能效率低下,因为当我怀疑数据中出现

gsub()
时,它会浪费时间与
"\U001"
。我建议你提前流一下文件,看看是否需要替换角色。在 Linux/Mac 上:

grep -P -l '\x01' *.csv

或者在 Windows PowerShell 上(可能有更优雅的方式):

Get-ChildItem -Path . -Filter *.csv -Recurse | ForEach-Object {
    if (Select-String -Path $_.FullName -Pattern ([char]0x01) -Quiet) {
        $_.FullName
    }
}

这些命令将返回包含该字符的 csv 文件列表。如果没有,您可以使用

fwrite(df, "./tmp.csv", sep = "\U0001", quote = FALSE)
正常保存并跳过可能昂贵的替换。

© www.soinside.com 2019 - 2024. All rights reserved.