1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

readrとdata.tableでデータを読み込む時に値が抜け落ちたり型がへんてこになる現象

Last updated at Posted at 2019-11-22

【最下部に追記あり】

こんにちは

今回はRでデータをいい感じに読み込んでくれるパッケージであるreadrやdata.tableについて、業務利用時に予想外の挙動をして時間を溶かしてしまったので、備忘録的に記録します。

具体的に発生した挙動を簡単に説明すると下記です。

  • readrにおいて、先頭行にNAが固まっていると、全てNAで読み込んでしまう
  • data.tableにおいて、NAを含む日付型の列が文字列型になってしまい、NAも空白セルの文字列という扱いで読み込んでしまう

自分のケースでは、データベースからSQLでローデータを抽出し、それをRで加工するために読み込む際に発生しました。
NAの多いデータセット扱う際に割と発生しそうな罠なので、気にしてみるといいかもしれません。

それでは本文です。

データと前準備

今回は下記のようなcsvデータを作成しました。
https://docs.google.com/spreadsheets/d/1hhNxisbIWrIwTY2w0xLFGwaOsWBCInP2vAuoHpmpS4o/edit?usp=sharing
自分で作成したい方は、行数が3000行で、かつ下記要件を満たすデータセットを自作してみてください。

  • 何かしらの値で埋まっている列を一つ
  • 冒頭1000行以上がNAでその後に日付型の値が入っている列を一つ
  • 冒頭1000行以上がNAでその後に数値型の値が入っている列を一つ

後はreadrとdata.tableパッケージを読み込んておいてください。

library(readr)
library(data.table)

readr

読み込むと下記のような表示が出ます

test_readr <- read_csv("dataset_compare_readr_data.table.csv")

# x,y,zがそれぞれ上述の1つ目、2つ目、3つ目の列と対応しています

Parsed with column specification:
cols(
  x = col_double(),
  y = col_logical(),
  z = col_logical()
)
Warning: 3001 parsing failures.
 row col           expected     actual                                   file
1500   y 1/0/T/F/TRUE/FALSE 2019-10-01 'dataset_compare_readr_data.table.csv'
1501   y 1/0/T/F/TRUE/FALSE 2019-10-02 'dataset_compare_readr_data.table.csv'
1501   z 1/0/T/F/TRUE/FALSE 2          'dataset_compare_readr_data.table.csv'
1502   y 1/0/T/F/TRUE/FALSE 2019-10-03 'dataset_compare_readr_data.table.csv'
1502   z 1/0/T/F/TRUE/FALSE 3          'dataset_compare_readr_data.table.csv'
.... ... .................. .......... ......................................
See problems(...) for more details.

不吉ですね。

yとzがlogi型で読み込まれてるし、Warningとか出てるし。

summaryで様子を見てみましょう。

summary(test_readr)

       x        y              z          
 Min.   :1   Mode:logical   Mode:logical  
 1st Qu.:1   NA's:3000      TRUE:1        
 Median :1                  NA's:2999     
 Mean   :1                                
 3rd Qu.:1                                
 Max.   :1         

おどろ木ももの木さんしょの木
yには日付型の値が入っていたのに、それは反映されていませんし、zには途中から1~1501の値が連番で入っていたのに、TRUEが1個だけになってます。

調べてみると、readrは最初の1000行で型を予測するらしく、その影響で、最初の1000行がたまたまNAだったらlogi型になってしまうものと思われます。
https://qiita.com/uri/items/9fdc5d831ff69a2d9128
https://readr.tidyverse.org/reference/read_delim.html

また、zで1個だけTRUEになってるのは、数値として入れていた"1"が反映されているようです。
データを読み込む時は、読み込んだ段階で型やデータ数などを簡単にチェックするとよいですね。

ちなみに私はこのトラップに気づくのに3時間を浪費しました

data.table

データを読み込むと、特にエラー表記は出ません

test_data.table <- fread("dataset_compare_readr_data.table.csv")

不吉じゃない!

しかし、summaryをしてみると。。。

summary(test_data.table)

      x          y                   z       
 Min.   :1   Length:3000        Min.   :1     
 1st Qu.:1   Class :character   1st Qu.:1     
 Median :1   Mode  :character   Median :1     
 Mean   :1                      Mean   :1     
 3rd Qu.:1                      3rd Qu.:1     
 Max.   :1                      Max.   :1     
                                NA's   :1499  

むーむーむっ!

yとzはどちらも冒頭がNAのはずなのに、yはchr型でzはint型になっています。

