跳转到内容

Julia/DataFrames 简介

来自 Wikibooks,开放世界中的开放书籍
Previous page
模块和包
Julia 简介 Next page
DataFrames

DataFrames

[编辑 | 编辑源代码]

Julia 的 DataFrames 包是 Python 的 Pandas 包的替代方案,但可以使用 Pandas.jl 包装器包与 Pandas 结合使用。Julia 独有的包 Query.jl 和 DataFramesMeta.jl 也可以与 DataFrames 结合使用。

DataFrame 是一种类似于表格或电子表格的数据结构。您可以使用它来存储和探索一组相关的数据值。可以将其视为用于保存表格数据的更智能的数组。

为了探索 DataFrames.jl 的用法,我们将首先检查一个名为 Anscombe 四重奏的著名统计数据集。

如果您以前从未使用过 DataFrames 和 CSV 包,请先下载它们。

julia> ]
(v1.2) pkg> add DataFrames
...messages
(v1.2) pkg>

您只需执行一次此操作。版本应至少为 v0.22.0,并且从 1.0 版本开始,DataFrames.jl 包已知速度更快。

此外,您也可以在此时添加 CSV.jl。

(v1.2) pkg> add CSV

使用 DataFrames

julia> using DataFrames

对于本文档,我们使用的是 DataFrames.jl 的 0.22 版本。早期版本访问列的语法略有不同,因此如果您使用的是早期版本,则值得更新。

将数据加载到 DataFrames 中

[编辑 | 编辑源代码]

有几种不同的方法可以创建新的 DataFrames。对于本入门指南,加载 Anscombe 数据集并将其分配给变量 anscombe 的最快方法是复制/粘贴几行数据,将其转换为数组,然后重命名列名,如下所示

julia>  anscombe = DataFrame(                 
 [10  10  10  8   8.04   9.14  7.46   6.58;    
  8   8   8   8   6.95   8.14  6.77   5.76;   
  13  13  13  8   7.58   8.74  12.74  7.71;   
  9   9   9   8   8.81   8.77  7.11   8.84;   
  11  11  11  8   8.33   9.26  7.81   8.47;   
  14  14  14  8   9.96   8.1   8.84   7.04;   
  6   6   6   8   7.24   6.13  6.08   5.25;   
  4   4   4   19  4.26   3.1   5.39   12.5;   
  12  12  12  8   10.84  9.13  8.15   5.56;   
  7   7   7   8   4.82   7.26  6.42   7.91;   
  5   5   5   8   5.68   4.74  5.73   6.89], :auto); 
julia> rename!(anscombe, [Symbol.(:X, 1:4); Symbol.(:Y, 1:4)])
11×8 DataFrames.DataFrame
│ Row │ X1   │ X2   │ X3   │ X4   │ Y1    │ Y2   │ Y3    │ Y4   │
├─────┼──────┼──────┼──────┼──────┼───────┼──────┼───────┼──────┤
│ 1   │ 10.0 │ 10.0 │ 10.0 │ 8.0  │ 8.04  │ 9.14 │ 7.46  │ 6.58 │
│ 2   │ 8.0  │ 8.0  │ 8.0  │ 8.0  │ 6.95  │ 8.14 │ 6.77  │ 5.76 │
│ 3   │ 13.0 │ 13.0 │ 13.0 │ 8.0  │ 7.58  │ 8.74 │ 12.74 │ 7.71 │
│ 4   │ 9.0  │ 9.0  │ 9.0  │ 8.0  │ 8.81  │ 8.77 │ 7.11  │ 8.84 │
│ 5   │ 11.0 │ 11.0 │ 11.0 │ 8.0  │ 8.33  │ 9.26 │ 7.81  │ 8.47 │
│ 6   │ 14.0 │ 14.0 │ 14.0 │ 8.0  │ 9.96  │ 8.1  │ 8.84  │ 7.04 │
│ 7   │ 6.0  │ 6.0  │ 6.0  │ 8.0  │ 7.24  │ 6.13 │ 6.08  │ 5.25 │
│ 8   │ 4.0  │ 4.0  │ 4.0  │ 19.0 │ 4.26  │ 3.1  │ 5.39  │ 12.5 │
│ 9   │ 12.0 │ 12.0 │ 12.0 │ 8.0  │ 10.84 │ 9.13 │ 8.15  │ 5.56 │
│ 10  │ 7.0  │ 7.0  │ 7.0  │ 8.0  │ 4.82  │ 7.26 │ 6.42  │ 7.91 │
│ 11  │ 5.0  │ 5.0  │ 5.0  │ 8.0  │ 5.68  │ 4.74 │ 5.73  │ 6.89 │ 

收集的数据集

[编辑 | 编辑源代码]

或者,您可以下载并安装 RDatasets 包,其中包含许多著名的数据集,包括 Anscombe 的数据集。

julia> ]
(v1.2) pkg> add RDatasets
julia> using RDatasets
julia> anscombe = dataset("datasets","anscombe")

还有其他方法可以创建 DataFrames,包括从文件读取数据(使用 CSV.jl)。

空 DataFrames

[编辑 | 编辑源代码]

您可以通过在数组中提供有关行和列名的信息来创建简单的 DataFrames。

julia> df = DataFrame(A = 1:6, B = 100:105)

6×2 DataFrame
│ Row │ A     │ B     │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 1     │ 100   │
│ 2   │ 2     │ 101   │
│ 3   │ 3     │ 102   │
│ 4   │ 4     │ 103   │
│ 5   │ 5     │ 104   │
│ 6   │ 6     │ 105   │

要创建一个完全空的 DataFrame,您需要提供列名(Julia 符号)并定义其类型(请记住,列是数组)。

df = DataFrame(Name=String[], 
    Width=Float64[], 
    Height=Float64[], 
    Mass=Float64[], 
    Volume=Float64[])

0×5 DataFrames.DataFrame

df = vcat(df, DataFrame(Name="Test", Width=1.0, Height=10.0, Mass=3.0, Volume=5.0))
1×5 DataFrames.DataFrame
│ Row │ Name │ Width │ Height │ Mass │ Volume │
├─────┼──────┼───────┼────────┼──────┼────────┤
│ 1   │ Test │ 1.0   │ 10.0   │ 3.0  │ 5.0    │

基础知识

[编辑 | 编辑源代码]

