8 个版本 (4 个重大更改)

0.5.0 2023 年 8 月 22 日
0.4.0 2021 年 7 月 19 日
0.3.0 2019 年 4 月 29 日
0.2.0 2019 年 3 月 21 日
0.1.1 2019 年 1 月 7 日

#222 in 数据结构

MIT/Apache

130KB
2K SLoC

Rust 数据读取器

到目前为止,此代码提供了与 Numpy 的 loadtxt 相似的功能。您可以在 doc.rs 的文档中了解更多信息。它目前旨在读取已知如何生成的数据。默认分隔符是任何空白字符。目前存在以下限制:

  1. 不计算在新行和注释行中的行数。
  2. 如果代码无法将字符串转换为支持的类型,则将失败。
  3. 从分隔符之间的任何字符串的前后都去除了空白字符。
  4. 所有读取的数据在转换为该类型时都需要是同一类型。

它支持以下原始类型:

u8 u16 u32 u64 u128 usize
i8 i16 i32 i64 i128
f32 f64
char bool String

原始 uint 和 int 使用 lexical crate 提供从字符串到给定类型的更快转换。浮点数使用 fast-float crate 从字符串转换为给定类型。其他类型使用内置的标准库 from_str 转换。读取的数据全部存储到向量中。从 load_text_* 方法返回一个结构体,该结构体提供读取的行数、从数据中读取的列数以及包含数据的向量。此结构体被包装在一个返回给用户的 Result 中。对于从 SSD 读取的 1GB float64 类型文件,我能够获得 190MB/s 的读取速度。

如果您感兴趣的类型支持 FromStr 特性,您还可以使用此 crate。下面的示例显示了如何使用 load_txt! 宏来加载自定义数据类型。

路线图

研究获取更大性能提升的读取大文件的方法。

示例

以下是如何使用代码的示例

extern crate anyhow;
#[macro_use]
extern crate data_reader;
use data_reader::reader::*;
use anyhow::Error;

use std::str;
use std::str::FromStr;
use std::vec::*;

//This example shows us how we might skip a footer file
fn load_txt_i32_test_sk_f(){
    //The file here is the one included in the main folder.
    let file = String::from("int_testv2.txt");

    //A default constructor could look like this:
    //let params = ReaderParams::default();
    //The below could also look like the following:
    //let params = ReaderParams{
    //     comments: Some(b'%'),
    //     skip_footer: Some(5),
    //     ..Default::default()
    //};
    let params = ReaderParams{
        comments: Some(b'%'),
        delimiter: Delimiter::WhiteSpace,
        skip_header: None,
        skip_footer: Some(5_usize),
        usecols: None,
        max_rows: None,
        ..Default::default()
    };

    let results = load_txt_i32(&file, &params);

    // Pattern matching for our results could look something like this.
    // match results{
    //     Ok(results) => println!("Number of lines {}\nNumber of fields {}\nResults {:?}",results.num_lines, results.num_fields, results.results),
    //     Err(err) => println!("Error {:?}", err),
    // }

    assert_eq!(results.unwrap().results, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);

}

以下是使用自定义类型的更详细示例。

extern crate anyhow;
#[macro_use]
extern crate data_reader;
use data_reader::reader::*;
use anyhow::Error;

use std::str;
use std::str::FromStr;
use std::vec::*;

//Everything needed for our custom type
#[derive(Debug, PartialEq, Clone)]
struct MinInt{
    x: i32,
}
//A simple example of implementing the FromStr trait for our custom type
impl FromStr for MinInt{
    type Err = Error;

    fn from_str(s: &str) -> Result<MinInt, anyhow::Error> {
        let temp = -1 * i32::from_str(s)?;
        Ok(MinInt{x: temp})
    }
}

//The test file for this has 0 commented lines in it but using a custom type
//The returned error is needed if we doing anything that's not in a function
fn load_txt_custom_test() -> Result<(), anyhow::Error> {
    let file = String::from("int_testv2.txt");

    let params = ReaderParams {
        comments: Some(b'%'),
        delimiter: Delimiter::WhiteSpace,
        skip_header: None,
        skip_footer: None,
        usecols: None,
        max_rows: None,
    };

    let ref_file = &file;
    let ref_params = &params;

    //I found the type annotation was needed for this to compile
    let results: Result<Box<dyn ReaderResults<MinInt>>, Error> = load_text!(ref_file, ref_params, MinInt);

    let temp = results.unwrap().results.clone();

    let vals: Vec<i32> = temp.iter().map(|x| x.x).collect();

    assert_eq!(
        vals,
        vec![
            -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14,
            -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27,
            -28, -29, -30
        ]
    );

    Ok(())
}

版本

  • 0.5.0 - 从failure crate迁移到anyhow。更新了lexical crate到6.0版本,这使得我们可以摆脱对fast-float crate的依赖,因为在该crate中的性能提升已经基本迁移到lexical。更新memmap2 crate到0.5.0版本。对于最终用户存在一些更改。首先,如果之前使用了这些,他们需要将failure::Error替换为anyhow::Error。接下来,返回的结果现在是Box::<dyn ReaderResults<T>>。现在parse_text函数需要用户提供实现了trait RawReaderParse的类型,例如parse_txt::<RawReaderResultsRows>,这是旧的默认方法。这些更改是为了让用户现在可以选择以行主序或列主序解析数据。作为这些更改的一部分,ReaderParams中添加了一个新的字段row_format。它默认为true,这将导致数据的行主序排序。现在ReaderResults*包含了许多在它们上实现的有用函数,允许直接操作或获取对用户有意义的部分数据。

  • 0.4.0 - 将UseCols更新为从0开始。更新了几个面向公共的函数以接受不同类型。在功能标志后添加了解析器的mmap版本。更新了多个crate,并将浮点解析后端从lexical更改为fast-float crate,以实现性能的大幅提升(在我的机器上从135MB/s提升到190MB/s)。向ReaderResults结构添加了一些函数,允许用户提取指定的行或列。

  • 0.3.0 - 注意到ReaderParams结构体中use_cols字段的bug,允许输入不可用的值。此外,更新了ReaderParams注释字段,使其成为可选的。还添加了额外的文档,指出use_cols字段假定值从索引1开始。

  • 0.2.0 - 添加了一个新的解析后端,它在大型的1GB f64文件中解析/读取速度提高了40%。将解析器暴露给最终用户,以便用户可以处理原始字节。现在任何支持FromStr trait的类型都可以进行转换。

  • 0.1.3 - 更新了代码,以提供在lexical crate v2.0中的bug修复。

  • 0.1.2 - 更新了代码中的注释和换行跟踪部分。现在代码可以正确跳过以空白字符开始的注释行和换行行,而且不会将包含多个注释字符的行计数多次。由于正确处理这些情况,从0.1.1和0.1.0版本中产生了性能回归。

  • 0.1.1 - 需要更新docs.rs中的文档。

  • 0.1.0 - 初始crates.io版本

依赖关系

~1MB
~16K SLoC