DATUM STUDIOブログ
「楽屋」

Rでデータクレンジング

えたいのしれないCSVふぁいるがあらわれた!!

データ分析の業務時間の8割を占めるとかなにやらな前処理、そのその頭に位置するデータクレンジング。手を抜くと大変な事態を招きかねない工程ですが、あまり時間をかけたくはありません。そこで、この工程を少しでも効率的に進めるために、tidyverseパッケージなどを用いた方法を紹介します。

(用語定義) 論理式: T/Fを返す関数など全般、 df:tbl_dfのこと

各種関数の用法

map()の用法補足

lapply()と同様に、リストを渡すとリスト毎に、dfを渡すと列毎に関数を適用します。ラムダ式 (map(~ 関数など)) を用いた書式が直感的にわかりやすいと思います。

select_if(), mutate_if()の用法補足

select_if()mutate_if()は引数.predicate (対象列の指定) の部分などにラムダ式を用いることができます。
面倒なのですが、列名を扱う場合は~が不要です (列内の要素ではないので評価する必要がないため)。

基本的な流れ

map_res <- df %>% map_dbl(~ sum(論理式))で気になる要素が各列に何個あるのかを求め、必要に応じてdf %>% filter(論理式) などで具体的に抽出して修正の必要があるかを確認し、df %>% mutate(...)df %>% mutate_if(...)で修正を行う、というのが基本的な流れとなります。

Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
999.0 3.6 1.4 0.2 setosa

実例編

データ

それでは、以下のcsvファイルのデータをクレンジングしていきましょう (ダウンロードしたい方はこちら)。

見づらいので補足しますと、seminar_nameちゅら〜の末尾とtypeの白紙マスには全角空白が入っています。コンソールで表示される\"のエスケープ文字なので気にしなくて大丈夫です。

seminar_code seminar_name seminar_date customer_id birthday type memo
test test 19800101 00001 19800101 社員 test
S00001 社員研修 20170820 00012 19800101 てすと
S00123 DATUM "サマースクール" 20170820100000 00133 20000101 NA
S00123 DATUM "サマースクール" 20170820100000 00011 19870516 guest NA
S00123 DATUM "サマースクール" 20170820 00165 20000101 NA
S00159 DATUM "ウィンタースクール" 20171215090000 00011 20000101 vip NA
S00159 DATUM "ウィンタースクール" 20171215090000 00802 20010410 ゲスト NA
S00572 ちゅら夏セミナー  20170925093000 00866 19971128 NA
S00572 ちゅら夏セミナー  20170925093000 00866 19900505 NA NA
S00598 ちゅら夏セミナー (追加)  20170926 01904 20000101 NA NA
S00604 ちゅら秋セミナー  20171010100000 02175 20000101 NA NA
S00604 ちゅら秋  20171010100000 02212 19891205 guest NA

読み込み

何はともあれ読み込まないと始まらなりません。ダーティーデータ相手なら、汎用性が高く読み込み速度もそこそこなread_csv()がおすすめです。col_typesは列数が少ない場合、全列characterが無難です。

みごとに文字化け、encodingがShift_JIS (正確にはCP932) なのでしょう (encodingが違うと表示すらできないことも)。

列数が多い場合、col_typesは最初は自動判別で読み込み、warningが出た場合その列のcol_typesをcharacterにして再度読み込む、というのが現実的です。

seminar_code seminar_name seminar_date customer_id birthday type memo
test test 19800101 00001 19800101 社員 test
S00001 社員研修 20170820 00012 19800101 てすと
S00123 DATUM “サマースクール” 20170820100000 00133 20000101 NA
S00123 DATUM “サマースクール” 20170820100000 00011 19870516 guest NA

NA関連

なにはともあれNAの処理です。map()を用いて探索し、NA専用の置換関数であるreplace_na()で潰します。

要素頭・末尾の空白潰し

read_csv()の自動削除機能は全角空白には働かないため、"全角空白"などには無力です。しっかり自力で潰す必要があります。

空文字チェック

特に指定していない場合、元々の""はread_csv()で既にNA置換されていますが、空白潰しの結果、””が生じている可能性ががあります、サボらずチェックしましょう。

流れは前述の空白潰しと同じです。

seminar_code seminar_name seminar_date customer_id birthday type memo
test test 19800101 00001 19800101 社員 test
S00001 社員研修 20170820 00012 19800101 NA てすと
S00123 DATUM “サマースクール” 20170820100000 00133 20000101 NA NA
S00123 DATUM “サマースクール” 20170820100000 00011 19870516 guest NA
S00123 DATUM “サマースクール” 20170820 00165 20000101 NA NA
S00159 DATUM “ウィンタースクール” 20171215090000 00011 20000101 vip NA
S00159 DATUM “ウィンタースクール” 20171215090000 00802 20010410 ゲスト NA
S00572 ちゅら夏セミナー 20170925093000 00866 19971128 NA NA
S00572 ちゅら夏セミナー 20170925093000 00866 19900505 NA NA
S00598 ちゅら夏セミナー (追加) 20170926 01904 20000101 NA NA
S00604 ちゅら秋セミナー 20171010100000 02175 20000101 NA NA
S00604 ちゅら秋 20171010100000 02212 19891205 guest NA