加载 Anscombe 数据集后,您应该会看到 DataFrame;如果您在终端工作或使用 IJulia 笔记本,则其外观会有所不同。但在任何一种情况下,您都可以看到您有一个数据表,其中包含 8 列命名列(X1 到 Y4)和 11 行(1 到 11)。第一对感兴趣的是 X1/Y1,第二对是 X2/Y2,依此类推。由于列已命名,因此在处理或分析数据时,很容易引用特定列。

julia> typeof(anscombe)
DataFrame

要获取列名列表,请使用 names() 函数。

julia> names(anscombe)
 8-element Array{String,1}:
 "X1"
 "X2"
 "X3"
 "X4"
 "Y1"
 "Y2"
 "Y3"
 "Y4"

其他有用函数

julia> size(anscombe) # number of rows, number of columns
(11, 8)

describe() 函数提供每个列的快速概述。

julia> describe(anscombe)
8×8 DataFrame
│ Row │ variable │ mean    │ min  │ median  │ max   │ nunique │ nmissing │ eltype   │
│     │ Symbol   │ Float64 │ Real │ Float64 │ Real  │ Nothing │ Nothing  │ DataType │
├─────┼──────────┼─────────┼──────┼─────────┼───────┼─────────┼──────────┼──────────┤
│ 1   │ X1       │ 9.0     │ 4    │ 9.0     │ 14    │         │          │ Int64    │
│ 2   │ X2       │ 9.0     │ 4    │ 9.0     │ 14    │         │          │ Int64    │
│ 3   │ X3       │ 9.0     │ 4    │ 9.0     │ 14    │         │          │ Int64    │
│ 4   │ X4       │ 9.0     │ 8    │ 8.0     │ 19    │         │          │ Int64    │
│ 5   │ Y1       │ 7.50091 │ 4.26 │ 7.58    │ 10.84 │         │          │ Float64  │
│ 6   │ Y2       │ 7.50091 │ 3.1  │ 8.14    │ 9.26  │         │          │ Float64  │
│ 7   │ Y3       │ 7.5     │ 5.39 │ 7.11    │ 12.74 │         │          │ Float64  │
│ 8   │ Y4       │ 7.50091 │ 5.25 │ 7.04    │ 12.5  │         │          │ Float64  │

请注意,某些列(所有 X 列)包含整数值,而其他列(所有 Y 列)是浮点数。DataFrame 中列中的每个元素都具有相同的数据类型,但不同的列可以具有不同的类型——这使得 DataFrame 非常适合存储表格数据——一列中的字符串,另一列中的数值,依此类推。

引用特定列

[编辑 | 编辑源代码]

有多种方法可以选择列。

您可以使用点/句点 (.),这是标准的 Julia 字段访问器。

julia> anscombe.Y2
11-element Array{Float64,1}:
 9.14
 8.14
 8.74
 8.77
 9.26
 8.1 
 6.13
 3.1 
 9.13
 7.26
 4.74

或者,您可以使用 Julia 中符号名称的一般约定:在列名前面加上冒号 (:)。因此,:Y2 指的是名为 Y2 的列,或第 6 列。

您可以使用整数和整数向量;以下是 anscombe 数据框的第 6 列(所有行)。

julia> anscombe[:, 6]
11-element Array{Float64,1}:
 9.14
 8.14
 8.74
 8.77
 9.26
 8.1 
 6.13
 3.1 
 9.13
 7.26
 4.74

以下是第 1、2、3、5 和 8 列。

julia> anscombe[:, [1, 2, 3, 5, 8]]
11×5 DataFrame
│ Row │ X1    │ X2    │ X3    │ Y1      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼─────────┼─────────┤
│ 1   │ 10    │ 10    │ 10    │ 8.04    │ 6.58    │
│ 2   │ 8     │ 8     │ 8     │ 6.95    │ 5.76    │
│ 3   │ 13    │ 13    │ 13    │ 7.58    │ 7.71    │
│ 4   │ 9     │ 9     │ 9     │ 8.81    │ 8.84    │
│ 5   │ 11    │ 11    │ 11    │ 8.33    │ 8.47    │
│ 6   │ 14    │ 14    │ 14    │ 9.96    │ 7.04    │
│ 7   │ 6     │ 6     │ 6     │ 7.24    │ 5.25    │
│ 8   │ 4     │ 4     │ 4     │ 4.26    │ 12.5    │
│ 9   │ 12    │ 12    │ 12    │ 10.84   │ 5.56    │
│ 10  │ 7     │ 7     │ 7     │ 4.82    │ 7.91    │
│ 11  │ 5     │ 5     │ 5     │ 5.68    │ 6.89    │

以下是第一列 X 和 Y。

julia> anscombe[:, [:X1, :Y1]]
11×2 DataFrame
│ Row │ X1    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │

您可以提供正则表达式来获取一组匹配的列名。以下是显示所有名称以“2”结尾的列的结果。

julia> anscombe[:, r".2"]
11×2 DataFrame
│ Row │ X2    │ Y2      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 9.14    │
│ 2   │ 8     │ 8.14    │
│ 3   │ 13    │ 8.74    │
│ 4   │ 9     │ 8.77    │
│ 5   │ 11    │ 9.26    │
│ 6   │ 14    │ 8.1     │
│ 7   │ 6     │ 6.13    │
│ 8   │ 4     │ 3.1     │
│ 9   │ 12    │ 9.13    │
│ 10  │ 7     │ 7.26    │
│ 11  │ 5     │ 4.74    │

要访问数据集 anscombe 的第 3 列和第 5 列,您可以使用以下任何一种方法:

julia> anscombe[:, [:X3, :Y1]] 
11×2 DataFrame
│ Row │ X3    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │
julia> anscombe[:, [3, 5]]
11×2 DataFrame
│ Row │ X3    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │
julia> anscombe[:, ["X3", "Y1"]]
11×2 DataFrame
│ Row │ X3    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │

您可以使用变量访问列。例如,如果 a = "X3" 且 b = "Y1",则

julia> anscombe[:, [a, b]]
11×2 DataFrame
│ Row │ X3    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │

会按预期执行。

获取矩形“切片”

[编辑 | 编辑源代码]
julia> anscombe[4:6, [:X2,:X4]]
3×2 DataFrame
│ Row │ X2    │ X4    │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 9     │ 8     │
│ 2   │ 11    │ 8     │
│ 3   │ 14    │ 8     │

这显示了第 4、5 和 6 行以及 X2 和 X4 列。您可以使用逗号指定单个行,而不是行范围。

