Rでデータの整形(列のデータ型確認、列の抽出、列名の変更、列の型変換)

ローデータから分析対象とする変数のみ抽出し(個人情報など、保持すべきでない変数を削除するなど)、情報を失わない範囲で分析するためのデータセットを作る。分析プロジェクトにおけるローデータと同じ量の情報を持つ、整形された(扱いやすい)データセットを作るのである。

この後のデータクレンジング以降で、データの加工方法を変更するなどで手戻りが発生することもある。その際ローデータの読み込みまで戻るのは大変なので、ローデータを同じ情報を持つ、整形された状態のデータを作っておくのが重要である。データクレンジングで手戻りが発生しても、ここで整形したデータセットまで戻ればいい。

カテゴリ変数の型となるfactor型の扱い

変数の型

データの型確認

関数str()を使う。これはデータフレーム、data.table同様に使える関数である。

データフレーム

str(customer.df)

data.table

str(customer.dt)
customer.dt %>% str

基本的なデータの型の種類

boolean

logical: TRUE or FALSE、短縮してT or Fとしても可能

数値

  • 整数はinteger
    明示的にintegerとして扱うにはx <- 5LのようにLを付ける
  • 小数を含めるとnumeric
  • bigint相当はlibrary(bit64)を使うとinteger64として指定できる。fread()で読み込んだdata.tableではデフォルトで整数型はinteger64で扱われるas.data.table()で生成されたdata.tableでは元の形式がそのまま適用される)。

文字列の扱い

  • 単純な文字列character
  • カテゴリカル変数として扱うにはfactor
  • 順序つきカテゴリカル変数として扱う場合ordered

分析対象とする列の抽出、列名の変更

列の抽出、削除

データフレーム

指定した列を削除する

customer.df$firstname <- NULL

data.table

customer.dt %>%
  dplyr::select(列名, ...) %>% ...

select()関数名が他のパッケージと重複するので、必ずdplyr::select()と指定するのがいい。

列名の変更

データフレーム

データフレームの列名変更はできない。

data.table

customer.dt %>%
  rename(新列名 = 旧列名) %>% ...

列の型変換

データフレーム

customer.df$reg_store<- as.factor(customer.df$reg_store)
customer.df$older <- as.logical(customer.df$age>40)

data.table

customer.dt %>%
  mutate(x = 変換処理の関数(x)) %>% ...
# customer.dt$x <- 変換処理の関数(customer.dt$x) と同じ

列の型変換の細かいTips

欠損値処理、「NULL」をNAに変換

customer.df$reg_store[customer.df$reg_store=='NULL'] <- NA

factor型

自動で変換されるfactor型の扱い

read.table()などでファイルを読み込む際、その列の全行の値が数字であれば数値型/整数型として扱われるが、一部文字列が入っているとfactor型として読み込んでしまう。それを数値に戻す。そのままas.numeric()すると水準(レベル)を数値にしてしまうため、一旦文字列にしてから数値にする

customer.df$customer_grp <- as.numeric(as.character(customer.df$customer_grp))

複数のデータフレーム間でfactorの水準(レベル)を統一する

データフレームが変われば同じ意味(都道府県など)のfactor変数でも水準が変わるため、まったく別の変数扱いになってしまう。分析で使う際にはそれらを合わせておく必要がある。

データフレームdf1の列prefecture(factor型)の水準をdf2の列prefectureの水準に合わせる。

df1$prefecture <- factor(df1$prefecture, levels=levels(df2$prefecture))

日付、日時

日付はDate型。as.Date()関数を使う。日時はPOSIXctがスカラなので分かりやすい(エポック秒のスカラ)。POSIXltはリストで要素が複数あるのでややこしい。

customer.df$reg_date <- as.POSIXct(customer.df$reg_date, format = '%Y/%m/%d')
customer.df$latest_datetime <- as.POSIXct(customer.df$latest_datetime, format='%Y/%m/%d %H:%M:%S')

※ただし遅いので速さを求めるならライブラリ{lubridate}を使う手もある。
http://qiita.com/hoxo_m/items/6f18b163946f6f41deca

ライブラリ{data.table}のIDate(日付)やITime(時刻)形式も高速。この形式はdata.tableの中でない普通のベクトルやデータフレームの中でも使える。

日付