その他探索用コード例 (以下例では、正規表現を用いて全角文字を探索しています)

型変換 (主に日時型)

date/datetime型のformatが統一されてる、などという幸運を期待するとバカを見ますよ! ということで、date/datetime型っぽい列は文字数をチェックすることをお勧めします。

seminar_code seminar_name seminar_date customer_id birthday type memo
test test 1980-01-01 00:00:00 00001 1980-01-01 社員 test
S00001 社員研修 2017-08-20 00:00:00 00012 1980-01-01 NA てすと
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00133 2000-01-01 NA NA
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00011 1987-05-16 guest NA
S00123 DATUM “サマースクール” 2017-08-20 00:00:00 00165 2000-01-01 NA NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00011 2000-01-01 vip NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00802 2001-04-10 ゲスト NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1997-11-28 NA NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1990-05-05 NA NA
S00598 ちゅら夏セミナー (追加) 2017-09-26 00:00:00 01904 2000-01-01 NA NA
S00604 ちゅら秋セミナー 2017-10-10 10:00:00 02175 2000-01-01 NA NA
S00604 ちゅら秋 2017-10-10 10:00:00 02212 1989-12-05 guest NA

ユニークな要素のチェック

このあたりまできたら、具体的な中身も絡めてデータを綺麗にしていきましょう。

気合いと根性でマニュアル修正

上のtype列とmemo列の要素で色々と嫌なものが見えました。type列の一部の要素を置換し、testっぽい文字のある行は消去しましょう。

seminar_code seminar_name seminar_date customer_id birthday type memo
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00133 2000-01-01 normal NA
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00011 1987-05-16 guest NA
S00123 DATUM “サマースクール” 2017-08-20 00:00:00 00165 2000-01-01 normal NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00011 2000-01-01 vip NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00802 2001-04-10 guest NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1997-11-28 normal NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1990-05-05 normal NA
S00598 ちゅら夏セミナー (追加) 2017-09-26 00:00:00 01904 2000-01-01 normal NA
S00604 ちゅら秋セミナー 2017-10-10 10:00:00 02175 2000-01-01 normal NA
S00604 ちゅら秋 2017-10-10 10:00:00 02212 1989-12-05 guest NA

対応チェック

だいぶデータが綺麗になってきました。論理的な対応関係を用いてのチェックを行ってみましょう。

例えば、seminar_codeとseminar_nameには1:1関係が期待されます。このような1:1関係は、以下のような処理を行うと例外が無いかチェックできます。

seminar_code seminar_name n nn
S00123 DATUM “サマースクール” 2 1
S00123 DATUM “サマースクール” 2 1
S00604 ちゅら秋セミナー 2 1
S00604 ちゅら秋 2 1

ちょいちょいと修正しておきます。楽をするためにcase_when()内で対応がおさまったtbl_dfを用いていますが、tbl_df[行番号, 列番号]は1要素のみのtbl_dfなので[[1]]で取り出す必要があります。

異常値チェック

次に異常値がないかのチェックです。データ数とも相談ですが、numeric型以外の要素は全て数え上げておくと吉。会員の大半が2000年1月1日生まれなど、不思議な現象を見逃さないですみますよ!

四則演算可能な列は summary() にぶちこんで要約統計量をチェックしましょう。本例では存在しませんが、numeric型はヒストグラムも書いておくのがお勧めです。(箱ヒゲ図や散布図はデータ量によっては大変なことになるので、要注意です)。

集計する前に

一通りデータクレンジングが終了したら、chr型列、特にgroup_by()で用いそうな列はfactor型に変換しておきましょう。処理速度がだいぶ変わります。

seminar_code seminar_name seminar_date customer_id birthday type memo
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00133 2000-01-01 normal NA
S00123 DATUM “サマースクール” 2017-08-20 10:00:00 00011 1987-05-16 guest NA
S00123 DATUM “サマースクール” 2017-08-20 00:00:00 00165 2000-01-01 normal NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00011 2000-01-01 vip NA
S00159 DATUM “ウィンタースクール” 2017-12-15 09:00:00 00802 2001-04-10 guest NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1997-11-28 normal NA
S00572 ちゅら夏セミナー 2017-09-25 09:30:00 00866 1990-05-05 normal NA
S00598 ちゅら夏セミナー (追加) 2017-09-26 00:00:00 01904 2000-01-01 normal NA
S00604 ちゅら秋セミナー 2017-10-10 10:00:00 02175 2000-01-01 normal NA
S00604 ちゅら秋セミナー 2017-10-10 10:00:00 02212 1989-12-05 guest NA


ここまでおつきあいいただき、どうもありがとうございました。

Let’s enjoy R life !!