7 数据操作 - 表格
7.1 简介
Arrow 项目的目标之一是减少不同数据框实现之间的重复。数据框的底层实现与您编写用于与其交互的代码或应用程序编程接口 (API) 是一个概念上不同的东西。
您可能在 dbplyr 等包中见过这种情况,它允许您使用 dplyr API 与 SQL 数据库进行交互。
Arrow R 包的编写是为了使用 dplyr API 操作底层的 Arrow 表格类对象,这使您可以使用 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
以及使用 Arrow 和 dplyr 语法获得相同结果
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 函数。如果您发现任何您希望在 Arrow 中看到映射的基本 R 或 tidyverse 函数,请 在项目 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 的 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 等效项,我们强烈建议您尽可能使用这些映射,因为原始函数有详细的文档记录,并且已测试映射版本以确保返回的结果符合预期。
但是,在某些情况下,您可能希望使用 Arrow C++ 库中没有基本 R 或 tidyverse 等效项的计算函数。
您可以在 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 博客文章 上找到有关此集成的更多信息。