表格¶
在本例中,我们将详细介绍表格以及如何在 Engee 脚本中使用表格。 让我们从连接辅助库和创建表格开始。
Pkg.add(["TypedTables"])
Pkg.add("TypedTables")
using TypedTables
t = Table(a = [1, 2, 3], b = [2.0, 4.0, 6.0])
接下来,我们将详细了解与表格交互的功能。让我们来看看表格的第一行和第一列。
t[1]
t.a
现在,让我们来描述一下什么是表格以及表格的应用范围。
表格是 Julia 类型的数组,其中每个元素(行)代表一个 NamedTuple 文件。具体来说
1.从外部看,表是一个命名元组数组。也就是说,Table 的每一行都表示为一个 Julia 的新 NamedTuple 元素,它易于使用且非常高效。在 Table 子类型指定 <: AbstractArray{<:NamedTuple} 中。
2.Table 内部存储的是一个(已命名的)元组,这是一种用于存储基于列的表格数据的便捷结构。
因此,操作 Table 数据非常简单。除了使用数组和命名的元组外,这种高效、简单和有趣的特性也是 Julia 的核心理念。
表格及其列可以是任意维度的抽象数组(AbstractArray)类型。这样,您就可以利用 Julia 强大的数组功能,例如多维广播。每一列都必须是与其他列具有相同维度和大小的数组。
TypedTables.jl 的目标是通过最少的培训介绍很少的概念,以便您可以原生操作表格数据。这种表格类型是对列的简单封装,是众所周知的、非常有用的 AbstractArray 接口。如果您熟悉数组和命名元组,就可以使用 Table 文件编写自己的数据分析。
但是,如果数据容器本身速度很慢,或者如果使用容器会导致程序员使用惯用模式时性能下降,那么这种功能就没什么用处了。在这种情况下,由于编译器完全了解每一列的类型,因此在表的行上进行 for 循环的速度可以与 C 语言等静态编译语言中手写代码的速度相媲美。因此,用户可以结合使用手写循环和调用 map、filter、reduce 等函数以及 groupQuery.jl 等包提供的高级接口来编写通用函数,而且仍然可以获得最佳性能。
最后,由于 Table 与底层数组存储无关(更像是一个方便的元编程层),代表每一列的数组可以具有完全不同的属性--例如,支持内存内、核外和分布式工作负载(详见 "数据表示")。
创建表格的方法¶
创建列表最简单的方法是使用关键字参数。
t = Table(name = ["Alice", "Bob", "Charlie"], age = [25, 42, 37])
构造函数同样接受来自列的 NamedTuple,即 Table((name = ["Alice", "Bob", "Charlie"], age = [25, 42, 37]))(注意多出的括号)。
使用 Table 构造函数还可以轻松地将基于行存储的命名元组向量转换为列存储。
Table([(name = "Alice", age = 25), (name = "Bob", age = 42), (name = "Charlie", age = 37)])
访问存储在表中的数据¶
让我们从行开始。在我们的表中,行就是一个 NamedTuple(命名元组),正如我们前面看到的,它很容易访问。让我们简单地引用行索引。
t[1]
多行的索引方式与标准数组类似。
t[2:3]
我们还可以使用标准 Engee 函数获取表格尺寸。需要注意的是,调用 size 时不会显示列数。
length(t)
size(t)
列可以通过调用列名来访问。
t.name
检索多个列的最简单方法是根据列创建一个新表(如 table2 = Table(column1 = table1.column1, column2 = table1.column2, ...))。
列可以使用 columns 函数直接作为 NamedTuple 数组访问。
columns(t)
此外,我们还可以访问该函数来获取列名。
columnnames(t)
综上所述,我们来重点介绍获取数据单元格的两种等效方法。
t[1].name
t.name[1]
TypedTables 和 DataFrame 的比较。¶
对于那些有 DataFrames.jl 软件包使用经验的人来说,这种比较可能会有所帮助:
1.存储在表中的列是不可变的:不能添加、删除或重命名列。然而,创建一个具有不同列的新表非常容易,这就鼓励使用函数式编程风格来处理外部数据结构。(另请参见更灵活的替代方案 FlexTable)。相比之下,IndexedTables 和 JuliaDB 也采用了类似的方法,而 DataFrames 则使用了无类型的列向量。
2.列本身可以修改。您可以修改一列或多列中的数据,也可以添加或删除行。因此,如果需要,对数据(而不是数据结构)的操作可以采用命令形式。
3.编译器知道列的类型,这使得直接操作(如搜索表行)非常快速。程序员可以自由编写低级 for 循环组合,使用 map、filter、reduce 等操作,或使用 Query.jl 等高级查询界面,所有这些都能达到静态编译组语言所能达到的高性能。 .innerjoin
相反,Julia 编译器会花费精力跟踪表中所有列的名称和类型。如果列的数量非常多(数以百计),Table 可能不是一个合适的数据结构(具有动态大小和 DataFrame 类型的列向量更适合)。
5.5. 表可以是任意维数的数组。
6.与 DataFrame 不同的是,您不能在一次 getindex 调用中访问单个单元格(您必须先检索一列,然后为该列中的单元格建立索引)。同样,列的数量也与 size 或 .lengthTable 无关。
输出¶
静态编译的 Table 和动态的 DataFrames 方法哪个更适合您的任务,您可以按以下方法进行检查。查看所编写的代码是否倾向于通过名称来引用列,或者列名是否更加动态(例如,需要跨列迭代)。