DataFrame C++

一、说明

项目地址:https://github.com/hosseinmoein/DataFrame
使用教程:查看

DataFrame 是一个模板化的异构C++容器,专为统计、机器学习或金融应用程序的数据分析而设计。

  • 一个数据帧可以有一个索引列和许多任何内置或用户定义类型的数据列
  • 数据帧中的每一列最多可以与索引列一样长
  • 数据帧中的列按创建顺序排列,可以按名称或索引访问它们。如果旋转列,其顺序会更改
  • 若要访问任何操作的列,必须在编译时知道其名称(或索引)及其类型
  • 要开始基本操作,请参阅 Hello World
  • 数据帧具有同步和异步接口,后者返回C++ std::futures
  • 在认真了解此库之前,请阅读下面的视图、访问者、多线程和内存对齐部分

二、数据类型

DataFrame 实例 公共类型描述
ViewDataFrame 视图。这是对另一个相同类型的 DataFrane 的连续切片的视图。 可在此视图中读取和更改数据。
ConstView常量DataFrame 视图。这是对另一个相同类型的 DataFrane 的连续切片的视图。 一个只读视图
PtrViewDataFrame 视图。这是对另一个相同类型的 DataFrane 的分离切片的视图。 可在此视图中读取和更改数据。
ConstPtrView常量DataFrame 视图。这是对另一个相同类型的 DataFrane 的分离切片的视图。 一个只读视图
align_value一个整数值,指定DataFrame 类型的内存分配中的字节对齐边界。
template<typename T> AllocatorType用于为此类型的DataFrame 分配内存的分配器类型。 它是 std::allocator 或自定义分配器,用于在自定义字节边界上分配内存。
size_typestd::size_t
IndexType此DataFrame 的索引列类型
IndexVecType用于索引列的向量类型。它是 std::vector 或矢量视图之一,具体取决于这是DataFrame 还是DataFrame 视图。 此外,分配器取决于align_value。
template<typename T> ColumnVecType用于数据列的向量类型。它是 std::vector 或矢量视图之一,具体取决于这是DataFrame 还是DataFrame 视图。 此外,分配器取决于align_value
template<typename T> StlVecType具有与此DataFrame 类型兼容的分配器的 stl::vector 类型。
库范围类型描述
template<typename I> StdDataFrame索引类型 I 的DataFrame ,它使用系统默认字节边界进行内存分配。
template<typename I> StdDataFrame64索引类型 I 的DataFrame ,使用 64 字节边界进行内存分配。
template<typename I> StdDataFrame128索引类型 I 的DataFrame ,使用 128 字节边界进行内存分配。
template<typename I> StdDataFrame256索引类型 I 的DataFrame ,使用 256 字节边界进行内存分配。
template<typename I> StdDataFrame512索引类型 I 的DataFrame ,使用 512 字节边界进行内存分配。
template<typename I> StdDataFrame1024索引类型 I 的DataFrame ,使用 1024 字节边界进行内存分配。
template<typename T> struct Index2D { T begin {}; T end {}; };它表示在连续内存空间内开始和结束的范围
DF_INDEX_COL_NAME一个常量字符 名称,通常指索引列。
DataFrame 异常描述
struct DataFrameError{ }它源自 std::runtime_error。它是所有DataFrame 异常的基础。 在其他例外可能不适用的情况下,也可能引发它。
struct BadRange{ }它派生自 DataFrameError。在查询请求超出范围的数据的情况下,可能会引发此问题。 例如,当您尝试访问超出范围的数据或索引列时。
struct ColNotFound{ }它派生自 DataFrameError。在操作请求不存在的列的情况下,可能会引发它。
struct InconsistentData{ }它派生自 DataFrameError。在数据不一致的情况下,可能会引发它。 例如,当您尝试使用比索引列长的数据向量填充列时。
struct NotFeasible{ }它派生自 DataFrameError。在操作不可行的情况下,可能会抛出它。 例如,要求在字符串列中插入缺失的数据。
struct NotImplemented{ }它派生自 DataFrameError。在操作尚未实现的情况下,可能会引发它。

数据帧类定义为:
模板<类型名 I、类型名 H>
类数据帧;

我指定索引列类型 H 指定一个异构向量类型来包含 DataFrame 列 — 不要太纠结于此,而是使用 DataFrame 库类型
中方便的 typedef。

H 只能是:
HeteroVector<std::size_t A = 0>:这是一个包含数据的实际异构向量。这将产生一个“标准”数据框
HeteroPtrView<std::size_t A = 0>:这是一个异构矢量视图。转换视图为不连续的切片。
HeteroConstPtrView<std::size_t A = 0>:HeteroPtrView 的常量版本。
HeteroView<std::size_t A = 0>:这是一个异构矢量视图。转换视图为连续的切片。此视图比 HeteroPtrView 效率略高
HeteroConstView<std::size_t A = 0>:HeteroView 的常量版本。

