
Packages
Background
前回の記事で、シミュレーションの生の結果のparquetファイルを処理するときにduckplyrを使ったので、少しだけ調べた使い方をメモ。
Contents
DuckDB/duckplyr
データベースのことは全く知らない・わからないんですが、DuckDB1はデータベース管理システムの一種だそうです。DuckDBはいろんな言語で使うことができて、Rにもduckdbパッケージ2が存在しています。そして、DuckDBのシステムをdplyrパッケージの書き方で動かせるのがduckplyrパッケージ3です。SQLの文法とか知らなくても、tidyverseに慣れていればほとんど問題なく使えるので便利です。 DuckDB自体の存在はgibasaパッケージによるテキストマイニングのチュートリアルページ4で知りました。メモリに乗りきらない大規模データとかを扱えます。というか、そもそも前回の記事で使ったのも、RAMには乗るけどRでちょっとでもデカすぎるdfを呼び出そうとするとコンソールが固まるからでした。
この記事で使うパッケージのバージョンは以下の通りです。
Read / write parquet files
read / write a single parquet file
parquetファイルの読み込みはduckplyr::read_parquet_duckdb()を使います。前回の記事でもこれでparquetファイルを読み込みました。
# A duckplyr data frame: 8 variables
idx_skew n_total n_ratio var_ratio ftest levene bf nplevene
<int> <int> <int> <chr> <dbl> <dbl> <dbl> <dbl>
1 0 12 1 5/1 0.000547 0.000606 0.000710 1
2 0 12 1 5/1 0.0513 0.118 0.121 1
3 0 12 1 5/1 0.0977 0.320 0.344 1
4 0 12 1 5/1 0.104 0.153 0.316 1
5 0 12 1 5/1 0.00232 0.0137 0.0225 1
6 0 12 1 5/1 0.00494 0.00379 0.107 1
7 0 12 1 5/1 0.171 0.381 0.420 1
8 0 12 1 5/1 0.558 0.642 0.590 1
9 0 12 1 5/1 0.214 0.124 0.123 1
10 0 12 1 5/1 0.0222 0.0147 0.0176 1
# ℹ more rows
元ファイルは2.7GBくらいあって、R上でdfとして持っていてhead()とかなしに呼び出そうとしたりコード補完で選ぼうとしたりすると、すーぐ固まります。一方、こちらはファイルの10行目までがすぐに返ってきます。というのも、この時点では実体としてデータを持っていないので速いらしいです。引数prudenceを設定することで、DuckDBが管理するRAM上にデータを実体化するかどうか選べます。デフォルトだと"thrifty"が入るっぽくて、100万セルくらいのものまでは実体化するっぽい?"lavish"だとサイズに関わらず実体化、"stingy"だと実体化しないようです。詳しくは実体化に関するvignette5参照のこと。
ちなみに戻り値のクラスにはduckplyr_dfが追加されていて、さらに、prudence = "stingy"の場合にはprudent_duckplyr_dfも追加されます。
[1] "prudent_duckplyr_df" "duckplyr_df" "tbl_df" "tbl"
[5] "data.frame"
parquetファイルの書き込みは、duckplyr::compute_parquet()を使います。引数はx, path, ...の順番なので、パイプフローでできますね。
# A duckplyr data frame: 5 variables
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <chr>
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
# ℹ more rows
[1] "test_parquet_iris.parquet"
引数optionsにはlist形式で書き込むときのオプション6を設定できるようです。例えば圧縮形式(COMPRESSION)はデフォルトだと"snappy"になっているようです(初めてこの圧縮形式を知りました)。
read multiple parquet files
duckplyr::read_parquet_duckdb()は複数のparquetファイルを一度に読み込んでくれます。まずはデモデータを作ります。シミュレーションで3つの検定を3つの条件で100回ずつ反復した想定です。
data.frame(
cond = rep(1:3, each = 100L),
matrix(
runif(100L * 3 * 3),
ncol = 3,
dimnames = list(NULL, paste0("test_", 1:3))
)
) |>
group_by(cond) |>
group_walk(
.f = \(x, idx) {
x |>
duckplyr::compute_parquet(
path = fs::path_temp(paste0("cond_", idx$cond)) |>
fs::path(ext = "parquet")
)
},
.keep = TRUE
)[1] "cond_1.parquet" "cond_2.parquet" "cond_3.parquet"
3つparquetファイルがありますね。これを一気に読み込みます。
# A duckplyr data frame: 4 variables
cond test_1 test_2 test_3
<int> <dbl> <dbl> <dbl>
1 1 0.503 0.855 0.693
2 1 0.132 0.130 0.789
3 1 0.137 0.635 0.720
4 1 0.0184 0.635 0.698
5 1 0.995 0.456 0.688
6 1 0.492 0.809 0.722
7 1 0.741 0.214 0.169
8 1 0.946 0.415 0.214
9 1 0.287 0.917 0.448
10 1 0.781 0.216 0.568
# ℹ more rows
一見すると1ファイルしか読み込まれていないような気がしますが、確認すると3つのファイルが1つにまとめられていることがわかります。
Compatible functions
上のチャンクでちゃっかり使っちゃいましたが、dplyrやbase R のいくつかの関数は特に気にせずそのまま使えます。これは内部でDuckDBの関数に変換されて実行されているからのようです。「え?dplyr::ってついてるじゃん」と思うかもしれませんが、ちゃんとDuckDBで処理されています。duckplyr::stats_show()を使ってみてみましょう。この関数は、DuckDBで処理された数とdplyrで処理された数を示してくれます。
上から順に、このセッションでのduckplyrへのリクエスト数、dplyrによる処理数、duckdbによる処理数です。🦆の下に並んだ文字はduckplyrで上書きしたメソッドっぽい(?)です。コンソール上ではかわいいEmojiで表示されているのですがどうでしょうか?7
では、先ほどの処理をしてからもう一度実行してみましょう。
# A duckplyr data frame: 2 variables
cond n
<int> <int>
1 1 100
2 2 100
3 3 100
🛠: 8
🔨: 1
🦆: 7
count, group_by.data.frame, head
はい、duck🦆のところだけ数字が増えていますね。したがって、処理はdplyrではなくduckdbの方で行われていたわけです。各condごとのデータ数をcount()で計算し、結果の表示をhead()で見せているので+2回のようです。
duckplyrで変換される関数はduckplyrのArticles “Translations”8に載っています。そこに載っているRの関数はduckplyrによって変換されるので、クラスがprudent_duckplyr_dfのままでも処理ができるようです。制約・互換性が取れていない部分についても書いてありました。
Fallback
なお今回の結果にはdplyrに1と入っていましたが、これはdplyr→duckplyrの順にパッケージを読み込こんでいる状態だとduckplyrを優先的に使い、使えない場合はdplyrへ自動的にfallbackするからです9。デモデータを作る際に使ったdplyr::group_by()はduckplyrがサポートしていないため、その処理は自動的にdplyrによる処理になります。以下の例で実演してみます。
🛠: 8
🔨: 1
🦆: 7
count, group_by.data.frame, head
🛠: 9
🔨: 2
🦆: 7
count, group_by.data.frame, head
duckplyr側にはgroup_by()のリクエストが飛んでいるのでリクエスト数は1増えますが、duckplyrはその処理を行えないためにfallbackされて、dplyrの処理数が1増えました10。一旦パッケージをアンロードして、pkg::fnの形で処理すると、また違った結果になります。
🛠: 0
🔨: 0
🦆: 0
🛠: 0
🔨: 0
🦆: 0
この場合はduckplyr側にそもそもリクエストが飛んでいないので、リクエスト数が計上されないようです。
library(duckplyr)をすると、セッション全体でduckplyrを優先して使うようになるので、それを避けたい場合はパッケージを呼び出さずduckplyr::fnの形で関数を使うか、library(duckplyr)の後にduckplyr::methods_restore()を実行してdplyrを優先的に使うようにするか、どちらかをせよとのことだそうです11。
Example
さっきアンロードしたので読み込みなおし。
さて、互換性ありの関数を使う例として、検定の経験的棄却率(ERR)を求めてみます。これは通る書き方。
- 1
- サイトで説明されていない(多分)けど、v1.0.0のChangelogで部分的にサポートと載っていたのとGithubのコードでそれっぽいのがあったので実は使える。
- 2
-
dplyr::everything()は、外側の集計関数で引数.byを使っているときには.byに含まれていない変数をすべて選んでくれるはずだけど、それで書くとエラーを吐いたので今回はdplyr::starts_with()で指定。 - 3
- 実はこの書き方はちょっと危ない。後述。
- 4
-
DuckDB側の制約として戻り値の順序が保証されないとのことなので、
dplyr::arrange()で整える。
# A duckplyr data frame: 4 variables
cond test_1 test_2 test_3
<int> <dbl> <dbl> <dbl>
1 1 0.07 0.05 0.07
2 2 0.07 0.02 0.04
3 3 0.01 0.05 0.07
[1] "prudent_duckplyr_df" "duckplyr_df" "tbl_df" "tbl"
[5] "data.frame"
prudentなままできましたね。
変換されない関数を使ってみます。ERRを求めるときにはSimDesign::ERR()やsimhelpers::calc_rejection()を使うと便利(特に後者はMCSEも出してくれる)のですが、これらは変換されない関数なのでprudent_duckplyr_dfには適用できずエラーが出ます。
Error in `summarise()`:
! This operation cannot be carried out by DuckDB, and the input is a stingy duckplyr frame.
ℹ Use `compute(prudence = "lavish")` to materialize to temporary storage and continue with
duckplyr.
ℹ See `vignette("prudence")` for other options.
Caused by error in `summarise()`:
! Can't translate function `SimDesign::ERR()`.
実体化されていないデータに対して変換不可な関数を使うには、その関数を使う前に実体化する必要があります。エラーに出ているcompute(..., prudence = "lavish")(正確にはduckplyr:::compute.duckplyr_df)やduckplyr::as_duckdb_tibble(..., prudence = "lavish")、dplyr::collect()が使えます。
# A tibble: 3 × 4
cond test_1 test_2 test_3
<int> <dbl> <dbl> <dbl>
1 1 0.07 0.05 0.07
2 2 0.07 0.02 0.04
3 3 0.01 0.05 0.07
[1] "tbl_df" "tbl" "data.frame"
prudentなままのときと同じ数値ですね。ただしクラスから(prudent_)duckplyr_dfは抜けています。
通らない書き方。関数の複数行記述に必要な{}(curly brackets/ braces)は変換されないらしく、無名関数は{}なしの1行で書く必要があります。
Error in `summarise()`:
! This operation cannot be carried out by DuckDB, and the input is a stingy duckplyr frame.
ℹ Use `compute(prudence = "lavish")` to materialize to temporary storage and continue with
duckplyr.
ℹ See `vignette("prudence")` for other options.
Caused by error in `summarise()`:
! Can't translate function `` `{`() ``.
これも通らない書き方。base Rだとmean(lgl)はTRUE = 1, FALSE = 0で計算してくれるので\(x) mean (x < alpha, na.rm = TRUE)でERRを求められるのですが、DuckDBだとそれができないらしくエラーになります。
Error in `summarise()`:
! This operation cannot be carried out by DuckDB, and the input is a stingy duckplyr frame.
ℹ Use `compute(prudence = "lavish")` to materialize to temporary storage and continue with
duckplyr.
ℹ See `vignette("prudence")` for other options.
Caused by error in `rel_aggregate()`:
! {"exception_type":"Binder","exception_message":"No function matches the given name and argument types 'avg(BOOLEAN)'. You might need to add explicit type casts.\n\tCandidate functions:\n\tavg(DECIMAL) -> DECIMAL\n\tavg(SMALLINT) -> DOUBLE\n\tavg(INTEGER) -> DOUBLE\n\tavg(BIGINT) -> DOUBLE\n\tavg(HUGEINT) -> DOUBLE\n\tavg(INTERVAL) -> INTERVAL\n\tavg(DOUBLE) -> DOUBLE\n\tavg(TIMESTAMP) -> TIMESTAMP\n\tavg(TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\tavg(TIME) -> TIME\n\tavg(TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n","error_subtype":"NO_MATCHING_FUNCTION","catalog":"system","schema":"main","call":"avg(BOOLEAN)","position":"105","candidates":"avg(DECIMAL) -> DECIMAL,avg(SMALLINT) -> DOUBLE,avg(INTEGER) -> DOUBLE,avg(BIGINT) -> DOUBLE,avg(HUGEINT) -> DOUBLE,avg(INTERVAL) -> INTERVAL,avg(DOUBLE) -> DOUBLE,avg(TIMESTAMP) -> TIMESTAMP,avg(TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE,avg(TIME) -> TIME,avg(TIME WITH TIME ZONE) -> TIME WITH TIME ZONE","name":"avg"}
avg(BOOLEAN)の入力はマッチしなかったと出てますね。いくつか動くであろう候補を挙げてくれてるみたいです。そこで変換可能なdplyr::if_else()を使えば通ります。duckplyrだとis.na(NaN)=FALSEになってしまうとのことなので、NAもNaNもNAで処理できるこっちの書き方の方が安全になるのかな。
# A duckplyr data frame: 4 variables
cond test_1 test_2 test_3
<int> <dbl> <dbl> <dbl>
1 1 0.07 0.05 0.07
2 2 0.07 0.02 0.04
3 3 0.01 0.05 0.07
Using DuckDB functions
変換可能な関数に加えて、dd$<DuckDB function name>の記法でDuckDBの関数が使えるようです12。例えばこんな書き方も通ります。関数はDuckDBのDocumentation > SQL > Functions13からいろいろ探して試してみました。
# A duckplyr data frame: 4 variables
cond test_1 test_2 test_3
<int> <dbl> <dbl> <dbl>
1 1 0.07 0.05 0.07
2 2 0.07 0.02 0.04
3 3 0.01 0.05 0.07
テキストデータも扱えます。gibasaパッケージに収録されている宮沢賢治の銀河鉄道の夜のデータ(gibasa::ginga)で試してみました。
duckplyr::duckdb_tibble(sentence = gibasa::ginga, .prudence = "stingy") |>
1 filter(dd$regexp_matches(sentence, pattern = "^[一二三四五六七八九十]")) |>
mutate(
dd_charlen = dd$len(sentence),
2 dd_left = dd$left(sentence, count = 1L),
dd_right = dd$right(sentence, count = 1L),
dd_extract = dd$regexp_extract(sentence, regex = "[ア-ンー]{1,}")
)- 1
-
grepl()が変換可能な関数に含まれているので、それでやる方が入力量は少ない。 - 2
-
Helpによると
count: BIGINTなので、Lをつけてint型だと明示しないと通らない模様。実際、Lつけないとエラーになった。
duckdb is storing downloaded extensions and secrets under ~/.duckdb:
ℹ C:\Users\takum/.duckdb
This persists across sessions and is shared with the DuckDB CLI and other clients.
ℹ Run duckdb(shared_home = FALSE) to use a temporary directory instead.
ℹ See ?duckdb_storage for details and alternatives.
# A duckplyr data frame: 5 variables
sentence dd_charlen dd_left dd_right dd_extract
<chr> <dbl> <chr> <chr> <chr>
1 一 午後の授業 7 一 業 ""
2 二 活版所 5 二 所 ""
3 三 家 3 三 家 ""
4 四 ケンタウル祭の夜 10 四 夜 "ケンタウル"
5 五 天気輪の柱 7 五 柱 ""
6 六 銀河ステーション 10 六 ン "ステーション"
7 七 北十字とプリオシン海岸 13 七 岸 "プリオシン"
8 八 鳥を捕る人 7 八 人 ""
9 九 ジョバンニの切符 10 九 符 "ジョバンニ"
こんな感じで使えました。他にも、regexp_extract_all()のような1つの入力に対して2つ以上の要素を返す関数にも対応しているようです。その場合には戻り値はlist形式になるので、DuckDB側のlist型を処理してくれる関数を続けて書けば処理できるようです。
- 1
-
unicode文字クラスでの指定。
\p{}が一致するもので\P{}が一致しないもの。Zは区切り文字で全角スペース()も含めてくれる。 - 2
-
dd$regexp_replace()で最初の全角スペースを変えてあげれば、こういう書き方をしなくて済みます。
# A duckplyr data frame: 4 variables
x temp res1 res2
<chr> <list> <chr> <chr>
1 一 午後の授業 <chr [2]> 一:午後の授業 一‐午後の授業
2 二 活版所 <chr [2]> 二:活版所 二‐活版所
3 三 家 <chr [2]> 三:家 三‐家
4 四 ケンタウル祭の夜 <chr [2]> 四:ケンタウル祭の夜 四‐ケンタウル祭の夜
5 五 天気輪の柱 <chr [2]> 五:天気輪の柱 五‐天気輪の柱
6 六 銀河ステーション <chr [2]> 六:銀河ステーション 六‐銀河ステーション
7 七 北十字とプリオシン海岸 <chr [2]> 七:北十字とプリオシン海岸 七‐北十字とプリオシン海岸
8 八 鳥を捕る人 <chr [2]> 八:鳥を捕る人 八‐鳥を捕る人
9 九 ジョバンニの切符 <chr [2]> 九:ジョバンニの切符 九‐ジョバンニの切符
Speed Comparison
実体化ありなしでベンチマークをとってみます。使うデータは前回の記事の生データです。duckplyrの方はstingyなデータ、dplyrの方はR上にtibbleとして持っているデータとします。処理は表1の再現に必要なデータのうちSkew = 0、N=24のデータを絞り込んで計算して並び替えるところまでにします。(なんでそんなに絞り込んでいるのかというと、そうでもしないとベンチマークが終わらないからです。)事前にreprex上で回しておきました。
temp_duckdf <- fs::dir_ls(Sys.getenv("SIM_RES_DIR"), regexp = "nordstokke_zumbo_2007") |>
fs::dir_ls(regexp = "parquet") |>
duckplyr::read_parquet_duckdb(prudence = "stingy")
# check
class(temp_duckdf)
#> [1] "prudent_duckplyr_df" "duckplyr_df" "tbl_df"
#> [4] "tbl" "data.frame"
temp_dplyr_df <- dplyr::collect(temp_duckdf)
# check
class(temp_dplyr_df)
#> [1] "tbl_df" "tbl" "data.frame"
bench::mark(
"duckplyr" = {
temp_duckdf |>
dplyr::select(idx_skew:levene) |>
dplyr::filter(idx_skew == 0, n_total == 24, var_ratio == "1/1") |>
dplyr::select(-c(idx_skew, n_total)) |>
dplyr::summarise(
dplyr::across(
.cols = ftest:levene,
.fns = \(x) mean(dplyr::if_else(x < .05, 1, 0), na.rm = TRUE)
),
.by = c(n_ratio)
) |>
dplyr::arrange(n_ratio) |>
dplyr::relocate(levene, .before = ftest) |>
dplyr::collect() # duckplyr_df to tbl_df
},
"dplyr" = {
temp_dplyr_df |>
dplyr::select(idx_skew:levene) |>
dplyr::filter(idx_skew == 0, n_total == 24, var_ratio == "1/1") |>
dplyr::select(-c(idx_skew, n_total)) |>
dplyr::summarise(
dplyr::across(
.cols = ftest:levene,
.fns = \(x) mean(x < .05, na.rm = TRUE)
),
.by = n_ratio
) |>
dplyr::arrange(n_ratio) |>
dplyr::relocate(levene, .before = ftest)
},
"combine" = {
temp_duckdf |>
dplyr::select(idx_skew:levene) |>
dplyr::filter(idx_skew == 0, n_total == 24, var_ratio == "1/1") |>
dplyr::select(-c(idx_skew, n_total)) |>
dplyr::collect() |> # duckplyr_df to tbl_df
dplyr::summarise(
dplyr::across(
.cols = ftest:levene,
.fns = \(x) mean(x < .05, na.rm = TRUE)
),
.by = n_ratio
) |>
dplyr::arrange(n_ratio) |>
dplyr::relocate(levene, .before = ftest)
},
min_time = Inf,
iterations = 10
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 3 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 duckplyr 633.13ms 645.96ms 1.41 2.75MB 0.141
#> 2 dplyr 9.36s 9.53s 0.104 5.47GB 0.177
#> 3 combine 638.9ms 659.27ms 1.41 31.5MB 0Created on 2026-10-22 with reprex v2.1.1
全部dplyrは時間がかかりすぎています。元のデータは9720万行×6列あるのですが、このようにあまりにデカすぎるデータはRだとちょっと面倒ですね。なるべくduckplyrで処理した場合と、行・列のフィルタリングはduckplyrに任せて計算処理はdplyrにした場合がほぼ同じ処理時間ですね。R上でのアロケーションはduckplyrの方が少ないみたいです。元データの読み込み・フィルタリングだけにduckplyrを使うという方法もありですね。
Conclusion
今回はduckplyrの使い方を少しだけ調べてみました。シミュレーションの生データのような大規模なデータをそのままR上に展開すると結構固まりがちなので、データの読み込みと行・列のフィルタリング処理にはduckplyrを使っていこうかなと思いました。
Session Information
R version 4.5.3 (2026-03-11 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26300)
Matrix products: default
LAPACK version 3.12.1
locale:
[1] LC_COLLATE=Japanese_Japan.utf8 LC_CTYPE=Japanese_Japan.utf8 LC_MONETARY=Japanese_Japan.utf8
[4] LC_NUMERIC=C LC_TIME=Japanese_Japan.utf8
time zone: Asia/Tokyo
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] duckplyr_1.2.1 lubridate_1.9.5 forcats_1.0.1 stringr_1.6.0 dplyr_1.2.1 purrr_1.2.2
[7] readr_2.2.0 tidyr_1.3.2 tibble_3.3.1 ggplot2_4.0.3 tidyverse_2.0.0
loaded via a namespace (and not attached):
[1] gtable_0.3.6 xfun_0.62 htmlwidgets_1.6.4 tzdb_0.5.0
[5] vctrs_0.7.3 tools_4.5.3 generics_0.1.4 parallel_4.5.3
[9] proxy_0.4-29 pacman_0.5.1 pkgconfig_2.0.3 R.oo_1.27.1
[13] RColorBrewer_1.1-3 S7_0.2.2 RcppParallel_6.2.1 lifecycle_1.0.5
[17] compiler_4.5.3 farver_2.1.2 brio_1.1.5 textshaping_1.0.5
[21] codetools_0.2-20 class_7.3-24 htmltools_0.5.9 yaml_2.3.12
[25] pillar_1.11.1 R.utils_2.13.0 cachem_1.1.0 sessioninfo_1.2.4
[29] gibasa_1.1.3 parallelly_1.48.0 qs2_0.3.1 tidyselect_1.2.1
[33] digest_0.6.39 future_1.80.0 stringi_1.8.9 listenv_1.1.0
[37] duckdb_1.5.6 labeling_0.4.3 fastmap_1.2.0 grid_4.5.3
[41] cli_3.6.6 beepr_2.0 magrittr_2.0.5 utf8_1.2.6
[45] future.apply_1.21.0 e1071_1.7-17 clipr_0.8.1 SimDesign_2.28
[49] withr_3.0.3 scales_1.4.0 timechange_0.4.0 rmarkdown_2.32
[53] globals_0.20.0 audio_0.1-12 otel_0.2.0 progressr_1.1.0
[57] ragg_1.5.2 R.methodsS3_1.8.2 hms_1.1.4 stringfish_0.19.2
[61] pbapply_1.7-5 memoise_2.0.1 evaluate_1.0.5 knitr_1.52
[65] testthat_3.3.2 rlang_1.3.0 Rcpp_1.1.2 glue_1.8.1
[69] DBI_1.3.0 collections_0.3.12 rstudioapi_0.19.0 jsonlite_2.0.0
[73] R6_2.6.1 systemfonts_1.3.2 fs_2.1.0
Footnotes
https://cloud.r-project.org/web/packages/duckdb/index.html↩︎
https://cloud.r-project.org/web/packages/duckplyr/index.html↩︎
https://duckplyr.tidyverse.org/articles/prudence.html
R上ならvignette("prudence")でも読める。↩︎https://duckdb.org/docs/current/sql/statements/copy#parquet-options↩︎
duckと聞くと白いアヒルをイメージするのですが、どうやら英語では鴨でもアヒルでもduckなんだとか。WindowsのIMEだと「あひる」「かも」の変換で同じ絵文字(🦆)が出てきて、Win + . の絵文字検索で「あひる」と入力すると白い鳥(🪿)が出てきた。でもカーソルを合わせると「ガチョウ」とでてくる…。↩︎
https://duckplyr.tidyverse.org/articles/limits.html
もしくはvignette("limits")↩︎https://duckplyr.tidyverse.org/articles/fallback.html
もしくはvignette("fallback")↩︎理由はよくわからないですが、結果を明示的に返す
dplyr::group_map()だとリクエスト数は2増えて、明示的に返さないdplyr::group_walk()だとリクエスト数は1増えました。↩︎https://duckplyr.tidyverse.org/articles/developers.html
もしくはvignette("developers")↩︎https://duckplyr.tidyverse.org/articles/duckdb.html#call-arbitrary-functions-in-duckplyr↩︎