101-103 リレーションとXXX_join関数
リレーションとXXX_join関数
リレーションをイメージで把握する
- 基本的に、「表で保存されている」データは、「リレーショナルデータベース」に保存されています。
- データ分析を行うにあたって、必要な考え方のみイメージでお伝えします
left_join
left_join
を説明する目的のために、dplyrに入っている、band_members
とband_instruments
で説明します。
library(tidyverse) band_members band_instruments band_instruments2 left_join(band_members, band_instruments, by = "name") left_join(band_members, band_instruments, by = c("name" = "name")) band_members %>% left_join(band_instruments, by = "name") left_join(band_members, band_instruments2, by = "name")
- エラー!
band_instruments2
- コラム名が、artist とplayerで、artistを利用したいので、
band_members %>% left_join(band_instruments2, by =c("name" = "artist"))
他のjoin
- 多くのケースでは、left_joinが使えれば、何とかなるので、例とその動作のみ簡単に説明します。
- 尚、この例は、すべて、?left_joinでみられるヘルプファイルからのコピーです。
# "Mutating" joins add variables to the LHS band_members %>% inner_join(band_instruments, by="name") #両方にあるものを残す。 band_members %>% left_join(band_instruments) #左にあるものを残す band_members %>% right_join(band_instruments) #右にあるものを残す band_members %>% full_join(band_instruments) #どちらか片方にあるものを残す
098-100 gather&spread
データの方向の転換
- gatherとspreadの解説を行います。
- ただし、tidyrのV1.1以降では、
pivot_longer
,pivot_wider
という新しい関数が導入されています - 考え方はほぼ一緒ですが、
pivot_XXX
系の関数の方ができることが増えているので、これから学ばれる方はpivot_XXX
系の関数を利用することをお勧めします。 gather
<->pivot_longer
spread
<->piot_wider
gather
- gatherの処理のまとめは次のような形です
- 実際にデータに適応してみると。。
#横持データ dfyoko <- tibble( tosi = c("札幌","東京","那覇"), `2018-04-01` = c("晴れ", "雨", "曇り"), `2018-04-02` = c("雨", "雨", "晴れ"), `2018-04-03` = c("晴れ", "雨", "曇り"), `2018-04-04` = c("晴れ", "雨", "雨") )
- このdfyokoは典型的な「横持データ」と呼ばれているものです。
- 人には見やすいですが、tidyなデータではないということはお判りでしょうか?
dfyoko <- tibble( tosi = c("札幌","東京","那覇"), `2018-04-01` = c("晴れ", "雨", "曇り"), `2018-04-02` = c("雨", "雨", "晴れ"), `2018-04-03` = c("晴れ", "雨", "曇り"), `2018-04-04` = c("晴れ", "雨", "雨") ) dfyoko <- dfyoko %>% gather(-tosi, key=date_tenki, value = tenki)
- 尚、pivot_longerで書く場合は
- key <-> names_to
- value <-> values_to
dfyoko %>% pivot_longer(cols = -c(tosi), names_to = "date_tenki", values_to = "tenki")
spread
- spreadの処理をまとめると次のような形です
- dfyokoをもとの横持ちデータにもどしてみます。
dfyoko <- dfyoko %>% spread(key = date_tenki, value = tenki) #注、dateを列名でつかわないほうがよい理由:スライドでは、dateをつかっていましたが、dateはもともと関数です。エラーが生じるので、基本的には列名やオブジェクトの名前に他の関数と同じ名前を付けることは避けてください。
- よくあるエラー
dfyoko_error <- tibble( tosi = c(rep("札幌",4)), hiduke = c("2018-04-01","2018-04-02","2018-04-03","2018-04-01"), tenki = c("晴れ","雨","雨","雨") ) dfyoko_error dfyoko <- dfyoko_error %>% spread(key = hiduke, value = tenki)
Error: Duplicate identifiers for rows (1, 4)
- このような表示がでるのは、なぜでしょうか?
- 1行目と4行目のIdentifiers(札幌かつ2018-04-01)がDuplicate(重複)してますね。
手でこのtibbleを横方向にしようとしても、2018年4月1日の札幌の天気はどっち(晴れ/雨)なんだと悩みます。
こういったエラーの対処については、どのようにしてこの事態が発生したか、発生のメカニズムによって対処方法が違います。
ここでは、その内容には踏み込みません
尚、単純に同じデータが二回記録されているようなケースでは、
df_dup <- tibble( tosi = c(rep("札幌",4)), hiduke = c("2018-04-01","2018-04-02","2018-04-03","2018-04-01"), tenki = c("雨","雨","雨","雨") ) df_dup df_dup <- df_dup %>% distinct()
- とすることにより、重複列が削除されますので、
df_dup <- df_dup %>% distinct() %>% spread(key = hiduke, value = tenki) #横持にできます!
097 特殊加工:separateで列をわける
separateで列をうめる
- 練習問題を再度みてみましよう。
- 任意の文字列で列を分割するseparate関数を利用してみましょう。
dft <- tibble( target3 = c( "ope:A 4.5hr 80ml", "ope:B 3hr 10ml", "ope:C 12.5hr 100ml" ) ) dft
- separate(data, col, into, sep)で利用します。
- dataはデータフレーム、colはコラム名。
- intoは、分割した後のコラム名、
- sepは分割のしるしとなる文字列
dft %>% separate(col = target3, into = c("OPE","HR","ML"), sep=" ")
- このように、スペース(\s)で3つのコラムに分割され、もともとのtarget3は消えて、そのかわり、intoで指定した、OPE, HR, MLが出現しています。
dft %>% separate(col = target3, into = c("OPE", "HR", "ML"), sep=" ", remove = FALSE)
尚、remove=FALSEとすることで、削除せずに残すことも可能です。
課題:OPEコラムから、set_extractを使わずにオペ名を取り出してみましょう
dft %>% separate(col = target3, into = c("OPE", "HR", "ML"), sep=" ") %>% separate(col = OPE, into = c("ope","opename"), sep = ":") %>% select(opename)
- str_extractを利用すると、
dft %>% mutate(opename = str_extract(target3,"(?<=:)\\w+"))
- とより簡潔に書けますが、状況によっては、str_extractの正規表現が難しいケースもあり、そういう場合には、separateを利用することがよいと思います。
096 特殊加工:fillで列をうめる
fillで列をうめる
- これまでは、mutate filter arrange等の列や行を操作する関数は、dplyrパッケージ、
- strではじまる正規表現を利用できる関数は、stringrパッケージの解説を行ってきました
- 残りはfill separate gather spreadはtidyrパッケージの関数です。
dffill <- tibble(name = c("a",NA,NA,"b",NA,NA), type = rep(c("age","gender","bld"),2), value = c("20","男","O","31","女","AB"))
- のように、人からみたら空白に見えるが意味のある行を埋めるのに使います。
dffill dffill %>% fill(name) dffill dffill %>% fill(name, .direction = "up") #.directionを設定することで上下に任意に埋めることができます。
093-095 if_else, case_when:列内での条件分岐
列内での条件分岐
if_else
dfif <- tibble(num =c(1:10)) dfif <- dfif %>% mutate(bool = num>5)
- if_elseはBooleanを判断して、TRUEとFALSEで処理をわける関数です。
if_else(FALSE, "trueです", "falseです") if_else(c(TRUE,TRUE,FALSE), "trueだよ", "falseだよ") if_else(c(TRUE,NA,FALSE), "trueだよ", "falseだよ", "NAだよ")
- これとmutateを組み合わせると、
dfif %>% mutate(bool = num>4) dfif %>% mutate(bool = num>4) %>% mutate(kekka = if_else(bool, "4より大きいよ", "4より小さいよ"))
- 例では、一度boolというBooleanのベクトルを作っていますが、
dfif %>% mutate( kekka = if_else( num < 3, "<3",">=3" ) ) dfif <- dfif %>% select(-bool)
- でもOKです
dfif %>% mutate( kekka = if_else( num < 3, "0",">=3",NA_character_ ) )
case_when
if_elseはTRUE/FALSEをかえす2条件のみですが、case_whenは複数条件で条件分岐ができます。
<Booleanとなる条件式> ~ <返したい結果>
を繰り返すことで好きな条件で結果を返せます。
dfif %>% mutate(kekka = case_when( num == 1 ~ "one", num == 2 ~ "two", num == 3 ~ "three", TRUE ~ "else" ))
088-090 練習課題
練習課題
- 次のデータについて、
dft <- tibble( target1 = c("abc500ml 3unit" ,"def250ml 4unit","ghi100ml 5unit" ), target2 = c("AST 50IU" ,"HbA1c 5.0%" ,"BMI 23.1kg/m^2" ), target3 = c("ope:A 4.5hr 80ml","ope:B 3hr 10ml","ope:C 12.5hr 100ml") )
- 1 target1の"xxx100ml 2unit"の、unit前の数字をぬきだしてください。
answer <- dft %>% mutate(a = str_extract(target1,"\\d+(?=unit)")) answer
- 2 target1の"xxx100ml 2unit"の、ml前の数字をぬきだしてください。
answer <- dft %>% mutate(a = str_extract(target1,"\\d+(?=ml)")) answer
- 3 target1の"xxx100ml 2unit"の、xxx部分を抜き出して下さい。
answer <- dft %>% mutate(a = str_extract(target1,"[a-z]+(?=\\d+)")) answer
- 4 target2の検査結果のみを抜き出してください。
answer <- dft %>% mutate( a = str_extract( target2, "(?<=\\s)((\\d+\\.\\d+)|(\\d+))" ) ) answer
- 5 target2の単位を抜き出して下さい
- (str_replace(<ベクトル>,<正規表現>,<おきかえ>)を使うとマッチしたものをおきかえられます。)
answer <- dft %>% select(target2) %>% mutate( a = str_extract( target2, "(?<=\\s).+$" ) ) %>% mutate( a2 = str_replace( a, "^(\\d+\\.\\d+)|^(\\d+)", "" ) ) answer
- 6 target3の手術名を抜き出して下さい
asnwer <- dft %>% mutate(a = str_extract(target3,"(?<=:)\\w+"))
- 7 target3の出血量をぬきだしてください。
answer <- dft %>% mutate(a = str_extract(target3, "\\d+(?=ml)"))
087 filterで行の絞り込み
filter
で行の絞り込み
filter
の説明です。
test <- tibble(umare = c(1990, 1992, 1997, 1991), height = c(180.0, 176.2, 165.5, 172.3), weight = c(70.2, 80.3,65.3,61.1)) test test$umare > 1995
- このBooleanをfilterはtibbleの列に適応して、TRUEであるものを抜き出します。
test test %>% filter(umare > 1995) test %>% filter(height >= 175) #実際にdiamondsにfilterを適応してみましょう diamonds %>% filter(color == "E") diamonds %>% filter(clarity=="SI1"|clarity=="SI2") diamonds %>% filter(str_detect(clarity,"^SI\\d+$")) diamonds$clarity %>% summary() diamonds %>% filter(str_detect(clarity,"\\d")) diamonds %>% filter(clarity != "IF")
どうでしょうか?