三、多线程

  1. 数据帧使用静态容器来实现类型异构性。默认情况下,这些静态容器不受保护。这是通过设计完成的。因此,默认情况下,没有锁定开销。如果在多线程程序中使用 DataFrame,则必须提供 ThreadGranularity.h 文件中定义的 SpinLock。数据帧将使用您的旋转锁来保护容器。
    有关代码示例,请参阅上面的 set_lock()remove_lock()dataframe_tester.cc#3767

  2. 此外,数据帧的实例也不是多线程安全的。换句话说,数据帧的单个实例不得在没有保护的情况下在多个线程中使用,除非它用作只读。

  3. 同时,DataFrame 在内部以两种不同的方式利用多线程:

    1. 异步接口: 某些方法有异步版本。例如,你有sort()/sort_async()visit()/visit_async(),…更多。后面的版本返回一个可以并行执行的 std::future
    2. DataFrame 在某些算法中适当地使用多个线程(内部且用户不知道)。用户可以通过调用 set_thread_level() 来控制(或关闭)多线程,设置要使用的最大线程数。默认值为 0。最佳线程数是用户硬件/软件环境的函数,通常通过跟踪和错误获得。set_thread_level() 和线程级别通常是一个静态属性,一旦设置,它就会应用于所有实例。

四、视图

视图具有有用且实用的用例。视图是对原始数据帧的引用的数据帧的切片。它看起来与数据帧完全相同,但如果修改视图中的任何数据,原始数据帧中的相应数据点也将被修改,反之亦然。有些事情不能在视图中执行。例如,您无法添加或删除列、扩展索引列、…

一般来说有两种观点

  1. 常规视图:可以在视图或原始数据帧中更改数据,并查看两侧的更改
  2. 常量视图:不能更改视图中的数据。但是,您可以更改原始数据帧中的数据,也可以通过其他视图更改数据,这些数据将在常量视图中引用

为什么要使用视图

  • 在不复制数据的情况下对数据子集运行算法/切片
  • 混合和比较不同的数据子集,而不复制数据
  • 拥有一个事实来源,同时拥有不同的数据集而不复制数据

为了更好地理解,请进一步查看本文档和/或测试文件。


游客

访问者是实现分析(即统计、金融、机器学习)算法的主要机制。您可以轻松按照访问者的界面添加自定义算法,通过该算法扩展数据帧包。访问者还扮演着几个角色,在其他包中可能由单独的接口处理。访客扮演应用转换器算法的角色。例如,访问者可以转换列,也可以将列视为只读并实现算法。
有两个访客界面:

  1. 定期访问。此访问者是通过在数据帧实例上调用 visit() 方法来调用的。在这种情况下,数据帧将给定的索引和列数据点逐个传递给访问者函子。这对于一次可以对一个数据点进行操作的算法来说很方便。例如,相关性方差访问者。
  2. 单动访问。通过在数据帧实例上调用 single_act_visit() 方法来调用此访问者。在这种情况下,给定索引和列的开始和结束迭代器将传递给访问者函子。因此,函数可以同时访问所有索引和列数据。这对于需要将整个数据放在一起的算法是必要的。例如回访者或中位数访客。

大多数访问者中都有一些通用界面。例如,以下接口在几乎所有访问者之间都是通用的:
*get_result():*它返回访问者/算法的结果。
*pre():*每次在开始将数据传递给访问者之前,数据帧都会调用它。pre() 是初始化进程
*post() 的地方:*每次将数据传递给访问者时,数据帧都会调用它。

有关更多示例和文档,请参阅此文档 DataFrameStatsVisits.h、DataFrameMLVisitors.h、DataFrameFinancialVisits.h、DataFrameTransformVisits.htest/dataframe_tester[_2].cc。 我被问过很多次,为什么我选择算法的访问者模式而不是成员函数。
因为我希望算法是独立的对象。更准确地说明原因:

  • 如果我将它们实现为成员函数,我将在数据帧中拥有 100 个成员函数
  • 我希望用户能够在不接触DataFrame代码库的情况下合并他们的自定义算法。如果您遵循简单的界面,则可以编写自定义访问者并在数据帧中轻松使用它
  • 算法有时会产生复杂的结果。有时结果是一个数字。但有时算法的结果可能是单个或多个向量。作为成员函数实现效率不高
  • 我希望算法像对象一样自包含。这意味着单个对象应包含算法、输入、参数和结果
  • 由于算法是自包含对象,因此可以传递给其他算法

内存对齐

数据帧使您能够在自定义对齐边界上分配内存。
您可以使用此功能来利用现代 CPU 中的 SIMD 指令。由于 DataFrame 算法都是在数据向量(列)上完成的,因此这与编译器优化结合使用时会派上用场。
有一些方便的typedef定义了分配内存的数据帧,例如,在64,128,256,…字节边界。请参阅数据帧库类型
当您访问数据帧中的时,您将获得对 StlVecType 的引用。StlVecType 只是一个 stl::vector,带有用于请求对齐的自定义分配器。


