library(tidyverse)
Warning: package 'ggplot2' was built under R version 4.2.3
Warning: package 'tibble' was built under R version 4.2.3
Warning: package 'dplyr' was built under R version 4.2.3
library(fs)
2022年6月12日
本記事では,CSVの代替として有望かつビッグデータ分析にも適しているParquetを紹介します.
さて,データフレーム(Data Frames)は,データ分析において最も基本的なデータ構造の1つです.Rのtibble・dplyrやPythonのpandasなどのデータフレーム操作のためのパッケージを使えば,これまでExcelなどの表計算ソフトで行っていたデータ分析をさらに効率的に行うことができます.
このようにデータ分析ツールが充実している一方で,データの保存にはExcelなどとの互換性が高いCSVが未だに広く使われています.しかし,CSVは,必ずしもデータ分析に適したファイル形式とは言えません.そこで,CSVの代替として使われることが多くなっているParquetをCSVと比較してみましょう.
CSVとParquetを比較するため,まずは,データ分析にありがちなサンプルデータを用意しましょう.今回は,tidyrパッケージで提供されているwho
(世界保健機関(WHO)結核データ)からサンプルデータをつくります.
近年,データ分析では,整然データ(tidy data)の概念が普及しています.tidy dataは,個々の変数が1つの列をなし,個々の観測(値)が1つの行をなすようなデータです.
それでは,who
は,tidy dataと言えるでしょうか?who
には,"new_sp_m014"
~"newrel_f65"
といったたくさんの列が存在しますが,これらには,1列ごとに,診断結果(sp
やsel
)・性別(m
とf
)・年齢階級(014
や65
)といった複数の変数が含まれています.そのため,who
は,tidy dataでないといえます.そこで,こちらに従ってtidy dataであるwho_longer
に変形します.
データ分析では,who
よりtidy dataであるwho_longer
のほうを分析が行いやすい一方で,行数はwho
(約7,000行)よりwho_longer
(約400,000行)のほうが約50倍多いことがわかります.そのため,tidy dataであるwho_longer
のようなデータをテキストファイルであるCSVで保存すると容量が増大してしまいます.
このように,tidy dataはデータ分析に適している一方で,CSVのようなテキストファイルでの保存に適していないことがわかります.しかし,このようなデータ保存上の課題はParquetを使えば解決することができます.
ここで,tidy dataでないwho
とtidy dataであるwho_longer
を見比べてみましょう.
Warning: package 'ggplot2' was built under R version 4.2.3
Warning: package 'tibble' was built under R version 4.2.3
Warning: package 'dplyr' was built under R version 4.2.3
levels_gender <- c("f", "m")
levels_age <- c("014", "1524", "2534", "3544", "4554", "5564", "65")
who_longer <- who |>
pivot_longer(cols = new_sp_m014:newrel_f65,
names_to = c("diagnosis", "gender", "age"),
names_pattern = "new_?(.*)_(.)(.*)",
names_transform = list(gender = ~ .x |>
readr::parse_factor(levels = levels_gender),
age = ~ .x |>
readr::parse_factor(levels = levels_age,
ordered = TRUE)),
values_to = "count")
# A tibble: 7,240 × 60
country iso2 iso3 year new_sp_m014 new_sp_m1524 new_sp_m2534 new_sp_m3544
<chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Afghanis… AF AFG 1980 NA NA NA NA
2 Afghanis… AF AFG 1981 NA NA NA NA
3 Afghanis… AF AFG 1982 NA NA NA NA
4 Afghanis… AF AFG 1983 NA NA NA NA
5 Afghanis… AF AFG 1984 NA NA NA NA
# ℹ 7,235 more rows
# ℹ 52 more variables: new_sp_m4554 <dbl>, new_sp_m5564 <dbl>,
# new_sp_m65 <dbl>, new_sp_f014 <dbl>, new_sp_f1524 <dbl>,
# new_sp_f2534 <dbl>, new_sp_f3544 <dbl>, new_sp_f4554 <dbl>,
# new_sp_f5564 <dbl>, new_sp_f65 <dbl>, new_sn_m014 <dbl>,
# new_sn_m1524 <dbl>, new_sn_m2534 <dbl>, new_sn_m3544 <dbl>,
# new_sn_m4554 <dbl>, new_sn_m5564 <dbl>, new_sn_m65 <dbl>, …
# A tibble: 405,440 × 8
country iso2 iso3 year diagnosis gender age count
<chr> <chr> <chr> <dbl> <chr> <fct> <ord> <dbl>
1 Afghanistan AF AFG 1980 sp m 014 NA
2 Afghanistan AF AFG 1980 sp m 1524 NA
3 Afghanistan AF AFG 1980 sp m 2534 NA
4 Afghanistan AF AFG 1980 sp m 3544 NA
5 Afghanistan AF AFG 1980 sp m 4554 NA
# ℹ 405,435 more rows
Rでは,write_csv()
でCSVを保存できます.同様に,arrowパッケージのwrite_parquet()
でParquetを保存することができます.who_longer
をCSVとParquetで保存してみましょう.
CSVとParquetでは,どちらも簡単にデータ保存ができることがわかります.
ここからは,保存したwho_longer
のCSV・Parquetファイルを比較して,CSVに対するParquetのメリットを紹介していきます.
tidy dataは行数が多くなるため,CSVでの保存に適しておらず,Parquetを使ったほうがよいことを既に述べました.
実際に,who_longer
のCSV・Parquetのデータ容量は,それぞれ,14.1 MBと154 KBとなり,ParquetはCSVの約1 %のデータ容量しかないことがわかります.
どのようなケースでもこのようなデータ容量の削減が見込めるわけではありませんが,Parquetは列指向でデータ圧縮を行うため,Rなどでよく用いられるtidy dataの保存に適したデータ形式であるといえます.
write_csv()
・write_parquet()
でデータを書き込めるのと同様に,read_csv()
・read_parquet()
でCSV・Parquetデータを読み込むことができます.
CSVはテキスト形式であるため,読み込み時にcol_types
で各列の型を指定する必要があります(デフォルトでは自動で型を推測).
一方,Parquetは,書き込み時に各列の型情報も保存されているため読み込み時に型を指定する必要がありません.
CSVはビッグデータの保存に適しておらず,これまでは,ビッグデータの保存にはSQLを用いるなどの使い分けが必要でした.
Rでは,dplyr(dbplyr)・DBIなどのパッケージで簡単にSQLが使えますが,データベースへの接続・切断などが必要なSQLは,CSVと使い勝手が異なり,初学者にとってはハードルがあるかもしれません.
また,(ほとんどの?)SQLは行指向であるため,データの追加・更新・削除などに適していますが,データ分析に用いられるデータの保存・集計には列指向であるParquetのほうが適していると思われます.
CSVファイルを用いてビッグデータを集計する場合には,一度,全データをメモリに移す必要があります.そのため,データの読み込みでメモリが逼迫するおそれがあります.
Parquetでは,読み込み時にas_data_frame = FALSE
とすることで,SQLと同様にメモリにデータを移すことなくデータのフィルタリング・集計などが可能です.
ここでは,日本の年・症例別の患者数を計算してみましょう.dplyrのfilter()
・group_by()
・summarise()
などを使って効率的にクエリを作成することができます.最後にcollect()
を行えばデータフレームを出力することができます.
read_parquet("who_longer.parquet",
as_data_frame = FALSE) |>
filter(country == "Japan",
!is.na(count)) |>
group_by(country, year, diagnosis) |>
summarise(count = sum(count),
.groups = "drop") |>
collect()
# A tibble: 33 × 4
country year diagnosis count
<chr> <dbl> <chr> <dbl>
1 Japan 1995 sp 14367
2 Japan 1996 sp 12867
3 Japan 1997 sp 13571
4 Japan 1998 sp 11935
5 Japan 2001 sp 11408
6 Japan 2002 sp 10807
7 Japan 2003 sp 10843
8 Japan 2004 sp 10471
9 Japan 1999 sp 12909
10 Japan 2000 sp 11853
# ℹ 23 more rows
Parquetは列指向であるため,行指向であるSQLと違い,データの追加・更新・削除などに適していません.しかし,Parquetでは,複数のデータからなるデータセットの読み込みが簡単に行えるため,このようなデメリットを簡単に解決することができます.
ここでは,who_longer
を年齢階級別に分割したParquetファイルを格納した"who_longer_byage"
フォルダをデータセットのサンプルとして用いましょう.
open_dataset("who_longer_byage")
とすることで,複数のParquetファイルを含むにもかかわらず,さきほどと同様のデータ集計を簡単に行うことができます.
open_dataset("who_longer_byage") |>
filter(country == "Japan",
!is.na(count)) |>
group_by(country, year, diagnosis) |>
summarise(count = sum(count),
.groups = "drop") |>
collect()
# A tibble: 33 × 4
country year diagnosis count
<chr> <dbl> <chr> <dbl>
1 Japan 1995 sp 14367
2 Japan 1996 sp 12867
3 Japan 1997 sp 13571
4 Japan 1998 sp 11935
5 Japan 2001 sp 11408
6 Japan 2002 sp 10807
7 Japan 2003 sp 10843
8 Japan 2004 sp 10471
9 Japan 2005 sp 10931
10 Japan 2006 sp 10159
# ℹ 23 more rows
PythonのpandasパッケージはParquetの読み書きに対応しているため,Parquetは,R・Python間でのデータのやり取りにも適しています.
Rで作成した'who_longer.parquet'
をpandasで読み込んでみましょう.
country iso2 iso3 year diagnosis gender age count
0 Afghanistan AF AFG 1980.0 sp m 014 NaN
1 Afghanistan AF AFG 1980.0 sp m 1524 NaN
2 Afghanistan AF AFG 1980.0 sp m 2534 NaN
3 Afghanistan AF AFG 1980.0 sp m 3544 NaN
4 Afghanistan AF AFG 1980.0 sp m 4554 NaN
... ... ... ... ... ... ... ... ...
405435 Zimbabwe ZW ZWE 2013.0 rel f 2534 4649.0
405436 Zimbabwe ZW ZWE 2013.0 rel f 3544 3526.0
405437 Zimbabwe ZW ZWE 2013.0 rel f 4554 1453.0
405438 Zimbabwe ZW ZWE 2013.0 rel f 5564 811.0
405439 Zimbabwe ZW ZWE 2013.0 rel f 65 725.0
[405440 rows x 8 columns]
ここまで,R・Pythonで利用可能なParquetのメリットを紹介しました.Parquetは,近年,データ分析で普及しているtidy dataの保存・集計に適しています.
また,最近では,地理データを扱えるsfパッケージのデータをparquetとして保存できるsfarrowなども登場しています.
CSVの代わりにParquetを用いることでデータ分析がさらに簡単になることが期待されます.