こちらはイマイチ理由が分からなかったのですが、NA以外で入っている値が日付型なのか数値型なのかで変わっているようです。
これに気づかず、is.na(y) == TRUE とかやるとデータが0件になって "Why R language!" ってなります
データを読み込む時は、読み込んだ段階で型やデータ数などを簡単にチェックするとよいですね(2度目)。

ちなみに私はこのトラップに気づくのに30分を浪費しました

まとめ

挙動は上で記載したとおりです。
実務で扱う際は、readrの挙動ではそもそものデータの値が変わってしまうので、その点ではdata.tableを利用するほうが安全かもしれません
readrも、読み込む際に型指定をできるそうですが、個人的には一旦読み込んでから型変換などしたほうが楽なので、今後はdata.tableも併用していこうかと思います。

以上です。

追記

readrでの読み込み時の型指定について、konandoiruasaさんにコメントいただきました。ありがとうございます!
readrのGithubページ(こちら)には下記の通り記載されています。

In many cases, these functions will just work: you supply the path to a file and you get a tibble back. The following example loads a sample file bundled with readr:

mtcars <- read_csv(readr_example("mtcars.csv"))
#> Parsed with column specification:
#> 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()
#> )
Note that readr prints the column specification. This is useful because it allows you to check that the columns have been read in as you expect, and if they haven’t, you can easily copy and paste into a new call:

mtcars <- read_csv(readr_example("mtcars.csv"), col_types = 
  cols(
    mpg = col_double(),
    cyl = col_integer(),
    disp = col_double(),
    hp = col_integer(),
    drat = col_double(),
    vs = col_integer(),
    wt = col_double(),
    qsec = col_double(),
    am = col_integer(),
    gear = col_integer(),
    carb = col_integer()
  )
)

Parsed with column specification:に読み込んだ列の型を記載しているから、自分で型指定をしたい時はそれをコピーして、read_csvcol_typesに入れた上で、指定したい部分のみ書き換えれば簡単に型指定ができるようです。

試してみましょう

test_readr <- read_csv("dataset_compare_readr_data.table.csv",
                       col_types = cols(
                         x = col_double()
                         ,y = col_date()
                         ,z = col_integer() #col_double()でも良かったですが、なんとなくこちらにしました
                       ))

summary(test_readr)
       x           y                    z       
 Min.   :1   Min.   :2019-10-01   Min.   :   1  
 1st Qu.:1   1st Qu.:2020-10-10   1st Qu.: 376  
 Median :1   Median :2021-10-20   Median : 751  
 Mean   :1   Mean   :2021-10-20   Mean   : 751  
 3rd Qu.:1   3rd Qu.:2022-10-30   3rd Qu.:1126  
 Max.   :1   Max.   :2023-11-09   Max.   :1501  
             NA's   :1499         NA's   :1499 

できてますね。
これはイージー

ちなみに一部の列のみの指定もできるようです。

test_readr <- read_csv("dataset_compare_readr_data.table.csv",
                       col_types = cols(
                         x = col_double()
                         ,y = col_date()
                         #,z = col_integer()
                       ))
Warning: 1500 parsing failures.
 row col           expected actual                                   file
1501   z 1/0/T/F/TRUE/FALSE      2 'dataset_compare_readr_data.table.csv'
1502   z 1/0/T/F/TRUE/FALSE      3 'dataset_compare_readr_data.table.csv'
1503   z 1/0/T/F/TRUE/FALSE      4 'dataset_compare_readr_data.table.csv'
1504   z 1/0/T/F/TRUE/FALSE      5 'dataset_compare_readr_data.table.csv'
1505   z 1/0/T/F/TRUE/FALSE      6 'dataset_compare_readr_data.table.csv'
.... ... .................. ...... ......................................
See problems(...) for more details.

summary(test_readr)
       x           y                 z          
 Min.   :1   Min.   :2019-10-01   Mode:logical  
 1st Qu.:1   1st Qu.:2020-10-10   TRUE:1        
 Median :1   Median :2021-10-20   NA's:2999     
 Mean   :1   Mean   :2021-10-20                 
 3rd Qu.:1   3rd Qu.:2022-10-30                 
 Max.   :1   Max.   :2023-11-09                 
             NA's   :1499  

こちらだとzのみlogi型で読み込まれる挙動になっています。

col_typesでの型指定方法については下記記事で細かく触れられているので、参考にしてみるとよいかもしれません。
https://heavywatal.github.io/rstats/readr.html
https://qiita.com/matsuou1/items/78fedad2766ce4bbf861
https://qiita.com/uri/items/9fdc5d831ff69a2d9128

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?