7 操縱資料 - 表格
7.1 簡介
Arrow 專案的目標之一是減少不同資料框實作之間的重複性。資料框的底層實作是一個概念上與你用來處理它的程式碼或應用程式程式介面 (API) 不同的東西。
你可能看過像 dbplyr 的套件,它允許你使用 dplyr API 與 SQL 資料庫互動。
Arrow R 套件編寫的目的在於,讓底層的類 Arrow Table 的物件能夠使用 dplyr API 進行操作,這允許你使用 dplyr 動詞。
例如,以下是一個僅使用 dplyr 的簡短資料處理管道
library(dplyr)
%>%
starwars filter(species == "Human") %>%
mutate(height_ft = height/30.48) %>%
select(name, height_ft)
## # A tibble: 35 × 2
## name height_ft
## <chr> <dbl>
## 1 Luke Skywalker 5.64
## 2 Darth Vader 6.63
## 3 Leia Organa 4.92
## 4 Owen Lars 5.84
## 5 Beru Whitesun lars 5.41
## 6 Biggs Darklighter 6.00
## 7 Obi-Wan Kenobi 5.97
## 8 Anakin Skywalker 6.17
## 9 Wilhuff Tarkin 5.91
## 10 Han Solo 5.91
## # ℹ 25 more rows
以及透過 dplyr 語法使用 Arrow 產生的結果相同
arrow_table(starwars) %>%
filter(species == "Human") %>%
mutate(height_ft = height/30.48) %>%
select(name, height_ft) %>%
collect()
## # A tibble: 35 × 2
## name height_ft
## <chr> <dbl>
## 1 Luke Skywalker 5.64
## 2 Darth Vader 6.63
## 3 Leia Organa 4.92
## 4 Owen Lars 5.84
## 5 Beru Whitesun lars 5.41
## 6 Biggs Darklighter 6.00
## 7 Obi-Wan Kenobi 5.97
## 8 Anakin Skywalker 6.17
## 9 Wilhuff Tarkin 5.91
## 10 Han Solo 5.91
## # ℹ 25 more rows
您會注意到我們在上面的 Arrow 管道中使用了 collect()
。對於 Arrow 的運作效率之一,是因為它找出需要執行的運算(表達式)的指示,並且只在您實際拉取資料進入 R 階段後才使用 Arrow 執行計算。表示它並非執行許多單獨的運算,而是同時執行所有運算,並使用更最佳化的方式進行。這稱為惰性求值。
如果僅在您選擇所需子集,或使用可針對資料區塊進行運算的函數時才將資料拉取至 R,那麼它也表示您能夠處理大於執行程式碼時您機器記憶體的大小資料。
您也可以使用跨越多個檔案分割的資料。例如,您可能有檔案儲存在多個 Parquet 或 Feather 檔案中,並分割在不同的目錄中。您可以透過 open_dataset()
開啟分割或多檔案資料集(如前一章節所述),然後在將任何資料讀入 R 之前使用 Arrow 處理此資料。
7.2 在 Arrow 中使用 dplyr 字句
您想要在 Arrow 中使用 dplyr 字句。
7.2.1 解決方案
library(dplyr)
arrow_table(starwars) %>%
filter(species == "Human", homeworld == "Tatooine") %>%
collect()
## # A tibble: 8 × 14
## name height mass hair_color skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke Sky… 172 77 blond fair blue 19 male mascu…
## 2 Darth Va… 202 136 none white yellow 41.9 male mascu…
## 3 Owen Lars 178 120 brown, gr… light blue 52 male mascu…
## 4 Beru Whi… 165 75 brown light blue 47 fema… femin…
## 5 Biggs Da… 183 84 black light brown 24 male mascu…
## 6 Anakin S… 188 84 blond fair blue 41.9 male mascu…
## 7 Shmi Sky… 163 NA black fair brown 72 fema… femin…
## 8 Cliegg L… 183 NA brown fair blue 82 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list<character>>,
## # vehicles <list<character>>, starships <list<character>>
7.2.3 另請參閱
您可以在「dplyr 簡介」中找到各種 dplyr 字句的範例 - 執行 vignette("dplyr", package = "dplyr")
或在pkgdown 網站上檢視。
您可以在建立 Arrow 物件中看到更多關於使用 arrow_table()
建立 Arrow 表格,以及使用 collect()
將它們視為 R 資料框的資訊。
7.3 在 Arrow 中的 dplyr 字句中使用 R 函數
您要在 Arrow 中的 dplyr 字句中使用 R 函數。
7.3.1 解決方案
arrow_table(starwars) %>%
filter(str_detect(name, "Darth")) %>%
collect()
## # A tibble: 2 × 14
## name height mass hair_color skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Darth Va… 202 136 none white yellow 41.9 male mascu…
## 2 Darth Ma… 175 80 none red yellow 54 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list<character>>,
## # vehicles <list<character>>, starships <list<character>>
7.3.2 討論
Arrow R 套件允許您使用包含基本 R 和許多 tidyverse 函數的表達式的 dplyr 動詞,但在底層呼叫 Arrow 函數。如果您發現任何基本 R 或 tidyverse 函數您希望在 Arrow 中看到其對應函數,請 在專案 JIRA 中開啟問題。
以下套件(以及其他一些套件)有許多函數繫結/對應關係寫在 arrow 中。
如果你試著呼叫沒有 arrow 對應關係的函數,資料將會拉回 R,而且您將會看到一個警告訊息。
library(stringr)
arrow_table(starwars) %>%
mutate(name_split = str_split_fixed(name, " ", 2)) %>%
collect()
## Warning: Expression str_split_fixed(name, " ", 2) not supported in Arrow;
## pulling data into R
## # A tibble: 87 × 15
## name height mass hair_color skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke Sk… 172 77 blond fair blue 19 male mascu…
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
## 4 Darth V… 202 136 none white yellow 41.9 male mascu…
## 5 Leia Or… 150 49 brown light brown 19 fema… femin…
## 6 Owen La… 178 120 brown, gr… light blue 52 male mascu…
## 7 Beru Wh… 165 75 brown light blue 47 fema… femin…
## 8 R5-D4 97 32 <NA> white, red red NA none mascu…
## 9 Biggs D… 183 84 black light brown 24 male mascu…
## 10 Obi-Wan… 182 77 auburn, w… fair blue-gray 57 male mascu…
## # ℹ 77 more rows
## # ℹ 6 more variables: homeworld <chr>, species <chr>, films <list<character>>,
## # vehicles <list<character>>, starships <list<character>>,
## # name_split <chr[,2]>
7.4 在 Arrow 的 dplyr 動詞中使用 Arrow 函數
您想要使用在 Arrow 的 C++ 函式庫中執行的函數,但
- 它沒有對應該基本 R 或 tidyverse 的等效項,或
- 它有對應關係,但您仍然要直接呼叫 C++ 函數
7.4.1 解決方案
arrow_table(starwars) %>%
select(name) %>%
mutate(padded_name = arrow_ascii_lpad(name, options = list(width = 10, padding = "*"))) %>%
collect()
## # A tibble: 87 × 2
## name padded_name
## <chr> <chr>
## 1 Luke Skywalker Luke Skywalker
## 2 C-3PO *****C-3PO
## 3 R2-D2 *****R2-D2
## 4 Darth Vader Darth Vader
## 5 Leia Organa Leia Organa
## 6 Owen Lars *Owen Lars
## 7 Beru Whitesun lars Beru Whitesun lars
## 8 R5-D4 *****R5-D4
## 9 Biggs Darklighter Biggs Darklighter
## 10 Obi-Wan Kenobi Obi-Wan Kenobi
## # ℹ 77 more rows
7.4.2 討論
絕大多數 Arrow C++ 運算函數已對應到它們的基本 R 或 tidyverse 等效項,而且強烈建議您盡可能使用這些對應關係,因為原始函數有良好的說明文件,而且已測試對應版本以確保傳回結果符合預期。
然而,在某些情況下,您可能想要使用沒有基本 R 或 tidyverse 等效項的 Arrow C++ 函式庫中的運算函數。
您可以在 C++ 文件檔 中找到 Arrow C++ 運算函數的說明文件。此說明文件列出所有可用的運算函數、它們需要的任何相關選項類別,以及可與它們一起使用的有效資料型態。
您可以透過呼叫 list_compute_functions()
來列出 R 中所有可用的 Arrow 運算函數。
list_compute_functions()
## [1] "abs" "abs_checked"
## [3] "acos" "acos_checked"
## [5] "add" "add_checked"
## [7] "all" "and"
## [9] "and_kleene" "and_not"
## [11] "and_not_kleene" "any"
## [13] "approximate_median" "array_filter"
## [15] "array_sort_indices" "array_take"
## [17] "ascii_capitalize" "ascii_center"
## [19] "ascii_is_alnum" "ascii_is_alpha"
## [21] "ascii_is_decimal" "ascii_is_lower"
## [23] "ascii_is_printable" "ascii_is_space"
## [25] "ascii_is_title" "ascii_is_upper"
## [27] "ascii_lower" "ascii_lpad"
## [29] "ascii_ltrim" "ascii_ltrim_whitespace"
## [31] "ascii_reverse" "ascii_rpad"
## [33] "ascii_rtrim" "ascii_rtrim_whitespace"
## [35] "ascii_split_whitespace" "ascii_swapcase"
## [37] "ascii_title" "ascii_trim"
## [39] "ascii_trim_whitespace" "ascii_upper"
## [41] "asin" "asin_checked"
## [43] "assume_timezone" "atan"
## [45] "atan2" "binary_join"
## [47] "binary_join_element_wise" "binary_length"
## [49] "binary_repeat" "binary_replace_slice"
## [51] "binary_reverse" "binary_slice"
## [53] "bit_wise_and" "bit_wise_not"
## [55] "bit_wise_or" "bit_wise_xor"
## [57] "case_when" "cast"
## [59] "ceil" "ceil_temporal"
## [61] "choose" "coalesce"
## [63] "cos" "cos_checked"
## [65] "count" "count_all"
## [67] "count_distinct" "count_substring"
## [69] "count_substring_regex" "cumulative_max"
## [71] "cumulative_min" "cumulative_prod"
## [73] "cumulative_prod_checked" "cumulative_sum"
## [75] "cumulative_sum_checked" "day"
## [77] "day_of_week" "day_of_year"
## [79] "day_time_interval_between" "days_between"
## [81] "dictionary_encode" "divide"
## [83] "divide_checked" "drop_null"
## [85] "ends_with" "equal"
## [87] "exp" "extract_regex"
## [89] "fill_null_backward" "fill_null_forward"
## [91] "filter" "find_substring"
## [93] "find_substring_regex" "first"
## [95] "first_last" "floor"
## [97] "floor_temporal" "greater"
## [99] "greater_equal" "hour"
## [101] "hours_between" "if_else"
## [103] "index" "index_in"
## [105] "index_in_meta_binary" "indices_nonzero"
## [107] "invert" "is_dst"
## [109] "is_finite" "is_in"
## [111] "is_in_meta_binary" "is_inf"
## [113] "is_leap_year" "is_nan"
## [115] "is_null" "is_valid"
## [117] "iso_calendar" "iso_week"
## [119] "iso_year" "last"
## [121] "less" "less_equal"
## [123] "list_element" "list_flatten"
## [125] "list_parent_indices" "list_slice"
## [127] "list_value_length" "ln"
## [129] "ln_checked" "local_timestamp"
## [131] "log10" "log10_checked"
## [133] "log1p" "log1p_checked"
## [135] "log2" "log2_checked"
## [137] "logb" "logb_checked"
## [139] "make_struct" "map_lookup"
## [141] "match_like" "match_substring"
## [143] "match_substring_regex" "max"
## [145] "max_element_wise" "mean"
## [147] "microsecond" "microseconds_between"
## [149] "millisecond" "milliseconds_between"
## [151] "min" "min_element_wise"
## [153] "min_max" "minute"
## [155] "minutes_between" "mode"
## [157] "month" "month_day_nano_interval_between"
## [159] "month_interval_between" "multiply"
## [161] "multiply_checked" "nanosecond"
## [163] "nanoseconds_between" "negate"
## [165] "negate_checked" "not_equal"
## [167] "or" "or_kleene"
## [169] "pairwise_diff" "pairwise_diff_checked"
## [171] "partition_nth_indices" "power"
## [173] "power_checked" "product"
## [175] "quantile" "quarter"
## [177] "quarters_between" "random"
## [179] "rank" "replace_substring"
## [181] "replace_substring_regex" "replace_with_mask"
## [183] "round" "round_binary"
## [185] "round_temporal" "round_to_multiple"
## [187] "run_end_decode" "run_end_encode"
## [189] "second" "seconds_between"
## [191] "select_k_unstable" "shift_left"
## [193] "shift_left_checked" "shift_right"
## [195] "shift_right_checked" "sign"
## [197] "sin" "sin_checked"
## [199] "sort_indices" "split_pattern"
## [201] "split_pattern_regex" "sqrt"
## [203] "sqrt_checked" "starts_with"
## [205] "stddev" "strftime"
## [207] "string_is_ascii" "strptime"
## [209] "struct_field" "subsecond"
## [211] "subtract" "subtract_checked"
## [213] "sum" "take"
## [215] "tan" "tan_checked"
## [217] "tdigest" "true_unless_null"
## [219] "trunc" "unique"
## [221] "us_week" "us_year"
## [223] "utf8_capitalize" "utf8_center"
## [225] "utf8_is_alnum" "utf8_is_alpha"
## [227] "utf8_is_decimal" "utf8_is_digit"
## [229] "utf8_is_lower" "utf8_is_numeric"
## [231] "utf8_is_printable" "utf8_is_space"
## [233] "utf8_is_title" "utf8_is_upper"
## [235] "utf8_length" "utf8_lower"
## [237] "utf8_lpad" "utf8_ltrim"
## [239] "utf8_ltrim_whitespace" "utf8_normalize"
## [241] "utf8_replace_slice" "utf8_reverse"
## [243] "utf8_rpad" "utf8_rtrim"
## [245] "utf8_rtrim_whitespace" "utf8_slice_codeunits"
## [247] "utf8_split_whitespace" "utf8_swapcase"
## [249] "utf8_title" "utf8_trim"
## [251] "utf8_trim_whitespace" "utf8_upper"
## [253] "value_counts" "variance"
## [255] "week" "weeks_between"
## [257] "xor" "year"
## [259] "year_month_day" "years_between"
這裡的大部分函數已對應到它們的基本 R 或 tidyverse 等效項,而且可以像平常一樣在 dplyr 查詢中呼叫。對於沒有基本 R 或 tidyverse 等效項的函數,或您想要提供自訂選項,您可以在它們的名稱之前加上前綴「arrow_」來呼叫。
例如,基本 R 的 is.na()
函數等同於 Arrow C++ 運算函數 is_null()
,其中選項 nan_is_null
設為 TRUE
。
在 arrow 中已建立這些函數之間的對應關係(其中 nan_is_null
設為 TRUE
)。
<- data.frame(x = c(1, 2, 3, NA, NaN))
demo_df
arrow_table(demo_df) %>%
mutate(y = is.na(x)) %>%
collect()
## # A tibble: 5 × 2
## x y
## <dbl> <lgl>
## 1 1 FALSE
## 2 2 FALSE
## 3 3 FALSE
## 4 NA TRUE
## 5 NaN TRUE
如果你想呼叫 Arrow 的 `is_null()` 函數,但將 `nan_is_null` 設定為 `FALSE`(因此會在檢查的值為 `NA` 時傳回 `TRUE`,但在檢查的值為 `NaN` 時傳回 `FALSE`),你必須直接呼叫 `is_null()`,並指定 `nan_is_null = FALSE` 選項。
arrow_table(demo_df) %>%
mutate(y = arrow_is_null(x, options = list(nan_is_null = FALSE))) %>%
collect()
## # A tibble: 5 × 2
## x y
## <dbl> <lgl>
## 1 1 FALSE
## 2 2 FALSE
## 3 3 FALSE
## 4 NA TRUE
## 5 NaN FALSE
7.4.2.1 具有選項的運算函數
雖然並非所有 Arrow C++ 運算函數都需要指定選項,但大部分都需要。若要讓這些函數在 R 中運作,它們必須透過 R 套件的 C++ 程式碼連結至適當的 libarrow 選項 C++ 類別。在撰寫本文時,Arrow R 套件開發版本中提供的所有運算函數都已與其選項類別關聯在一起。然而,隨著 Arrow C++ 函式庫功能的擴充,可能會新增尚未具備 R 繫結的運算函數。如果你在 R 套件中找到你想要使用的 C++ 運算函數,請 在 Github 專案中開啟一個議題。
7.5 運算視窗聚集
你想要對分組表格或在像 `filter()` 之類的行向操作中應用聚集(例如 `mean()`)。
7.5.1 解決方案
arrow_table(starwars) %>%
select(1:4) %>%
filter(!is.na(hair_color)) %>%
left_join(
arrow_table(starwars) %>%
group_by(hair_color) %>%
summarize(mean_height = mean(height, na.rm = TRUE))
%>%
) filter(height < mean_height) %>%
select(!mean_height) %>%
collect()
## # A tibble: 29 × 4
## name height mass hair_color
## <chr> <int> <dbl> <chr>
## 1 Luke Skywalker 172 77 blond
## 2 Leia Organa 150 49 brown
## 3 Beru Whitesun lars 165 75 brown
## 4 Wedge Antilles 170 77 brown
## 5 Yoda 66 17 white
## 6 Lobot 175 79 none
## 7 Ackbar 180 83 none
## 8 Wicket Systri Warrick 88 20 brown
## 9 Nien Nunb 160 68 none
## 10 Finis Valorum 170 NA blond
## # ℹ 19 more rows
或者使用 `to_duckdb()`
arrow_table(starwars) %>%
select(1:4) %>%
filter(!is.na(hair_color)) %>%
to_duckdb() %>%
group_by(hair_color) %>%
filter(height < mean(height, na.rm = TRUE)) %>%
to_arrow() %>%
collect()
## # A tibble: 29 × 4
## name height mass hair_color
## <chr> <int> <dbl> <chr>
## 1 Luke Skywalker 172 77 blond
## 2 Finis Valorum 170 NA blond
## 3 Yoda 66 17 white
## 4 Leia Organa 150 49 brown
## 5 Beru Whitesun lars 165 75 brown
## 6 Wedge Antilles 170 77 brown
## 7 Wicket Systri Warrick 88 20 brown
## 8 Cordé 157 NA brown
## 9 Dormé 165 NA brown
## 10 Padmé Amidala 165 45 brown
## # ℹ 19 more rows
7.5.2 討論
Arrow 不支援視窗函數,並將資料提取到 R。對於大型表格,這會犧牲效能。
arrow_table(starwars) %>%
select(1:4) %>%
filter(!is.na(hair_color)) %>%
group_by(hair_color) %>%
filter(height < mean(height, na.rm = TRUE))
## Warning: Expression height < mean(height, na.rm = TRUE) not supported in Arrow;
## pulling data into R
## # A tibble: 29 × 4
## # Groups: hair_color [5]
## name height mass hair_color
## <chr> <int> <dbl> <chr>
## 1 Luke Skywalker 172 77 blond
## 2 Leia Organa 150 49 brown
## 3 Beru Whitesun lars 165 75 brown
## 4 Wedge Antilles 170 77 brown
## 5 Yoda 66 17 white
## 6 Lobot 175 79 none
## 7 Ackbar 180 83 none
## 8 Wicket Systri Warrick 88 20 brown
## 9 Nien Nunb 160 68 none
## 10 Finis Valorum 170 NA blond
## # ℹ 19 more rows
你可以透過以下方式對 arrow 表格執行這些視窗聚集運算:
- 個別運算聚集,並加入結果
- 將資料傳遞到 DuckDB,並使用 DuckDB 查詢引擎執行運算
Arrow 支援與 DuckDB 的零複製整合,DuckDB 可以直接查詢 Arrow 資料集,並將查詢結果串流回 Arrow。此整合使用 DuckDB 與 Arrow 之間的零複製資料串流,讓你可以在两者之間組合查詢,而且在前後傳遞資料時不需要負擔(重新)序列化資料的成本。這在 Arrow 或 DuckDB 查詢引擎其中一種支援某項功能,但另一種不支援時特別有用。你可以在 Arrow 部落格文章 中找到更多關於此整合的資訊。