R言語による医療データ分析

R言語によるデータ分析のオンラインコースを中心に、さまざまなデータ分析について記載してあります。

101-103 リレーションとXXX_join関数

まとめ一覧

リレーションとXXX_join関数

リレーションをイメージで把握する

  • 基本的に、「表で保存されている」データは、「リレーショナルデータベース」に保存されています。
  • データ分析を行うにあたって、必要な考え方のみイメージでお伝えします

f:id:tentencon:20191222102037j:plain


f:id:tentencon:20191222102053j:plain


f:id:tentencon:20191222102115j:plain


f:id:tentencon:20191222102230j:plain

left_join

f:id:tentencon:20191222102255j:plain


f:id:tentencon:20191222102311j:plain


f:id:tentencon:20191222102328j:plain

  • 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の処理のまとめは次のような形です

f:id:tentencon:20191221165244j:plain


f:id:tentencon:20191221165319j:plain


f:id:tentencon:20191221165346j:plain


f:id:tentencon:20191221165408j:plain


f:id:tentencon:20191221165432j:plain


  • 実際にデータに適応してみると。。
#横持データ
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の処理をまとめると次のような形です

f:id:tentencon:20191222101313j:plain


f:id:tentencon:20191222101331j:plain


f:id:tentencon:20191222101403j:plain


f:id:tentencon:20191222101424j:plain


  • 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")

どうでしょうか?

まとめ一覧