julia> anscombe[[4, 6, 9], [:X2,:X4]]
3×2 DataFrame
│ Row │ X2    │ X4    │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 9     │ 8     │
│ 2   │ 14    │ 8     │
│ 3   │ 12    │ 8     │

这显示了第 4、6 和 9 行以及 X2 和 X4 列。

或者,您可以使用索引号。

julia> anscombe[[4, 6, 8], [2, 6, 8]]
3×3 DataFrame
│ Row │ X2    │ Y2      │ Y4      │
│     │ Int64 │ Float64 │ Float64 │
├─────┼───────┼─────────┼─────────┤
│ 1   │ 9     │ 8.77    │ 8.84    │
│ 2   │ 14    │ 8.1     │ 7.04    │
│ 3   │ 4     │ 3.1     │ 12.5    │

要指定行和列的范围,请使用索引号。

julia> anscombe[4:6, 3:5]
3×3 DataFrame
│ Row │ X3    │ X4    │ Y1      │
│     │ Int64 │ Int64 │ Float64 │
├─────┼───────┼───────┼─────────┤
│ 1   │ 9     │ 8     │ 8.81    │
│ 2   │ 11    │ 8     │ 8.33    │
│ 3   │ 14    │ 8     │ 9.96    │

请注意,返回的 DataFrames 的行编号不同——第 4、5 和 6 行在新的 DataFrame 中变成了第 1、2 和 3 行。

与数组一样,当您想要查看内容时,请单独使用冒号来指定“所有”列或行(当您修改内容时,语法不同,如下所述)。因此,要查看第 4、6、8 和 11 行,显示所有列。

julia> anscombe[[4,6,8,11], :]
4×8 DataFrame
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 1   │ 9     │ 9     │ 9     │ 8     │ 8.81    │ 8.77    │ 7.11    │ 8.84    │
│ 2   │ 14    │ 14    │ 14    │ 8     │ 9.96    │ 8.1     │ 8.84    │ 7.04    │
│ 3   │ 4     │ 4     │ 4     │ 19    │ 4.26    │ 3.1     │ 5.39    │ 12.5    │
│ 4   │ 5     │ 5     │ 5     │ 8     │ 5.68    │ 4.74    │ 5.73    │ 6.89    │

所有行,X1 和 Y1 列。

julia> anscombe[:, [:X1, :Y1]]
11×2 DataFrame
│ Row │ X1    │ Y1      │
│     │ Int64 │ Float64 │
├─────┼───────┼─────────┤
│ 1   │ 10    │ 8.04    │
│ 2   │ 8     │ 6.95    │
│ 3   │ 13    │ 7.58    │
│ 4   │ 9     │ 8.81    │
│ 5   │ 11    │ 8.33    │
│ 6   │ 14    │ 9.96    │
│ 7   │ 6     │ 7.24    │
│ 8   │ 4     │ 4.26    │
│ 9   │ 12    │ 10.84   │
│ 10  │ 7     │ 4.82    │
│ 11  │ 5     │ 5.68    │

使用条件选择行

[编辑 | 编辑源代码]

您可以选择 DataFrame 中所有元素满足一个或多个条件的行。

以下是如何选择 Y1 列中元素的值大于 7.0 的行。

julia> anscombe[anscombe.Y1 .> 7.0, :]
7×8 DataFrame
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 1   │ 10    │ 10    │ 10    │ 8     │ 8.04    │ 9.14    │ 7.46    │ 6.58    │
│ 2   │ 13    │ 13    │ 13    │ 8     │ 7.58    │ 8.74    │ 12.74   │ 7.71    │
│ 3   │ 9     │ 9     │ 9     │ 8     │ 8.81    │ 8.77    │ 7.11    │ 8.84    │
│ 4   │ 11    │ 11    │ 11    │ 8     │ 8.33    │ 9.26    │ 7.81    │ 8.47    │
│ 5   │ 14    │ 14    │ 14    │ 8     │ 9.96    │ 8.1     │ 8.84    │ 7.04    │
│ 6   │ 6     │ 6     │ 6     │ 8     │ 7.24    │ 6.13    │ 6.08    │ 5.25    │
│ 7   │ 12    │ 12    │ 12    │ 8     │ 10.84   │ 9.13    │ 8.15    │ 5.56    │

“内部”短语 anscombe.Y1 .> 7.0 对 Y1 列中的值执行逐元素比较,并返回一个布尔真值或假值数组,每个行一个。请注意广播运算符 .。然后使用它们从 DataFrame 中选择行。就像您输入了以下内容一样:

julia> anscombe[[true, false, true, true, true, true, true, false, true, false, false], :]

同样,以下结果包含 Y1 列中的数字的值大于 Y2 列中的数字的每一行。

julia> anscombe[anscombe.Y1 .> anscombe.Y2, :]
6x8 DataFrame
| Row | X1 | X2 | X3 | X4 | Y1    | Y2   | Y3   | Y4   |
|-----|----|----|----|----|-------|------|------|------|
| 1   | 9  | 9  | 9  | 8  | 8.81  | 8.77 | 7.11 | 8.84 |
| 2   | 14 | 14 | 14 | 8  | 9.96  | 8.1  | 8.84 | 7.04 |
| 3   | 6  | 6  | 6  | 8  | 7.24  | 6.13 | 6.08 | 5.25 |
| 4   | 4  | 4  | 4  | 19 | 4.26  | 3.1  | 5.39 | 12.5 |
| 5   | 12 | 12 | 12 | 8  | 10.84 | 9.13 | 8.15 | 5.56 |
| 6   | 5  | 5  | 5  | 8  | 5.68  | 4.74 | 5.73 | 6.89 |

另一种选择匹配行的方法是使用 Julia 函数 filter

julia> filter(row -> row.Y1 > 7.0, anscombe) 

组合两个或多个条件也是可能的。以下结果包含 Y1 的值大于 5.0 并且 Y2 的值小于 7.0 的行。

julia> anscombe[(anscombe.Y1 .> 5.0) .& (anscombe.Y2 .< 7.0), :]
2×8 DataFrame
 Row  X1     X2     X3     X4     Y1       Y2       Y3       Y4      
      Int64  Int64  Int64  Int64  Float64  Float64  Float64  Float64 
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
 1    6      6      6      8      7.24     6.13     6.08     5.25    
 2    5      5      5      8      5.68     4.74     5.73     6.89    

使用 filter 的等效方法如下:

