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. 在外部,表是一个命名元组的数组。 也就是说,表的每一行都表示为新的Julia NamedTuple元素之一,这些元素易于使用且非常有效。 在表子类型指定<:AbstractArray{<:NamedTuple}。

  2. 一个(命名的)数组元组存储在表中,这是一个方便的结构,用于存储基于列的表格数据。

因此,操作表数据非常简单。 除了使用数组和命名元组之外–这种效率,简单性和魅力是Julia意识形态所固有的。

表及其列可以是任何维度的AbstractArray类型。 这使您可以利用Julia数组的强大功能,例如多维广播。 每列应该是与其他列具有相同维度和大小的数组。

TypedTables的目标。jl是用最小的训练成本呈现很少的概念,以便您可以立即操作表格数据。 此表类型是对列的简单包装,是一个众所周知且非常高效的AbstractArray接口。 如果您熟悉数组和命名元组,则可以使用表文件编写自己的数据分析。

但是,如果数据容器本身很慢,或者当程序员使用惯用模式时,使用容器会遇到性能缺陷,则此功能将没有多大用处。 在这种情况下,由于编译器完全知道每列的类型,因此可以在静态编译语言(如C)中以手写代码的速度遍历表的行。 因此,用户可以使用手写循环和对map,filter,reduce等函数的调用以及groupQuery等包提供的高级接口的组合来编写通用函数。jl,并在同一时间获得最佳性能。[医]内

最后,由于表对底层阵列存储没有意见(并且更多地充当方便的元编程层),表示每个列的阵列可以具有完全不同的属性–例如,支持内存中,核外和分布式工作负载(有关更多信息,请参阅数据表示)。

创建表的方法

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

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])) ( 注意附加括号)。

此外,使用表构造函数可以很容易地将基于行存储的命名元组的向量转换为列存储。

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。列2,。..)).

列可以使用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-all具有静态编译组语言所期望的高性能。 .[医]内

  4. 相反,Julia编译器花费精力跟踪表中所有列的名称和类型。 如果您有非常大量的列(数百个),则表可能不是正确的数据结构(最好使用具有动态大小和DataFrame类型的列向量)。

  5. 表可以是任何维度的数组。

  6. 与DataFrame不同,您不能在单个getindex调用中访问一个单元格(您必须首先提取列并从该列索引单元格)。 同样,列的数量也不涉及大小或。可伸缩。

结论

您可以检查哪个更适合您的任务–静态编译表或动态DataFrames方法,如下所示。 查看编写的代码是否倾向于按名称引用列,或者列名称是否更具动态性(例如,需要列迭代)。