跳轉到內容

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
華夏公益教科書