base と Tidyverse と data.table と

恋しさと せつなさと 心強さと」といえば『ストリートファイターII MOVIE』の挿入歌ですね。

さて、初心者にRを教えるとき、base準拠にすべきか、Tidyverse準拠にすべきか、という議論があります。
これは私も悩むところでありまして、さらに言えば data.table ってのもあります。

んで、まずはこの三つの流派(?)が存在してそれぞれこんな書き方をするんだよ、というのを示せるといいのかなーと思うわけです。

そこで試しにこんな処理を base, Tidyverse, data.tableのそれぞれで書いてみました。

  1. データセット flights の変数をdep_delay、arr_delay、carrier、air_time、distanceに絞る。
  2. いずれかの変数にNAを含む行を削除。
  3. 時速をkm/hで算出。air_timeは分単位なので60で除して時間単位に、distanceはマイル単位なので1.60934を乗じてkm単位に。
  4. carrier毎にdep_delay、arr_delay、Hourly speedの平均と件数を算出。
  5. 件数の降順に並べる。

こんな風に書いてみましたが、それぞれの書き方の違いの雰囲気ぐらいは伝わるかな?

base

library(nycflights13)
flights.df <- na.omit(as.data.frame(flights)[, c("dep_delay", "arr_delay", "carrier", "air_time", "distance")])
flights.df$HourlySpeed <- (flights.df$distance * 1.60934) / (flights.df$air_time / 60)
flights.df2 <- aggregate(
  flights.df[, c("dep_delay", "arr_delay", "HourlySpeed") ],
  list(carrier = flights.df$carrier),
  mean
)
flights.df2$n <- table(flights.df$carrier)
flights.df2[order(flights.df2$n, decreasing = TRUE), ]

Tidyverse

library(nycflights13)
library(tidyverse)
flights %>%
  select(dep_delay, arr_delay, carrier, air_time, distance) %>%
  drop_na() %>%
  mutate(HourlySpeed = (distance * 1.60934) / (air_time / 60)) %>%
  group_by(carrier) %>%
  summarise(
    mean_dep_delay = mean(dep_delay),
    mean_arr_delay = mean(arr_delay),
    mean_HourlySpeed = mean(HourlySpeed),
    n = n()
  ) %>% 
  arrange(desc(n))

data.table

library(nycflights13)
library(data.table)
flights.dt <- na.omit(as.data.table(flights)[, c("dep_delay", "arr_delay", "carrier", "air_time", "distance")])
setorder(flights.dt[
  , HourlySpeed := (distance * 1.60934) / (air_time / 60)
  ][
    j = list(
      mean_dep_delay = mean(dep_delay),
      mean_arr_delay = mean(arr_delay),
      mean_HourlySpeed = mean(HourlySpeed),
      n = .N
      ),
    by = carrier
    ],
  -n) -> flights.dt2
flights.dt2

実行速度は?

data.table圧勝!……かと思いきや、これくらいのデータ量だとTidyverseと大差ないですね。

f:id:bob3:20190707223349p:plain
実行速度比較