数字生成器

随机生成器和其他一些数字生成器被添加为一系列方便的独立函数来生成随机数(它涵盖了所有标准分布C++)。您可以无缝使用这些例程来生成随机数据帧列。
请参阅此文档和文件 RandGen.h 和 *dataframe_tester.cc。*有关 RandGenParams 的定义和默认值,请参阅此文档和文件 DataFrameTypes.h


代码结构

DataFrame 库几乎是一个仅标头库,其中包含一些样板源文件异常、HeteroVector.ccHeteroView.cc 以及其他一些异常。此外,还有 DateTime.cc。

从根目录开始:

包含目录包含大部分代码。它包括.h和*.tcc*文件。后者是C++模板代码文件(它们大多位于 Internals 子目录中)。主头文件是 DataFrame.h。它包含数据帧类及其公共接口。该文件中的每个公共接口调用都有全面的注释。其余文件将向您展示香肠的制作方法。包含目录还包含主要包含内部数据帧实现的子目录。一个例外,DateTime.h 位于 Utils 子目录 src 目录中,其中包含仅限 Linux 的 make 文件和一些包含各种源代码的子目录。

测试目录包含所有测试源文件、模拟数据文件和测试输出文件。主要的测试源文件是 dataframe_tester.ccdataframe_tester_2.cc。它包含数据帧所有功能的测试用例。它不是一个很有组织的结构。我计划使测试用例更有条理。

示例
std::vector<unsigned long>  idx_col1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    std::vector<MyData>         mydata_col (10);
    std::vector<int>            int_col1 = { 1, 2, -3, -4, 5, 6, 7, 8, 9, -10 };
    std::vector<double>         dbl_col1 = { 0.01, 0.02, 0.03, 0.03, 0.05, 0.06, 0.03, 0.08, 0.09, 0.03 };

    ULDataFrame ul_df1;

    // One way to load data into the DataFrame is one column at a time.
    // A DataFrame column could be at most as long as its index column. So, you must load the index
    // first before loading any column.
    //
    // Once you load a column or index, the data is moved to DataFrame. The original vectors are now empty.
    // There are other ways of loading data without the move.
    //
    ul_df1.load_index(std::move(idx_col1));
    ul_df1.load_column("dbl_col", std::move(dbl_col1));
    ul_df1.load_column("my_data_col", std::move(mydata_col));
    ul_df1.load_column("integers", std::move(int_col1));

    std::vector<unsigned long>  idx_col2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    std::vector<std::string>    str_col = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
    std::vector<std::string>    cool_col =
        { "Azadi", "Hello", " World", "!", "Hype", "cubic spline", "Shawshank", "Silverado", "Arash", "Pardis" };
    std::vector<double>         dbl_col2 = { 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0 };

    ULDataFrame ul_df2;

    // Also, you can load data into a DataFrame all at once.
    // In this case again the data is moved to the DataFrame.
    //
    ul_df2.load_data(std::move(idx_col2),
                     std::make_pair("string col", str_col),
                     std::make_pair("Cool Column", cool_col),
                     std::make_pair("numbers", dbl_col2));
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中,没有内置的DataFrame数据类型,不像一些其他编程语言(如Python中的pandas库)提供了DataFrame数据结构。但是,你可以使用一些库或者编写自己的代码来实现类似DataFrame功能的数据结构。 下面是一些常用的库,可用于在C++中处理类似DataFrame的数据: 1. **Rcpp**: Rcpp是一个C++库,它提供了与R语言的无缝集成。R语言中有很多用于数据处理和分析的DataFrame相关的函数和库,使用Rcpp可以在C++中直接调用这些函数。 2. **Boost.MultiIndex**: Boost库提供了一个MultiIndex容器,它可以实现类似于DataFrame的功能。MultiIndex容器可以根据不同的索引方式对数据进行多维度的组织和访问。 3. **Eigen**: Eigen是一个用于线性代数计算的C++模板库。虽然它不是一个专门的DataFrame库,但它提供了一个Matrix类和一些矩阵操作函数,可以方便地处理表格数据。 4. **FastPFor**: FastPFor是一个C++库,用于高效地压缩和解压缩整型数据。尽管它并不是一个专门用于DataFrame的库,但可以在处理大量整型数据时提供高效的存储和检索功能。 如果你想自己实现一个简单的DataFrame数据结构,你可以使用C++的容器(如vector、map等)和结构体来存储和组织数据。你可以创建一个结构体来表示每一列的数据类型和名称,并使用vector或map来存储多个结构体对象。然后,你可以编写函数来实现类似于DataFrame的操作,如数据插入、删除、查询等。 需要注意的是,C++是一种静态类型语言,因此在处理类似DataFrame的数据时,类型安全性需要特别关注。你需要确保每个数据列的类型一致,并进行必要的类型转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SongpingWang

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值