cucumber flesh

Rを中心としたデータ分析・統計解析らへんの話題をしていくだけ

readxlパッケージ 1.0.0の主要な変更点

先日、エクセルファイルのデータをRに読み込ませるパッケージのreadxlパッケージの1.0.0がCRANに登録されました。

実はしばらく更新を追えていなくて、色々な新機能があったのでメモしておきます。まとめると以下の通りです。気になった点を挙げていますので、詳細はNEWSをご覧ください。

  • エクセルファイル読み込み関数に2つの引数の追加
    • 範囲を指定した読み込みのためのrange引数
    • 読み込み時の行制限としてn_maxの適用
  • 列指定の際の挙動に大きな変更
    • col_types引数にlist, logical, guessが指定することが可能に
    • blankの廃止。代わりにskipの指定
    • 欠損値に複数の値を指定可能に
  • その他
    • 変数名の初期値がX__1からX__0に変更

特に気になるのが読み込み時の範囲指定とcol_types引数のlist対応です。それぞれ詳しく見ていきましょう。

読み込み時の範囲指定

エクセルファイルだと、目的のデータ以外の備考や合計値を算出している行などが含まれていることがよくありますね。これまでの readxlパッケージでは、対象のシートに含まれるセルをよしなにデータフレームに変換して読み込み、そこから利用者が必要なデータを選択するという手間がありました。今回のバージョンで追加されたrange引数は、その手間を省略するのに大変役立ちます。

エクセルファイルを印刷する際、「範囲指定した部分のみを印刷」という処理をしたことがある人が多いと思います。range引数はそれと同じく、read_excel()の実行時に範囲を指定できるようになりました。これにより直接必要な箇所だけを読み込ませることができます。

range引数の指定方法は、エクセルで使われるA1:B2 (A1セルからB2セルまで)のような記述が可能です。また範囲指定の専用の関数としてcell_rows()anchored()なども実装されています。

col_types引数のlist対応

Rのデータフレームの性質として、変数内の値はベクトルとして扱われ、そのデータ型は統一される、というものがあります。 そのため、文字列と日付が含まれる列を読み込む場合、日付は文字列に変換されてしまいます。一方でリストは異なるデータ型の値を保持するのに適しています。

今回、引数col_typesがlistに対応したことで、複数のデータ型が含まれる列であっても元の値を保持しておくことが可能になっています。少しわかりにくいのでヘルプに掲載されているコードを実行してみましょう。

library(readxl)
(df <- read_excel(readxl_example("clippy.xlsx"), 
    col_types = c("text", "list")))
## # A tibble: 4 × 2
##                   name      value
##                  <chr>     <list>
## 1                 Name  <chr [1]>
## 2              Species  <chr [1]>
## 3 Approx date of death <dttm [1]>
## 4      Weight in grams  <dbl [1]>

2列目、value列はlistとして格納されているのがわかります。各行は、実際の値でなく、データ型を示しています。リストとして保存することで、文字列、日付・時間、数値という異なるデータ型が共存しています。比較のため、通常の方法で読み込む例も示します。

read_excel(readxl_example("clippy.xlsx"))
## # A tibble: 4 × 2
##                   name     value
##                  <chr>     <chr>
## 1                 Name    Clippy
## 2              Species paperclip
## 3 Approx date of death     39083
## 4      Weight in grams       0.9

今度は直接、値が出力されました。しかし3行目の値が39083となっている点に注意です。これは元のエクセルファイルでは日付となっていたものでした。value列(ベクトル)が文字列として処理された結果、値が変化してしまいました。今回のアップデートでは、list列として処理することで、この問題を改善しています。

readxlのメジャーリリースとなる1.0.0が出たことで、Clippy君は悲しんでいるようですが、アップデートが済んでいない方は、ぜひ新しいreadxlをお試しください。

おまけ… RStudioのGUIからエクセルファイルも読み込めます

f:id:u_ribo:20170504123036p:plain

Enjoy!

日本の人口密度を可視化する: population lines

少し前(4月下旬ごろ?)に、reddit人口密度の高さを表現した地図が話題になりました。

www.reddit.com

この地図は、James Cheshire博士 (@spatialanalysis)が2014年に投稿した “Population Lines Print” が元となっていて、再現性のあるRコード、ヨーロッパに焦点を当てた地図が描かれた(Henrik Lindberg @hnrklndbrg )ことで話題が広がっています(という印象。今週のR Weeklyでもいくつかの記事が掲載されました。

日本の人口密度の情報を世界地図から伺うことはできますが、スケールダウンしたものがあった方がわかりやすいです。…というわけで作成してみました。

f:id:u_ribo:20170503102859p:plain

続きを読む

日付から曜日を取得する関数と日本語表記の対応

日付から曜日を取得する関数としてlubridate::wday() (days of the week)をよく使う。この関数は曜日を与えて実行し、デフォルトでは数値化した値(日曜日を起点 1とした1から7までの値)を返すが、label引数を有効化することで曜日のラベルが得られる。また省略形と正規の表現が選べる。

データフレームに含まれる日付の列から、曜日を求めて各曜日の集計値を求める、みたいなことをやる時などに便利だ。

library(lubridate)
today() %>% wday(label = TRUE)
## [1] Tues
## Levels: Sun < Mon < Tues < Wed < Thurs < Fri < Sat
library(dplyr)
d <- data_frame(date = make_date(2017, 5, 
    1:31)) %>% mutate(wd = wday(date))

ただ、時々、数値や英語の曜日ではなく日本語の「火曜日」、「金曜日」といったラベルを使いたい時がある。そういう場合にどうすれば良いかというのが今回の話。

結論から言うと、現在CRANに登録されているlubridateパッケージ (1.6.0)はこれに対応していない。しかしGitHubの開発版はこれに対応している (ref) #401, #508

こんな感じで日本語の曜日も表示できるようになる。

d$date %>% wday(label = TRUE, locale = "ja_JP.UTF-8")
# [1] 月 火 水 木 金 土 日 月 火 水 木 金
# 土 日 月 火 水 木 金 土 [21] 日 月 火
# 水 木 金 土 日 月 火 水 Levels: 日 < 月
# < 火 < 水 < 木 < 金 < 土

NEWSにも書かれているので次のバージョンが登録されたら機能することは間違い無いと思うが、今はforcats::fct_recodeを使う次の方法で対処している。早く出て欲しい…

library(forcats)
d.mod <- d %>% mutate(wd_j = fct_recode(as.factor(wd), 
    日曜日 = "1", 月曜日 = "2", 火曜日 = "3", 
    水曜日 = "4", 木曜日 = "5", 金曜日 = "6", 
    土曜日 = "7"))
d.mod$wd_j
##  [1] 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日曜日 月曜日 火曜日 水曜日
## [11] 木曜日 金曜日 土曜日 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日
## [21] 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日曜日 月曜日 火曜日
## [31] 水曜日
## Levels: 日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日

20170510追記

そういえば、過去のr-wakalangにて同様のやりとりがあったことを思い出した(発見した)。Rに標準実装されるbeseパッケージ内の関数weekdays()はデフォルトで現在利用中のロケールにあった曜日を返してくれる。こちらの方が手軽感がある。

weekdays(lubridate::today() - 0:6)
## [1] "水曜日" "火曜日" "月曜日" "日曜日" "土曜日" "金曜日" "木曜日"

注意として、文字列であるが因子にはなっていない。