julia> filter(row -> row.Y1 > 5 &&  row.Y2 < 7.0, anscombe)

将函数应用于列和行

[编辑 | 编辑源代码]

您可以将函数应用于一列。要找出名为 X2 的列中值的平均值

julia> using Statistics
julia> mean(anscombe.X2)
9.0

Dataframes 包提供了两个方便的工具,eachcol()eachrow()。这些可用于遍历每一列或每一行。每个值都是一个 Symbol(列标题)和 DataArray 的元组。

要将 mean() 函数应用于 DataFrame 的每一列,您可以使用列表推导式

julia>  [mean(col) for col in eachcol(anscombe)]
8-element Array{Float64,1}:
 9.0              
 9.0              
 9.0              
 9.0              
 7.500909090909093
 7.500909090909091
 7.500000000000001
 7.50090909090909 

它返回一个新数组,包含每一列的平均值。

或者,您可以使用广播版本

julia>  mean.(eachcol(anscombe)) 

以下是每一列的平均值

julia> for col in eachcol(anscombe)
             println(mean(col)) 
          end

9.0 9.0 9.0 9.0 7.500909090909093 7.500909090909091 7.500000000000001 7.50090909090909

eachrow() 函数为行提供了一个迭代器

julia> for r in eachrow(anscombe)
           println(r) 
       end
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 1   │ 10    │ 10    │ 10    │ 8     │ 8.04    │ 9.14    │ 7.46    │ 6.58    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 2   │ 8     │ 8     │ 8     │ 8     │ 6.95    │ 8.14    │ 6.77    │ 5.76    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 3   │ 13    │ 13    │ 13    │ 8     │ 7.58    │ 8.74    │ 12.74   │ 7.71    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 4   │ 9     │ 9     │ 9     │ 8     │ 8.81    │ 8.77    │ 7.11    │ 8.84    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 5   │ 11    │ 11    │ 11    │ 8     │ 8.33    │ 9.26    │ 7.81    │ 8.47    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 6   │ 14    │ 14    │ 14    │ 8     │ 9.96    │ 8.1     │ 8.84    │ 7.04    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 7   │ 6     │ 6     │ 6     │ 8     │ 7.24    │ 6.13    │ 6.08    │ 5.25    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 8   │ 4     │ 4     │ 4     │ 19    │ 4.26    │ 3.1     │ 5.39    │ 12.5    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 9   │ 12    │ 12    │ 12    │ 8     │ 10.84   │ 9.13    │ 8.15    │ 5.56    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 10  │ 7     │ 7     │ 7     │ 8     │ 4.82    │ 7.26    │ 6.42    │ 7.91    │
DataFrameRow
│ Row │ X1    │ X2    │ X3    │ X4    │ Y1      │ Y2      │ Y3      │ Y4      │
│     │ Int64 │ Int64 │ Int64 │ Int64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼───────┼───────┼───────┼───────┼─────────┼─────────┼─────────┼─────────┤
│ 11  │ 5     │ 5     │ 5     │ 8     │ 5.68    │ 4.74    │ 5.73    │ 6.89    │

在这个数据集中,每一行的每个元素都是一个数字,因此,如果我们想的话,我们可以使用 eachrow() 来查找每一行的(无意义的)平均值

julia> for row in eachrow(anscombe)
          println(mean(row)) 
        end 
8.6525
7.4525
10.47125
8.56625
9.358749999999999
10.492500000000001
6.3375
7.03125
9.71
6.92625
5.755000000000001

绘制 Anscombe 四重奏

[编辑 | 编辑源代码]

现在让我们将重点转向统计。

内置的 describe() 函数允许您快速计算数据集列的统计属性。提供您想知道的属性的符号,从 :mean:std:min:q25:median:q75:max:eltype:nunique:first:last:nmissing 中选择。

julia> describe(anscombe, :mean, :std, :min, :median)
8×5 DataFrame
 Row  variable  mean     std      min   median  
      Symbol    Float64  Float64  Real  Float64 
├─────┼──────────┼─────────┼─────────┼──────┼─────────┤
 1    X1        9.0      3.31662  4     9.0     
 2    X2        9.0      3.31662  4     9.0     
 3    X3        9.0      3.31662  4     9.0     
 4    X4        9.0      3.31662  8     8.0     
 5    Y1        7.50091  2.03157  4.26  7.58    
 6    Y2        7.50091  2.03166  3.1   8.14    
 7    Y3        7.5      2.03042  5.39  7.11    
 8    Y4        7.50091  2.03058  5.25  7.04    

我们也可以比较 XY 数据集

julia> [describe(anscombe[:, xy], :mean, :std, :median) for xy in [[:X1, :Y1], [:X2, :Y2], [:X3, :Y3], [:X4, :Y4]]]
4-element Array{DataFrame,1}:
 2×4 DataFrame
│ Row │ variable │ mean    │ std     │ median  │
│     │ Symbol   │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼─────────┼─────────┼─────────┤
│ 1   │ X1       │ 9.0     │ 3.31662 │ 9.0     │
│ 2   │ Y1       │ 7.50091 │ 2.03157 │ 7.58    │
 2×4 DataFrame
│ Row │ variable │ mean    │ std     │ median  │
│     │ Symbol   │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼─────────┼─────────┼─────────┤
│ 1   │ X2       │ 9.0     │ 3.31662 │ 9.0     │
│ 2   │ Y2       │ 7.50091 │ 2.03166 │ 8.14    │
 2×4 DataFrame
│ Row │ variable │ mean    │ std     │ median  │
│     │ Symbol   │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼─────────┼─────────┼─────────┤
│ 1   │ X3       │ 9.0     │ 3.31662 │ 9.0     │
│ 2   │ Y3       │ 7.5     │ 2.03042 │ 7.11    │
 2×4 DataFrame
│ Row │ variable │ mean    │ std     │ median  │
│     │ Symbol   │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼─────────┼─────────┼─────────┤
│ 1   │ X4       │ 9.0     │ 3.31662 │ 8.0     │
│ 2   │ Y4       │ 7.50091 │ 2.03058 │ 7.04    │

最后再看一眼 XY 数据集之间的相关性

julia> [cor(anscombe[:, first(xy)], anscombe[:, last(xy)]) for xy in [[:X1, :Y1], [:X2, :Y2], [:X3, :Y3], [:X4, :Y4]]]
4-element Array{Float64,1}:
 0.8164205163448398
 0.8162365060002429
 0.8162867394895982
 0.8165214368885028

