【问题标题】:Reading a .dat file in Julia, issues with variable delimeter spacing在 Julia 中读取 .dat 文件,可变分隔符间距问题
【发布时间】:2020-05-07 19:18:40
【问题描述】:

我在将 .dat 文件读入数据框时遇到问题。我认为问题在于分隔符。我已经包含了文件中数据的屏幕截图,如下所示。我最好的猜测是它在列之间用制表符分隔,然后在行之间用换行符分隔。我尝试使用以下命令读取数据:

    df = CSV.File("FORCECHAIN00046.dat"; header=false) |> DataFrame!
    df = CSV.File("FORCECHAIN00046.dat"; header=false, delim = ' ') |> DataFrame!

无论哪种方式,我的结果都只是一个 DataFrame,其中只有一列,包括连接成一个字符串的每一列的所有数据。我什至尝试使用以下代码指定类型:

df = CSV.File("FORCECHAIN00046.dat"; types=[Float64,Float64,Float64,Float64,
Float64,Float64,Float64,Float64,Float64,Float64,Float64,Float64]) |> DataFrame!

我收到以下错误:

┌ Warning: 2; something went wrong trying to determine row positions for multithreading; it'd be very helpful if you could open an issue at https://github.com/JuliaData/CS
V.jl/issues so package authors can investigate

我可以通过将其上传到谷歌表格然后下载 csv 来解决此问题,但我想找到一种方法来使原始 .dat 文件正常工作。

【问题讨论】:

  • 你试过delim=' ', ignorerepeated=true吗?
  • 可能无法正常工作,因为其中似乎也存在前导空格
  • 如果ignorerepeated=true,前导和尾随空格应该不是问题

标签: csv julia


【解决方案1】:

这里的部分问题是.dat 不是正确的文件格式——它只是似乎以某种人类可读的格式写出的,其中数字列由可变数量的空格分隔,以便数字当您在编辑器中查看它们时排队。谷歌表格内置了很多聪明的技巧来“做你想做的”来处理各种定义不明确的数据文件,所以我对它能够解析这些文件并不感到惊讶。另一方面,CSV 包支持使用单个字符作为分隔符,甚至支持多字符串,但不支持像这样可变数量的空格。

可能的解决方案:

  • 如果文件不是太大,您可以轻松滚动自己的解析器,拆分每一行,然后构建一个矩阵
  • 您还可以对文件进行预处理,将多个空格变为单个空格

这可能是最简单的方法,这里有一些 Julia 代码(未经测试,因为您没有提供测试数据)将打开您的文件并将其转换为更合理的格式:

function dat2csv(dat_path::AbstractString, csv_path::AbstractString)
    open(csv_path, write=true) do io
        for line in eachline(dat_path)
            join(io, split(line), ',')
            println(io)
        end
    end
    return csv_path
end

function dat2csv(dat_path::AbstractString)
    base, ext = splitext(dat_path)
    ext == ".dat" ||
        throw(ArgumentError("file name doesn't end with `.dat`"))
    return dat2csv(dat_path, "$base.csv")
end

您可以将此函数称为dat2csv("FORCECHAIN00046.dat"),它会创建文件FORCECHAIN00046.csv,这将是一个使用逗号作为分隔符的正确CSV 文件。如果文件中包含任何带有逗号的值,那将无法正常工作,但看起来它们只是数字,在这种情况下应该没问题。因此,您可以使用此功能将文件转换为正确的 CSV,然后使用 CSV 包加载该文件。

对代码的一点解释:

  • 两个参数dat2csv 方法打开csv_path 进行写入,然后在dat_path 上调用eachline 以一次读取一行格式
  • eachline 从每一行中删除任何尾随换行符,因此每个 line 将是由空格分隔的一堆数字,带有一些前导和/或尾随空格
  • split(line) 执行 line 的默认拆分,将其拆分为空白,删除任何空值 - 这仅将非空白条目作为字符串保留在数组中
  • join(io, split(line), ',') 将数组中的字符串连接在一起,由, 字符分隔,并将其写入io csv_path 的写入句柄
  • println(io) 之后会写一个换行符——否则所有内容都只会写在一条很长的行中
  • 单参数dat2csv 方法调用splitext 将文件名拆分为基本名称和扩展名,检查扩展名是否为预期的.dat,并使用.dat 调用双参数版本替换为.csv

【讨论】:

  • 哇,感谢您提供如此周到且有帮助的回复!它工作得很好,但只是关于它是如何工作的几个问题。我理解代码用逗号将每个换行符连接在一起,那么它如何最终用逗号连接每一列? println(io)的目的是什么。在这种情况下,它的功能是否类似于 write 语句?
  • 我将在答案中添加一些解释,而不是在这里回答。
【解决方案2】:

尝试使用DelimitedFiles库中的readdlm函数,然后转换为DataFrame:

using DelimitedFiles, DataFrames

df = DataFrame(readdlm("FORCECHAIN00046.dat"), :auto)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-03
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多