Introduction to readr

readr が解決する重要な問題は、フラットファイルを tibble に parsing することです。パースとは、テキストファイルを、各列が適切な部分である長方形の tibble に変換する処理のことです。構文解析は3つの基本的な段階で行われます。

  1. フラットファイルは、文字列の矩形行列に解析されます。

  2. 各列の型が決定されます。

  3. 文字列の各列が、より具体的な型のベクトルにパースされます。

この仕組みは、逆の順番で学ぶのが一番わかりやすいでしょう。 以下では、その仕組みについて学びます。

  1. ベクターパーサー は、文字ベクターをより具体的な型に変換します。

  2. 列指定 各列の型と、readrが型を推測するために使用する方法を説明します。

  3. 矩形パーサー は、フラットファイルを行と列の行列に変えます。

parse_*()col_*() 関数と結合されており、完全な tibble をパースする過程で使用されます。

ベクトルパーサー

ベクター解析は parse_ 関数を使って学ぶのが最も簡単です。これらの関数はすべて、文字ベクトルといくつかのオプションを受け取ります。そして、古いベクトルと同じ長さの新しいベクトルと、問題を示す属性が返されます。

アトミックベクトル

parse_logical()parse_integer()parse_double()parse_character() は、対応するアトミックベクトルを生成するパーサーです。

parse_integer(c("1", "2", "3"))
#> [1] 1 2 3
parse_double(c("1.56", "2.34", "3.56"))
#> [1] 1.56 2.34 3.56
parse_logical(c("true", "false"))
#> [1]  TRUE FALSE

デフォルトでは、readr は小数点以下の記号として . を、グループ化記号として , を期待します。このデフォルトを上書きするには、 vignette("locales") で説明されているように、 locale() を使用します。

柔軟な数値パーサー

parse_integer()parse_double() は厳格で、入力文字列は先行文字や後行文字のない単一の数値でなければなりません。parse_number() はより柔軟です。数値以外の接頭辞や接尾辞を無視し、グループ化記号の扱いも知っています。このため、通貨やパーセンテージを読み取るのに適しています。

parse_number(c("0%", "10%", "150%"))
#> [1]   0  10 150
parse_number(c("$1,234.5", "$12.45"))
#> [1] 1234.50   12.45

日付/時刻

readr は3種類の日付/時刻データをサポートしています。

parse_datetime("2010-10-01 21:45")
#> [1] "2010-10-01 21:45:00 UTC"
parse_date("2010-10-01")
#> [1] "2010-10-01"
parse_time("1:00pm")
#> 13:00:00

各関数は、文字列のフォーマットを記述する format 引数を取ります。指定されない場合は、デフォルト値が使用されます。

ほとんどの場合、 parse_datetime() で説明されているように、 format を指定する必要があります。

parse_datetime("1 January, 2010", "%d %B, %Y")
#> [1] "2010-01-01 UTC"
parse_datetime("02/02/15", "%m/%d/%y")
#> [1] "2015-02-02 UTC"

因子

既知の値の集合を持つ列を読み込む場合、因子に直接読み込むことができます。parse_factor() は、与えられた水準の中に値がない場合に警告を発生させます。

parse_factor(c("a", "b", "a"), levels = c("a", "b", "c"))
#> [1] a b a
#> Levels: a b c
parse_factor(c("a", "b", "d"), levels = c("a", "b", "c"))
#> Warning: 1 parsing failure.
#> row col           expected actual
#>   3  -- value in level set      d
#> [1] a    b    <NA>
#> attr(,"problems")
#> # A tibble: 1 × 4
#>     row   col expected           actual
#>   <int> <int> <chr>              <chr> 
#> 1     3    NA value in level set d     
#> Levels: a b c

列の指定

ファイルを読むときに、すべての列の型を指定しなければならないとしたら、面倒なことです。その代わりに readr は、発見的な方法を用いて各列の型を推測します。この結果には、 guess_parser() を使って自分でアクセスすることができます。

guess_parser(c("a", "b", "c"))
#> [1] "character"
guess_parser(c("1", "2", "3"))
#> [1] "double"
guess_parser(c("1,000", "2,000", "3,000"))
#> [1] "number"
guess_parser(c("2001/10/10"))
#> [1] "date"

推測の方針は、個々の関数のドキュメントに記載されています。推測はかなり厳密です。例えば、通貨は解析できるにもかかわらず、数字であるとは推測しません。

guess_parser("$1,234")
#> [1] "character"
parse_number("$1,234")
#> [1] 1234

絶対に推測されないパーサーが2つあります。col_skip()col_factor() です。これらは常に明示的に指定する必要があります。

spec_csv()spec_tsv() などを使用すると、readr が列ファイルに対して生成する仕様を確認することができます。

x <- spec_csv(readr_example("challenge.csv"))

大きなファイルでは、cols_condense() を使ってデフォルトの列タイプを変更することで、よりシンプルな仕様にできる場合があります。

