Engee 文档
Notebook

表格

在本例中,我们将详细介绍表格以及如何在 Engee 脚本中使用表格。 让我们从连接辅助库和创建表格开始。

In [ ]:
Pkg.add(["TypedTables"])
In [ ]:
Pkg.add("TypedTables")
using TypedTables
   Resolving package versions...
   Installed Indexing ────────── v1.1.1
   Installed SplitApplyCombine ─ v1.2.3
   Installed TypedTables ─────── v1.4.6
   Installed Dictionaries ────── v0.4.2
    Updating `/user/.project/Project.toml`
  [9d95f2ec] + TypedTables v1.4.6
    Updating `/user/.project/Manifest.toml`
  [85a47980] + Dictionaries v0.4.2
  [313cdc1a] + Indexing v1.1.1
  [03a91e81] + SplitApplyCombine v1.2.3
  [9d95f2ec] + TypedTables v1.4.6
Precompiling project...
Indexing
Dictionaries
SplitApplyCombine
TypedTables
  4 dependencies successfully precompiled in 18 seconds. 88 already precompiled. 4 skipped during auto due to previous errors.
In [ ]:
t = Table(a = [1, 2, 3], b = [2.0, 4.0, 6.0])
Out[0]:
Table with 2 columns and 3 rows:
     a  b
   ┌───────
 1 │ 1  2.0
 2 │ 2  4.0
 3 │ 3  6.0

接下来,我们将详细了解与表格交互的功能。让我们来看看表格的第一行和第一列。

In [ ]:
t[1]
Out[0]:
(a = 1, b = 2.0)
In [ ]:
t.a
Out[0]:
3-element Vector{Int64}:
 1
 2
 3

现在,让我们来描述一下什么是表格以及表格的应用范围。

表格是 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 与底层数组存储无关(更像是一个方便的元编程层),代表每一列的数组可以具有完全不同的属性--例如,支持内存内、核外和分布式工作负载(详见 "数据表示")。

创建表格的方法

创建列表最简单的方法是使用关键字参数。

In [ ]:
t = Table(name = ["Alice", "Bob", "Charlie"], age = [25, 42, 37])
Out[0]:
Table with 2 columns and 3 rows:
     name     age
   ┌─────────────
 1 │ Alice    25
 2 │ Bob      42
 3 │ Charlie  37

构造函数同样接受来自列的 NamedTuple,即 Table((name = ["Alice", "Bob", "Charlie"], age = [25, 42, 37]))(注意多出的括号)。

使用 Table 构造函数还可以轻松地将基于行存储的命名元组向量转换为列存储。

In [ ]:
Table([(name = "Alice", age = 25), (name = "Bob", age = 42), (name = "Charlie", age = 37)])
Out[0]:
Table with 2 columns and 3 rows:
     name     age
   ┌─────────────
 1 │ Alice    25
 2 │ Bob      42
 3 │ Charlie  37

访问存储在表中的数据

让我们从行开始。在我们的表中,行就是一个 NamedTuple(命名元组),正如我们前面看到的,它很容易访问。让我们简单地引用行索引。

In [ ]:
t[1]
Out[0]:
(name = "Alice", age = 25)

多行的索引方式与标准数组类似。

In [ ]:
t[2:3]
Out[0]:
Table with 2 columns and 2 rows:
     name     age
   ┌─────────────
 1 │ Bob      42
 2 │ Charlie  37

我们还可以使用标准 Engee 函数获取表格尺寸。需要注意的是,调用 size 时不会显示列数。

In [ ]:
length(t)
Out[0]:
3
In [ ]:
size(t)
Out[0]:
(3,)

列可以通过调用列名来访问。

In [ ]:
t.name
Out[0]:
3-element Vector{String}:
 "Alice"
 "Bob"
 "Charlie"

检索多个列的最简单方法是根据列创建一个新表(如 table2 = Table(column1 = table1.column1, column2 = table1.column2, ...))。

列可以使用 columns 函数直接作为 NamedTuple 数组访问。

In [ ]:
columns(t)
Out[0]:
(name = ["Alice", "Bob", "Charlie"], age = [25, 42, 37])

此外,我们还可以访问该函数来获取列名。

In [ ]:
columnnames(t)
Out[0]:
(:name, :age)

综上所述,我们来重点介绍获取数据单元格的两种等效方法。

In [ ]:
t[1].name
Out[0]:
"Alice"
In [ ]:
t.name[1]
Out[0]:
"Alice"

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 方法哪个更适合您的任务,您可以按以下方法进行检查。查看所编写的代码是否倾向于通过名称来引用列,或者列名是否更加动态(例如,需要跨列迭代)。