请注意这些相关性有多么相似:X1Y1 与 X2Y2、X3Y3、X4Y4 相同。

这四个数据集中的每一个都具有相同的平均值、中位数、标准差和 x 和 y 之间的相关系数。根据简单的汇总统计数据,您会认为它们非常相似。让我们绘制它们

using StatsPlots # add this package if necessary
@df anscombe scatter([:X1 :X2 :X3 :X4], [:Y1 :Y2 :Y3 :Y4],
           smooth=true,
           line = :red,
           linewidth = 2,
           title= ["X$i vs Y$i" for i in (1:4)'],
           legend = false,
           layout = 4,
           xlimits = (2, 20),
           ylimits = (2, 14))

Plotting the anscombe relationships

Anscombe 四重奏包含四个数据集,这些数据集具有几乎相同的简单统计属性,但实际上却大不相同。每个数据集都包含 11 个 (x,y) 点。它们是由统计学家弗朗西斯·安斯库姆在 1973 年精心构建的,以证明在依赖汇总统计数据之前查看数据和绘制数据的重要性,以及异常值对统计属性的影响。第一个似乎显示了一个简单的线性关系,对应于两个相关的变量并遵循正态性假设。

第二组点不是正态分布的;两个变量之间存在明显的联系,但它不是线性的,并且 Pearson 相关系数实际上并不相关。

在第三组中,分布是线性的,但回归线不同,它被一个异常值偏移,该异常值具有足够的 影响力来改变回归线并将相关系数从 1 降至 0.816。

最后,第四组显示了一个示例,其中一个异常值足以产生高相关系数,即使两个变量之间的关系不是线性的。

四重奏仍然经常被用来说明在开始根据特定类型的关系进行分析之前以图形方式查看数据集的重要性,以及基本统计属性对于描述真实数据集的不充分性。

回归和模型

[编辑 | 编辑源代码]

如果您想为数据集找到线性回归线,您可以转向 GLM(广义线性模型)包。

using GLM, StatsModels # add these packages if necessary

要创建线性模型,您使用 @formula 宏指定公式,提供列名和 DataFrame 的名称。结果是一个回归模型。

julia> linearmodel = fit(LinearModel, @formula(Y1 ~ X1), anscombe)
 StatsModels.DataFrameRegressionModel{GLM.LinearModel{GLM.LmResp{Array{Float64,1}},
 GLM.DensePredChol{Float64,Base.LinAlg.Cholesky{Float64,Array{Float64,2}}}},Array{Float64,2}}
Y1 ~ 1 + X1

Coefficients:
──────────────────────────────────────────────────────────────────────────
             Estimate  Std. Error  t value  Pr(>|t|)  Lower 95%  Upper 95%
──────────────────────────────────────────────────────────────────────────
(Intercept)  3.00009     1.12475   2.66735    0.0257   0.455737   5.54444 
X1           0.500091    0.117906  4.24146    0.0022   0.23337    0.766812
──────────────────────────────────────────────────────────────────────────

GLM 包中用于处理线性模型的有用函数包括 summary()coef()

julia> summary(linearmodel)
 "StatsModels.DataFrameRegressionModel{GLM.LinearModel{GLM.LmResp{Array{Float64,1}},
  GLM.DensePredChol{Float64,Base.LinAlg.Cholesky{Float64,Array{Float64,2}}}}, Array{Float64,2}}"

coef() 函数返回定义回归线的两个有用系数:估计的截距和估计的斜率

julia> coef(linearmodel)
2-element Array{Float64,1}:
 3.0000909090909054
 0.5000909090909096

现在可以轻松地生成一个用于回归线的函数,形式为 y = a x + c

julia> f(x) = coef(linearmodel)[2] * x + coef(linearmodel)[1]
f (generic function with 1 method)

现在我们有了描述回归线的函数 f(),它可以在图中绘制。在这里,我们绘制第一个序列,并添加函数 f(x) 的绘图,其中 x 从 2 运行到 20,并查看它与我们之前使用的平滑线相比如何。

p1 = plot(anscombe[!, :X1], anscombe[!, :Y1], 
    smooth=true, 
    seriestype=:scatter, 
    title = "X1 vs Y1", 
    linewidth=8,
    linealpha=0.5,
    label="data")

plot!(f, 2, 20, label="correlation")

plotting the anscombe quartet with regression

使用 DataFrames

[编辑 | 编辑源代码]

并非所有数据集都像 RDatasets 中的示例那样一致且整洁。在现实世界中,您可能会将一些数据读入 DataFrame,然后发现它存在一些问题,其中包含格式不一致和/或缺失的元素。

在本节中,我们将手动创建一个简单的测试 DataFrame,逐一定义列。它是可能存在的元素周期表中的一个简短摘录。

ptable = DataFrame(  Number       =   [1,   2,    6,    8,    26    ],
                     Name         =   ["Hydrogen",   "Helium",   "Carbon",   "Oxygen",   "Iron"   ],
                     AtomicWeight =   [1.0079,    4.0026,  12.0107, 15.9994, 55.845   ],
                     Symbol       =   ["H",    "He",    "C",    "O",  "Fe"  ],
                     Discovered   =   [1776,   1895,    0,    1774,    missing    ])
5x5 DataFrame
| Row | Number | Name       | AtomicWeight | Symbol | Discovered |
|-----|--------|------------|--------------|--------|------------|
| 1   | 1      | "Hydrogen" | 1.0079       | "H"    | 1776       |
| 2   | 2      | "Helium"   | 4.0026       | "He"   | 1895       |
| 3   | 6      | "Carbon"   | 12.0107      | "C"    | 0          |
| 4   | 8      | "Oxygen"   | 15.9994      | "O"    | 1774       |
| 5   | 26     | "Iron"     | 55.845       | "Fe"   | Missing    |

缺失值的情况

[编辑 | 编辑源代码]

使用 describe() 进行初步查看显示,Discovered 列有一些缺失值。它是包含铁的发现年份的列,在源数据中标记为缺失。