mtcars_spec <- spec_csv(readr_example("mtcars.csv"))
mtcars_spec
#> cols(
#>   mpg = col_double(),
#>   cyl = col_double(),
#>   disp = col_double(),
#>   hp = col_double(),
#>   drat = col_double(),
#>   wt = col_double(),
#>   qsec = col_double(),
#>   vs = col_double(),
#>   am = col_double(),
#>   gear = col_double(),
#>   carb = col_double()
#> )
cols_condense(mtcars_spec)
#> cols(
#>   .default = col_double()
#> )

デフォルトでは、readr は最初の1000行しか見ません。このため、ファイルの解析は高速に行われますが、不正確な推測が発生する可能性があります。例えば、challenge.csv では、1001行目で列の型が変わるので、readr は間違った型を推測してしまいます。この問題を解決する1つの方法は、行数を増やすことです。

x <- spec_csv(readr_example("challenge.csv"), guess_max = 1001)

もう一つの方法は、以下のように col_type を手動で指定することです。

矩形パーサ

readr には、矩形ファイルフォーマット用の5つのパーサーが付属しています。

これらの関数はそれぞれ、まず(上記のように) spec_xxx() を呼び出し、その列指定に従ってファイルをパースします。

df1 <- read_csv(readr_example("challenge.csv"))
#> Rows: 2000 Columns: 2
#> ─ Column specification ────────────────────────────
#> Delimiter: ","
#> dbl  (1): x
#> date (1): y
#> 
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

矩形解析関数はおおむね成功します。失敗するのは、フォーマットがひどく混乱している場合だけです。その代わり、readr は問題のデータフレームを生成します。最初の数個が出力され、 problems() でそれらすべてにアクセスすることができます。

problems(df1)
#> # A tibble: 0 × 5
#> # … with 5 variables: row <int>, col <int>, expected <chr>, actual <chr>,
#> #   file <chr>

推測に失敗した場合の対処法の1つとして、各列の型を推測するためには、もうお分かりでしょうが、使用する行数を増やすことがあります。

df2 <- read_csv(readr_example("challenge.csv"), guess_max = 1001)
#> Rows: 2000 Columns: 2
#> ─ Column specification ────────────────────────────
#> Delimiter: ","
#> dbl  (1): x
#> date (1): y
#> 
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

もう一つの方法は、手動で列の仕様を指定することです。

デフォルトを上書き

これまでの例で、readr がファイルの解析に使用した列の仕様を出力していることにお気づきでしょうか。

#> Parsed with column specification:
#> cols(
#>   x = col_integer(),
#>   y = col_character()
#> )

また、spec() を使えば、後からアクセスすることも可能です。

spec(df1)
#> cols(
#>   x = col_double(),
#>   y = col_date(format = "")
#> )
spec(df2)
#> cols(
#>   x = col_double(),
#>   y = col_date(format = "")
#> )

(非常に幅の広いファイルを読んでいる場合、完全な列の仕様にアクセスすることもできます。デフォルトでは、readr は最初の20列の仕様のみを表示します)。

もし、手動で列の種類を指定した場合は、このコードをコピー&ペーストして、パースの問題を修正することから始めるとよいでしょう。

df3 <- read_csv(
  readr_example("challenge.csv"), 
  col_types = list(
    x = col_double(),
    y = col_date(format = "")
  )
)

一般に、明示的に列を指定することは良い習慣です。より手間がかかりますが、予期せぬ方法でデータが変更された場合に警告が表示されるようになります。本当に厳密に行うには、 stop_for_problems(df3) を使用するとよいでしょう。これは解析に問題があった場合にエラーを出し、解析を進める前にそれらの問題を修正することを強制します。

利用可能な列の仕様

利用可能な仕様は以下の通りです。(括弧内は文字列の省略形)

デフォルトの選択肢をオーバーライドするには、 col_types 引数を使用します。使用方法は2つあります。

省略された列は自動的に解析されるので、前の呼び出しと同じ結果になる。

read_csv("iris.csv", col_types = list(
  Species = col_factor(c("setosa", "versicolor", "virginica")))
)

また、指定しない列の自動検出には頼らず、デフォルトで使用する型を設定することも可能です。

read_csv("iris.csv", col_types = list(
  Species = col_factor(c("setosa", "versicolor", "virginica")),
  .default = col_double())
)

指定した列のみを読み込む場合は、cols_only() を使用します。

read_csv("iris.csv", col_types = cols_only(
  Species = col_factor(c("setosa", "versicolor", "virginica")))
)

出力

これらの関数の出力はすべて tibble です。文字が自動的に因数に変換されることはなく (つまり、 stringsAsFactors = FALSE はありません)、列名はそのままで、有効な R 識別子に変換されません (つまり、 check.names = TRUE はありません)。行名は決して設定されません。

属性には、列の仕様(spec())と、パースに関する問題(problems())が格納されます。