as.IDate('2020-01-01') # ハイフン区切りならそのまま
as.IDate('20200101', format="%Y%m%d") # フォーマットも指定できる
customer.df$reg_date <- as.IDate(customer.df$reg_date, format = '%Y/%m/%d') # データフレームで扱う場合
customer.dt[,reg_date:=as.IDate(reg_date, format = '%Y/%m/%d')] # data.tableの中で扱う場合

時刻

as.ITime('09:23:15') # コロン区切りならそのまま
as.ITime('09:23') # 時分のみでもOK
as.ITime('092315', format="%H%M%S") # フォーマットも指定できる
customer.df$reg_time <- as.ITime(customer.df$reg_time, format = '%H%M%S') # データフレームで扱う場合
customer.dt[,reg_time:=as.ITime(reg_time, format = '%H%M%S')] # data.tableの中で扱う場合

日付と時刻を合わせた日時形式は存在しない。その場合はPOSIXct形式を使う。なおPOSIXctは以下のようにして生成できる。

as.POSIXct('2020-01-01') + as.ITime('09:23')

Rのdata.table形式で日時データを扱う際はPOSIXltを使わない。

データフレームで数字列を日時に変換する。

> head(x.df)
        date
1   20151122
2   20151118
3   20151123
4   20150330
5   20151106
6   20150801

このようなデータに対して、以下の3通りの方法で日時データに変換する。

  • strptime()のみ実行
  • strptime()してからas.POSIXlt()
  • strptime()してからas.POSIXct()
> x.df$date2 <- strptime(as.character(x.df$date), format="%Y%m%d")
> x.df$date3 <- as.POSIXlt(strptime(as.character(x.df$date), format="%Y%m%d"))
> x.df$date4 <- as.POSIXct(strptime(as.character(x.df$date), format="%Y%m%d"))
> 
> head(x.df)
      date      date2      date3      date4
1 20151122 2015-11-22 2015-11-22 2015-11-22
2 20151118 2015-11-18 2015-11-18 2015-11-18
3 20151123 2015-11-23 2015-11-23 2015-11-23
4 20150330 2015-03-30 2015-03-30 2015-03-30
5 20151106 2015-11-06 2015-11-06 2015-11-06
6 20150801 2015-08-01 2015-08-01 2015-08-01

strptime()はPOSIXltを返すのでdate2とdate3は同じだが比較のため。
POSIXltもPOSIXctも日時データだが、POSIXltでは内部で日時をリスト型変数として、POSIXctは数値型変数として格納している。

これに対してdata.tableでは

> head(x.dt)
         date
  1: 20151122
  2: 20151118
  3: 20151123
  4: 20150330
  5: 20151106
  6: 20150801
> x.dt %>% mutate(
  date2 = strptime(as.character(date), format="%Y%m%d"), 
  date3 = as.POSIXlt(strptime(as.character(date), format="%Y%m%d")), 
  date4 = as.POSIXct(strptime(as.character(date), format="%Y%m%d"))
) -> x.dt
 警告メッセージ: 