julia> describe(ptable)
5×8 DataFrame
│ Row │ variable     │ mean    │ min    │ median  │ max    │ nunique │ nmissing │ eltype   │
│     │ Symbol       │ Union…  │ Any    │ Union…  │ Any    │ Union…  │ Union…   │ DataType │
├─────┼──────────────┼─────────┼────────┼─────────┼────────┼─────────┼──────────┼──────────┤
│ 1   │ Number       │ 8.6     │ 1      │ 6.0     │ 26     │         │          │ Int64    │
│ 2   │ Name         │         │ Carbon │         │ Oxygen │ 5       │          │ String   │
│ 3   │ AtomicWeight │ 17.7731 │ 1.0079 │ 12.0107 │ 55.845 │         │          │ Float64  │
│ 4   │ Symbol       │         │ C      │         │ O      │ 5       │          │ String   │
│ 5   │ Discovered   │ 1361.25 │ 0      │ 1775.0  │ 1895   │         │ 1        │ Int64    │

缺失字段的问题是您在处理表格数据时必须面对的重要问题之一。有时,表中的并非每个字段都有数据。由于各种原因,可能存在缺失的观察值或数据点。除非您了解这些缺失值,否则它们会导致问题。例如,如果您没有注意到并考虑缺失值,则平均值和其他统计计算将不正确。(在使用缺失值的特殊标记之前,人们过去常常输入“明显错误”的数字,例如 -99 用于缺失的温度读数;在温度数据集中没有发现这些数字会导致问题……)此外,将公式应用于数字和字符串值的混合以及缺失值可能会很困难。

您会遇到数据编译器用来指示值缺失的各种方法。有时值以 0 或其他“不可能”的数字表示,或以“n/a”等文本字符串表示。有时——特别是对于 Tab 和逗号分隔的文件,它们只是被留空。

为了解决此问题,有一个特殊的 数据类型 Missing,它表示在此位置没有可用值。如果一致使用,DataFrames 包及其对 Missing 字段的支持将允许您充分利用数据集,同时确保您的计算准确且不会因缺失值而“受损”。Iron/Discovered 单元格中的 missing 允许 DataFrames 包将该单元格标记为 Missing。

但这里还有一个未揭示的问题。碳的发现年份设置为 0,选择表示“很久以前”,因为它不是有效的年份。(公元前 1 年之后是公元 1 年,所以年份 0 不存在。)此 0 值也应在 DataFrame 中标记为 Missing,然后再导致混乱。

有三种方法可以使 DataFrame 一致地使用缺失值

- 在导入之前使用文本编辑器或类似工具在 Julia 之外编辑文件/数据。这有时是最快速和最简单的方法。在我们的简单示例中,我们在文件中使用了单词 missing。这足以将该位置标记为 Missing 值。

- 导入数据时使用 CSV 包提供的选项。这些选项允许您指定用于识别某些值作为 Missing 的规则。

- 在将其导入 DataFrame 之前修复文件。

如何使用 readtable() 修复缺失值

[编辑 | 编辑源代码]

CSV.read 中内置了许多灵活性。

默认情况下,任何缺失值(表中的空字段)都将替换为 missing。使用 missingstrings 选项,您可以指定一组字符串,当在源文本中遇到这些字符串时,它们都将转换为 NA 值

pt1 = CSV.read("file.tsv", missingstrings=["NA", "na", "n/a", "missing"])

这解决了原始文件中指示不可用值的各种方法的大多数问题。如果 0 不用于任何合法值,也可以将“0”(作为字符串的零)添加到 nastrings 列表中。

使用包含缺失值的 DataFrames

[编辑 | 编辑源代码]

如果某列包含一个或多个缺失值,您会发现某些计算不起作用。

例如,您可以轻松地将函数应用于普通列。因此,计算 AtomicWeight 列的平均值很容易

julia> mean(ptable[:, :AtomicWeight])
17.77312

但是,由于 Discovered 列中存在 missing 值,因此您无法将函数应用于它

julia> mean(ptable[:, :Discovered])
missing

因为只有一个字段包含缺失值,所以整个计算被放弃,并且缺失值传播回顶层。

有两种方法可以解决此问题:编辑表格以将缺失值转换为实际值,或者在运行计算时确保不包含相关字段。

我们还需要解决年份 0 的问题……

在 DataFrames 中查找缺失值和其他值

[编辑 | 编辑源代码]

要在 DataFrame 中查找缺失值,您可以尝试使用 ismissing() 函数编写代码。这允许您测试 DataFrame 单元格是否存在缺失值

nrows, ncols = size(ptable)
for row in 1:nrows
      for col in 1:ncols
        if ismissing(ptable[row,col])
         println("$(names(ptable)[col]) value for $(ptable[row,:Name]) is missing!")
        end
      end
end
Discovered value for Iron is missing!

您可能会想到使用类似的代码来检查 Discovered 的 0 值,并将它们替换为 missing(如果您认为这是将元素标记为在特定年份未发现的好方法)。但这不起作用

for row in 1:nrows
   if ptable[row, :Discovered] == 0
       println("it's zero")
   end
end

因为

TypeError: non-boolean (Missings.Missing) used in boolean context

这里的问题在于,您正在布尔比较中检查每个单元格的值,但您不能将缺失值与数字进行比较:任何比较都必须返回“无法比较它们”,而不是简单地返回truefalse

相反,您可以这样编写循环

for row in 1:nrows
    if ismissing(ptable[row, :Discovered])
        println("skipping missing values")
    else
        println("the value is $(ptable[row, :Discovered])")
    end
end
the value is 1776
the value is 1895
the value is 0
the value is 1774
skipping missing values

ismissing()函数在其他上下文中也很有用。这是一种快速定位列中缺失值索引号的方法(注意.广播运算符)

julia> findall(ismissing.(ptable[:, :Discovered])) 
1-element Array{Int64,1}:
5

下一行返回一个新的 DataFrame,其中所有行中“Discovered”列都包含缺失值

julia> ptable[findall(ismissing.(ptable[:,:Discovered])), :]
 1×5 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │
├─────┼────────┼────────┼──────────────┼────────┼────────────┤
│ 1   │ 26     │ Iron   │ 55.845       │ Fe     │ missing    │

通过使用!ismissing,您可以返回一个不包含缺失值的 DataFrame。

您可以使用ismissing()选择特定列中包含缺失值的行,并将它们全部设置为新值。例如,此代码查找缺失的发现年份并将它们设置为0

julia> ptable[ismissing.(ptable[:, :Discovered]), :Discovered] .= 0
0
julia> ptable
5×5 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ 0          │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ 0          │

修复 DataFrame

[编辑 | 编辑源代码]

要清理您的数据,您可以编写一段简短的代码来更改不可接受的值。此代码查看每个单元格,并将任何“n/a”、“0”或 0 值更改为missing。请注意,第一个测试是ismissing()——它处理元素已经是缺失值的情况;这些情况将被跳过(否则后面的比较可能会失败)。

for row in 1:size(ptable, 1) # or nrow(ptable)
   for col in 1:size(ptable, 2) # or ncol(ptable)
       println("processing row $row column $col ")
       temp = ptable[row,col]
       if ismissing(temp)
          println("skipping missing")
       elseif temp == "n/a" || temp == "0" || temp == 0
          ptable[row, col] = missing
          println("changed row $row column $col ")
       end
    end
end
processing row 1 column 1
processing row 1 column 2
processing row 1 column 3
processing row 1 column 4
processing row 1 column 5
processing row 2 column 1
processing row 2 column 2
processing row 2 column 3
processing row 2 column 4
processing row 2 column 5
processing row 3 column 1
processing row 3 column 2
processing row 3 column 3
processing row 3 column 4
processing row 3 column 5
changed row 3 column 5
processing row 4 column 1
processing row 4 column 2
processing row 4 column 3
processing row 4 column 4
processing row 4 column 5
processing row 5 column 1
processing row 5 column 2
processing row 5 column 3
processing row 5 column 4
processing row 5 column 5
changed row 5 column 5
julia> ptable
5×5 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │

现在“Discovered”列有两个缺失值,因为碳的发现日期现在也被认为是未知的。

处理缺失值:completecases() 和 dropmissing()

[编辑 | 编辑源代码]

例如,要查找“Discovered”列的最大值(我们知道它包含缺失值),可以使用completecases()函数。它接收一个 DataFrame 并返回标志以指示哪些行有效。然后,这些标志可用于选择保证不包含缺失值的行

julia> maximum(ptable[completecases(ptable), :].Discovered)
1895

这使您可以编写按预期工作的代码,因为包含一个或多个缺失值的行将被排除在考虑范围之外。

dropmissing()函数返回一个不包含缺失值的 DataFrame 副本。

julia> dropmissing(ptable)
3×5 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │
│     │ Int64  │ String   │ Float64      │ String │ Int64      │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │
│ 3   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │

因此,completecases()方法的另一种方法是

julia> maximum(dropmissing(ptable).Discovered)
1895

修改 DataFrame

[编辑 | 编辑源代码]

添加、删除和重命名列

[编辑 | 编辑源代码]

要添加一列,您可以这样做

hcat(ptable, axes(ptable, 1))