1:  `[.data.table`(`_dt`, , `:=`(date2, strptime(as.character(date),  で: 
  Supplied 11 items to be assigned to 344 items of column 'date2' (recycled leaving remainder of 3 items).
2:  `[.data.table`(`_dt`, , `:=`(date3, as.POSIXlt(strptime(as.character(date),  で: 
  Supplied 11 items to be assigned to 344 items of column 'date3' (recycled leaving remainder of 3 items).
> 
> head(x.dt)
       date                    date2                    date3      date4
1: 20151122             0,0,0,0,0,0,             0,0,0,0,0,0, 2015-11-22
2: 20151118             0,0,0,0,0,0,             0,0,0,0,0,0, 2015-11-18
3: 20151123             0,0,0,0,0,0,             0,0,0,0,0,0, 2015-11-23
4: 20150330       22,18,23,30, 6, 1,       22,18,23,30, 6, 1, 2015-03-30
5: 20151106       10,10,10, 2,10, 7,       10,10,10, 2,10, 7, 2015-11-06
6: 20150801 115,115,115,115,115,115, 115,115,115,115,115,115, 2015-08-01

変な警告とともにPOSIXlt形式をそのままリストとして扱ってしまう。POSIXlt形式は内部ではリストなのだが、日付としてのラッパーが外れてしまうのである。

ということでdata.table内ではPOSIXltは扱えない。気を付けよう。

factor型に順序(Sでいうところのordered型にする)や基準となる水準を与える

factor型には順序を指定することができる。
また順序があってもなくても、回帰係数などの基準となる水準を指定することができる。

Rでは回帰分析の際、factor型の場合には自動でダミー変数を作って係数を推定するが、その係数は先頭の水準をベースとして、それに対する相対的な効果を表している(水準==aの効果をゼロとした場合のに対して、水準==bの効果がどの程度か)。特定の水準を基準として係数の大きさやp値を見たい場合には、先頭の水準を変更することによってそれが可能になる。

  • 先頭の水準を指定する:relevel(変更前のfactor型変数名, ref = '先頭の水準のラベル')
  • 順序全体を指定する:factor(変更前のfactor型変数名, levels = ラベルを順番に並べたベクトル)

データフレーム

# category == 'news'を基準にする
x.dt$category <- relevel(x.dt$category, ref = 'news')]
# 'small', 'medium', 'large'の順に並べる
x.dt$size <- factor(x.dt$size, levels = c('small', 'medium', 'large'))]

data.table

x.dt %>%
  mutate(
    # category == 'news'を基準にする
    category = relevel(category, ref = 'news'),
    # 'small', 'medium', 'large'の順に並べる
    size = factor(size, levels = c('small', 'medium', 'large'))
  ) %>%
# (参考)添字を使う場合
x.dt[,category := relevel(category, ref = 'news')]
x.dt[,size := factor(size, levels = c('small', 'medium', 'large'))]

分析対象とする列の抽出、列名の変更、列の型変換をdata.tableでまとめて行う

処理が定型的なのでdplyrのパイプを使ってまとめて行うといい。

customer.dt %>%
  # 列の抽出
  dplyr::select(
    `顧客ID`, 
    `メルマガ登録`, 
    `登録店舗`, 
    `登録日時`, 
    `性別`,
    `生年月日` 
  ) %>%
  # 列名の変更
  rename(
    customer_id = `顧客ID`,
    send_mail_magazine = `メルマガ登録`,
    reg_store = `登録店舗`,
    reg_datetime = `登録日時`,
    sex = `性別`,
    birthday = `生年月日`
  ) %>%
  # 型変換
  mutate_at(vars(customer_id), funs(as.integer64)) %>% # integer64型に変換
  mutate_at(vars(send_mail_magazine), funs(.==1)) %>% # 0/1の2値変数をlogical型に変換
  mutate_at(vars(reg_store, sex), funs(as.factor)) %>% # factor型に変換
  mutate_at(vars(reg_datetime), funs(as.POSIXct(., format='%Y/%m/%d %H:%M:%S'))) %>% # 日時データをPOSIXct型に変換
  mutate_at(vars(birthday), funs(as.Date(., format = '%Y/%m/%d'))) %>% # 日付データをDate型に変換
  as.data.table -> customer.dt # 途中でdata.table型でなくなる場合があるので最後に直す

select()関数は他のパッケージと衝突することがあるので、dplyrのselect()関数は常にパッケージ名を添えてdplyr::select()とするのが安全である。列名を指定する際、スペースや日本語を含む列名はバッククォート「“`」で囲む。
日次データはPOSIXctを使う。POSIXltを使うとデータが壊れるので絶対に使わないこと。

型変換はmutate_at(vars(列名) ,funs(関数))が便利。vars()の指定方法は列名以外にも

  • 前方一致:vars(starts_with('date_'))
  • 後方一致:vars(ends_with('_id'))
  • 部分一致:vars(contains('_t_'))
  • 正規表現一致:vars(matches('^type[0-9]'))

などが使える。vars()で指定する列の数が1個であればmutate()関数でもいいのだが、

  rename(
    :
  ) %>%
  mutate(
    customer_id = as.integer64(customer_id),
    send_mail_magazine = send_mail_magazine == 1,
    reg_store = as.factor(reg_store),
    reg_datetime = as.POSIXct(reg_datetime, format='%Y/%m/%d %H:%M:%S'),
    sex = as.factor(sex),
    birthday = as.Date(birthday, format = '%Y/%m/%d')
  ) %>%
  as.data.table -> customer.dt

同様の処理をする列が複数になるとコピペの嵐になる(処理の内容を1箇所書き換えるときに列の数だけ書き換える必要が出てくる)ので、mutate_at()を使うのが洗練されたやり方である。処理の内容によってはmutate_at()mutate()を併用するのもいい。

マスタなどで全列factorに変換する場合、以下のようにすると実現できる。

x.dt %>%
  mutate_all(funs(as.factor)) %>%

データの加工や分析で使うRの使い方 の記事一覧