它会从 1 到 n 添加另一列整数(将被称为:x1)。(这会创建 DataFrame 的副本,我们没有更改ptable或将新 DataFrame 分配给符号。

相反,让我们添加我们选择的元素的熔点和沸点

julia> ptable[!, :MP] = [-259, -272, 3500, -218, 1535] # notice the !
julia> ptable[!, :BP] = [-253, -269, 4827, -183, 2750]
julia> ptable
5×7 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ MP    │ BP    │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │

注意在更改列时访问列的不同语法。如果我们只想查看值,可以使用[:, ColumnName],它提供 DataFrame 的只读视图。如果要更改值,请使用[!, ColumnName]!是通常的 Julia 提示,指示可能修改数据参数的函数。

为了说明如何基于其他列中的内容创建新列,我们将添加一个名为Liquid的列,显示元素保持液态的摄氏度数(即 BP - MP)

julia> ptable[!, :Liquid] = map((x, y) -> y - x, ptable[:, :MP], ptable[:, :BP])
 5-element Array{Int64,1}:
     6
     3
  1327
    35
  1215

(或者简单地

julia> ptable[!, :Liquid] = ptable[:, :BP] - ptable[:, :MP]

)

5×8 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ MP    │ BP    │ Liquid │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │

要使用另一列数据(即正确长度的数组)添加或替换 DataFrame 的一列,请使用

julia> ptable[!, :Temp] = axes(ptable, 1)
Base.OneTo(5)

让我们真正地做一下

julia> ptable[!, :Temp] = map((x, y) -> y * x, ptable[:, :Liquid], ptable[:, :AtomicWeight])
5×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ MP    │ BP    │ Liquid │ Temp    │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ 6.0474  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ 12.0078 │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 15938.2 │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ 559.979 │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 67851.7 │

Temp 中的值被原子量乘以液态范围的结果所替换。

您可能希望添加一列,显示过时的华氏单位中的熔点

julia> ptable[!, :MP_in_F] = map(deg -> 32 + (deg * 1.8), ptable[:, :MP])
5×10 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ MP    │ BP    │ Liquid │ Temp    │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ 6.0474  │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ 12.0078 │ -457.6  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 15938.2 │ 6332.0  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ 559.979 │ -360.4  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 67851.7 │ 2795.0  │

重命名列很容易

julia> rename!(ptable, :Temp => :Junk)

julia> rename!(ptable, [f => t for (f, t) = zip([:MP, :BP], [:Melt, :Boil])])
 5×10 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ Junk    │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ 6.0474  │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ 12.0078 │ -457.6  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 15938.2 │ 6332.0  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ 559.979 │ -360.4  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 67851.7 │ 2795.0  │

(还有一个rename()函数(没有感叹号),它不会更改原始 DataFrame。)

select!()函数创建一个包含所选列的新 DataFrame。因此,要删除列,请将select!()Not一起使用,后者会取消选择您不想包含的列。

julia> select!(ptable, Not(:Junk))
5×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ -457.6  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 6332.0  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ -360.4  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 2795.0  │

添加和删除行

[编辑 | 编辑源代码]

添加行很容易。使用push!和合适长度和类型的合适数据

julia> push!(ptable, [29, "Copper", 63.546, "Cu", missing, 1083, 2567, 2567-1083, map(deg -> 32 + (deg * 1.8), 1083)])
julia> ptable
6×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ -457.6  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 6332.0  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ -360.4  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 2795.0  │
│ 6   │ 29     │ Copper   │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

这些缺失值应该很快就会使用前面介绍的函数进行替换。我们可以通过名称找到新元素,并像这样更改:Liquid的值

julia>  ptable[[occursin(r"Copper", elementname) for elementname in ptable[:, :Name]], :][:, :Liquid] .= 2567 - 1083
1-element view(::Array{Int64,1}, :) with eltype Int64:
 14843

或者我们可以使用原子序数访问正确的行并以这种方式进行操作

julia> ptable[ptable[!, :Number] .== 6, :][:, :Liquid] .= 4827 - 3500
1-element view(::Array{Int64,1}, :) with eltype Int64:
 1327

要删除行,请使用delete!()函数(谨慎使用),以及一个或多个行说明符

julia> temp = select(ptable, r".") # make a copy
julia> delete!(temp, 3:5)
3×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ -457.6  │
│ 3   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 2795.0  │

或者,您可以通过指定条件来删除行。例如,要保留沸点低于 100 摄氏度的行,您只需找到大于或等于 100 摄氏度的行,然后分配一个变量来保存结果

julia> ptable1 = ptable[ptable[:, :Boil] .>= 100, :] 
3×9 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 6      │ Carbon │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 6332.0  │
│ 2   │ 26     │ Iron   │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 2795.0  │
│ 3   │ 29     │ Copper │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

在 DataFrame 中查找值

[编辑 | 编辑源代码]

您可以使用子集ByRow()查找包含值的行,以将函数应用于值向量。

这查找:Name字段等于“Iron”的行。

julia> subset(ptable, [:Name] => ByRow((nm) -> nm == "Iron")) 
1×8 DataFrame
 Row │ Number  Name    AtomicWeight  Symbol  Discovered  Melt   Boil ⋯
     │ Int64   String  Float64       String  Int64?      Int64  Int6 ⋯
─────┼────────────────────────────────────────────────────────────────
   1 │     26  Iron          55.845  Fe               0   1535   275 ⋯

这查找名称中ro字母的出现。

julia> subset(ptable, [:Name] => ByRow((x) -> occursin("ro", x)))
2×8 DataFrame
 Row │ Number  Name      AtomicWeight  Symbol  Discovered  Melt   Bo ⋯
     │ Int64   String    Float64       String  Int64?      Int64  In ⋯
─────┼────────────────────────────────────────────────────────────────
   1 │      1  Hydrogen        1.0079  H             1776   -259   - ⋯
   2 │     26  Iron           55.845   Fe               0   1535   2

您可以通过将函数与&&||组合来查找不同列中的不同值。这将查找 Number 为 2 或 Name 为“Carbon”的所有行。

julia subset(ptable, [:Number, :Name] => ByRow((n, nm) -> n == 2 || nm == "Carbon"))
2×8 DataFrame
 Row │ Number  Name    AtomicWeight  Symbol  Discovered  Melt   Boil ⋯
     │ Int64   String  Float64       String  Int64?      Int64  Int6 ⋯
─────┼────────────────────────────────────────────────────────────────
   1 │      2  Helium        4.0026  He            1895   -272   -26 ⋯
   2 │      6  Carbon       12.0107  C                0   3500   482

使用索引

[编辑 | 编辑源代码]

要查找值,基本思想是使用逐元素运算符或函数检查所有行,并返回布尔值数组以指示每个单元格是否满足每行的条件

julia> ptable[:, :Melt] .< 100
6-element BitArray{1}:
 1
 1
 0
 1
 0
 0

然后使用此布尔数组选择行

julia> ptable[ptable[:, :Melt] .< 100, :]
3×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ -457.6  │
│ 3   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ -360.4  │

您可以使用它来返回列中值与正则表达式匹配的行

julia> ptable[[occursin(r"Co", elementname) for elementname in ptable[:, :Name]], :]
1×9 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 29     │ Copper │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

您可以以相同的方式编辑元素

julia> ptable[[occursin(r"Copper", elementname) for elementname in ptable.Name], :][:, :Liquid] .= Ref(2567 - 1083)
1-element view(::Array{Int64,1}, :) with eltype Int64:
 1484

6×9 DataFrame
│ Row │ Number │ Name     │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String   │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼──────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 1      │ Hydrogen │ 1.0079       │ H      │ 1776       │ -259  │ -253  │ 6      │ -434.2  │
│ 2   │ 2      │ Helium   │ 4.0026       │ He     │ 1895       │ -272  │ -269  │ 3      │ -457.6  │
│ 3   │ 6      │ Carbon   │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 6332.0  │
│ 4   │ 8      │ Oxygen   │ 15.9994      │ O      │ 1774       │ -218  │ -183  │ 35     │ -360.4  │
│ 5   │ 26     │ Iron     │ 55.845       │ Fe     │ missing    │ 1535  │ 2750  │ 1215   │ 2795.0  │
│ 6   │ 29     │ Copper   │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

要查找匹配的条目

julia> ptable[occursin.("Copper", ptable.Name), :]
1×9 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 29     │ Copper │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │
julia> ptable[occursin.(r"C.*", ptable.Name), :]
2×9 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 6      │ Carbon │ 12.0107      │ C      │ missing    │ 3500  │ 4827  │ 1327   │ 6332.0  │
│ 2   │ 29     │ Copper │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

julia> ptable[occursin.(r"Co", ptable.Name), :]
1×9 DataFrame
│ Row │ Number │ Name   │ AtomicWeight │ Symbol │ Discovered │ Melt  │ Boil  │ Liquid │ MP_in_F │
│     │ Int64  │ String │ Float64      │ String │ Int64?     │ Int64 │ Int64 │ Int64  │ Float64 │
├─────┼────────┼────────┼──────────────┼────────┼────────────┼───────┼───────┼────────┼─────────┤
│ 1   │ 29     │ Copper │ 63.546       │ Cu     │ missing    │ 1083  │ 2567  │ 1484   │ 1981.4  │

子集和组

[编辑 | 编辑源代码]

要调查子集和分组,让我们重新创建 DataFrame

julia> ptable = DataFrame(
          Number       = [1,  2,  6,  8,  26,     29,     ],
          Name         = ["Hydrogen",     "Helium",   "Carbon",   "Oxygen",   "Iron",     "Copper",  ],
          AtomicWeight = [1.0079,     4.0026,     12.0107,    15.9994,    55.845,     63.546,     ],
          Symbol       = ["H",    "He",   "C",    "O",    "Fe",   "Cu",  ],
          Discovered   = [1776,   1895,   0,  1774,   0,  missing,    ],
          Melt         = [-259,   -272,   3500,   -218,   1535,   1083,   ],
          Boil         = [-253,   -269,   4827,   -183,   2750,   2567,   ],
          Liquid       = [6,  3,  1327,   35,     1215,   1484,   ],
      )

并添加另一列

julia> ptable[!, :Room] = [:Gas, :Gas, :Solid, :Gas, :Solid, :Solid]
 6-element Array{Symbol,1}:
  :Gas  
  :Gas  
  :Solid
  :Gas
  :Solid
  :Solid
华夏公益教科书