できましたか?
これまでの知識も利用しながら実施する問題なので少し難しかったかもしれません。
Q0: data/stringr.xlsxファイルのSheet1にあるデータをよみこみましょう。
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.2 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.0
✔ ggplot2 3.4.2 ✔ tibble 3.2.1
✔ lubridate 1.9.2 ✔ tidyr 1.3.0
✔ purrr 1.0.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
dat <- read_excel("data/stringr.xlsx")
dat
# A tibble: 6 × 3
target1 target2 target3
<chr> <chr> <chr>
1 abc:500ml 1unit AST 50IU, ope:A 4.5hr 8Oml
2 def 250ml 4units HbA1c 5.0% ope:B 3hr 10ml
3 ghi 100ml 5units BMI 23.1kg/m^2 ope:C 12.5hr 1OOml
4 jkl 100ml 6units AST: 60IU ope:D 4.5hr 180ml
5 jkl 100ml 9units HbA1c 5.0% ope:E 3hr 120ml
6 abc 100ml 20units BMI 18.1kg/m^2 ope:F 12.5hr 1OOml
これは、特に問題ありませんね。これまで通りです。
Q1-1 target1列のみに列を絞って、“xxx100ml 1unit”の、unitの前の数字をぬきだして新しい列、unitを作成してください
dat1 <- dat %>%
select(target1) %>%
mutate(unit = str_extract(target1, "\\d+(?=unit)"))
dat1
# A tibble: 6 × 2
target1 unit
<chr> <chr>
1 abc:500ml 1unit 1
2 def 250ml 4units 4
3 ghi 100ml 5units 5
4 jkl 100ml 6units 6
5 jkl 100ml 9units 9
6 abc 100ml 20units 20
これも、基本的なextract関数の使い方で、正規表現そのものもそれほど複雑ではないですね?
Q1-2 Q1-1で作成した表で、target1列の”xxx100ml 1unit”の、ml前の数字をぬきだして、mlではなくてL単位に変換(100mlなら0.1L)してlitterという新しい列を追加してください。
dat1 <- dat1 %>%
mutate(ml = str_extract(target1,"\\d+(?=ml)"))
dat1 <- dat1 %>%
mutate(litter = as.numeric(ml)/1000)
dat1
# A tibble: 6 × 4
target1 unit ml litter
<chr> <chr> <chr> <dbl>
1 abc:500ml 1unit 1 500 0.5
2 def 250ml 4units 4 250 0.25
3 ghi 100ml 5units 5 100 0.1
4 jkl 100ml 6units 6 100 0.1
5 jkl 100ml 9units 9 100 0.1
6 abc 100ml 20units 20 100 0.1
すこし込み入ってきました。as.numericで文字列を数字型に変換してあげれば計算することができます。
Q1-3 Q1-2で作成した表で、target1の”xxx100ml 2unit”の、xxx部分を抜き出して、nameという名前の新しい列を作成してください。尚前後に余分なスペースがあればname列から削除してください。
最初の正規表現的な難問です。
dat1 <- dat1 %>%
mutate(name = str_extract(target1,"^.+(?=(:| ))"))
dat1
# A tibble: 6 × 5
target1 unit ml litter name
<chr> <chr> <chr> <dbl> <chr>
1 abc:500ml 1unit 1 500 0.5 abc:500ml
2 def 250ml 4units 4 250 0.25 def 250ml
3 ghi 100ml 5units 5 100 0.1 ghi 100ml
4 jkl 100ml 6units 6 100 0.1 jkl 100ml
5 jkl 100ml 9units 9 100 0.1 jkl 100ml
6 abc 100ml 20units 20 100 0.1 abc 100ml
このように、Look Aroundでスペースかコロンを指定しても、後ろのスペースのところまでを.+が拾ってしまいます。
こういう場合は、正規表現のところで解説した.+?という+のマッチする範囲を最小に絞るという表記を利用しましょう。
dat1 <- dat1 %>%
mutate(name = str_extract(target1,"^.+?(?=(:| ))"))
dat1
# A tibble: 6 × 5
target1 unit ml litter name
<chr> <chr> <chr> <dbl> <chr>
1 abc:500ml 1unit 1 500 0.5 abc
2 def 250ml 4units 4 250 0.25 def
3 ghi 100ml 5units 5 100 0.1 ghi
4 jkl 100ml 6units 6 100 0.1 jkl
5 jkl 100ml 9units 9 100 0.1 jkl
6 abc 100ml 20units 20 100 0.1 abc
できました!
Q1-4 Q1-3で作成した表で、target1列を削除して、name, litter, unitの順に列の順番を並べてください
# A tibble: 6 × 5
target1 unit ml litter name
<chr> <chr> <chr> <dbl> <chr>
1 abc:500ml 1unit 1 500 0.5 abc
2 def 250ml 4units 4 250 0.25 def
3 ghi 100ml 5units 5 100 0.1 ghi
4 jkl 100ml 6units 6 100 0.1 jkl
5 jkl 100ml 9units 9 100 0.1 jkl
6 abc 100ml 20units 20 100 0.1 abc
[1] "target1" "unit" "ml" "litter" "name"
から、必要なものだけを選べばよいので、問題文通りにするのであれば、
dat1 %>%
select(!c(target1, ml)) %>%
select(name, litter, unit)
# A tibble: 6 × 3
name litter unit
<chr> <dbl> <chr>
1 abc 0.5 1
2 def 0.25 4
3 ghi 0.1 5
4 jkl 0.1 6
5 jkl 0.1 9
6 abc 0.1 20
ですが、selectはそもそもそれ以外削除するので、
dat1 %>% select(name,litter,unit)
# A tibble: 6 × 3
name litter unit
<chr> <dbl> <chr>
1 abc 0.5 1
2 def 0.25 4
3 ghi 0.1 5
4 jkl 0.1 6
5 jkl 0.1 9
6 abc 0.1 20
でOKです。
Q2-1 target2列のみに絞って、検査結果のみを抜き出して新しいValue列を作成してください
dat2 <- dat %>% select(target2)
dat2
# A tibble: 6 × 1
target2
<chr>
1 AST 50IU,
2 HbA1c 5.0%
3 BMI 23.1kg/m^2
4 AST: 60IU
5 HbA1c 5.0%
6 BMI 18.1kg/m^2
dat2 <- dat2 %>%
mutate(value = str_extract(target2,"(?<=\\s)(\\d+\\.\\d+|\\d+)"))
Q2-2 Q2-1で作成した表に、target2列の単位を抜き出して新しい列、unitを作成してください。
正規表現で直接抜き出すのは難しいのでstr_replaceを使いましょう
dat2 <- dat2 %>%
mutate(num_unit = str_extract(target2, "(?<=\\s).+$")) %>%
mutate(unit = str_replace(num_unit,"\\d+\\.\\d+|\\d+",""))
dat2
# A tibble: 6 × 4
target2 value num_unit unit
<chr> <chr> <chr> <chr>
1 AST 50IU, 50 50IU, IU,
2 HbA1c 5.0% 5.0 5.0% %
3 BMI 23.1kg/m^2 23.1 23.1kg/m^2 kg/m^2
4 AST: 60IU 60 60IU IU
5 HbA1c 5.0% 5.0 5.0% %
6 BMI 18.1kg/m^2 18.1 18.1kg/m^2 kg/m^2
なぜか1行目のIUの最後にカンマがありますね(これは本気で私が消し忘れていたカンマです。ついでにこれをけしておきましょう。)
dat2 <- dat2 %>%
mutate(unit = str_replace(unit,",$",""))
dat2
# A tibble: 6 × 4
target2 value num_unit unit
<chr> <chr> <chr> <chr>
1 AST 50IU, 50 50IU, IU
2 HbA1c 5.0% 5.0 5.0% %
3 BMI 23.1kg/m^2 23.1 23.1kg/m^2 kg/m^2
4 AST: 60IU 60 60IU IU
5 HbA1c 5.0% 5.0 5.0% %
6 BMI 18.1kg/m^2 18.1 18.1kg/m^2 kg/m^2
Q2-3 Q2-2で作成した表に、target2列の検査名を抜き出して、新しい列、nameを作成してください。
dat2 <- dat2 %>%
mutate(name = str_extract(target2,".+?(?=(:|\\s))"))
dat2
# A tibble: 6 × 5
target2 value num_unit unit name
<chr> <chr> <chr> <chr> <chr>
1 AST 50IU, 50 50IU, IU AST
2 HbA1c 5.0% 5.0 5.0% % HbA1c
3 BMI 23.1kg/m^2 23.1 23.1kg/m^2 kg/m^2 BMI
4 AST: 60IU 60 60IU IU AST
5 HbA1c 5.0% 5.0 5.0% % HbA1c
6 BMI 18.1kg/m^2 18.1 18.1kg/m^2 kg/m^2 BMI
これも.+?とつけないと、:を省いて抜き出すことができませんね
Q2-4 Q2-3で作成した表の、target2列を削除して、name,value,unitの順番に列を並び替えてください
dat2 %>% select(name, value, unit)
# A tibble: 6 × 3
name value unit
<chr> <chr> <chr>
1 AST 50 IU
2 HbA1c 5.0 %
3 BMI 23.1 kg/m^2
4 AST 60 IU
5 HbA1c 5.0 %
6 BMI 18.1 kg/m^2
Q3-1 target3列のみに列を絞って、手術名(op:XX)を抜き出して新しい列nameを作成してください
dat3 <- dat %>%
select(target3) %>%
mutate(name = str_extract(target3,"(?<=ope:).+?(?= \\d)"))
Q3-2 Q3-1で作成した表の、 target3列のの出血量をぬきだして新しい列、blood_lostを作成してください。今までと同じ方法ではうまくいきませんがなぜでしょうか?元のデータは触らずに、本来入力したかったであろう数値に置き換えてください。
これではうまくいきません
dat3 %>%
mutate(blood_lost = str_extract(target3,"\\d+(?=ml)"))
# A tibble: 6 × 3
target3 name blood_lost
<chr> <chr> <chr>
1 ope:A 4.5hr 8Oml A <NA>
2 ope:B 3hr 10ml B 10
3 ope:C 12.5hr 1OOml C <NA>
4 ope:D 4.5hr 180ml D 180
5 ope:E 3hr 120ml E 120
6 ope:F 12.5hr 1OOml F <NA>
実は
dat3$target3 %>%
str_view("O") #大文字のオー
[1] │ ope:A 4.5hr 8<O>ml
[3] │ ope:C 12.5hr 1<O><O>ml
[6] │ ope:F 12.5hr 1<O><O>ml
という風に、一部のゼロが大文字のオーに置き換わっています。、
dat3 %>%
mutate(blood_lost = str_extract(target3,"(?<=hr\\s).+(?=ml)")) %>%
mutate(blood_lost = str_replace(blood_lost,"O","0"))
# A tibble: 6 × 3
target3 name blood_lost
<chr> <chr> <chr>
1 ope:A 4.5hr 8Oml A 80
2 ope:B 3hr 10ml B 10
3 ope:C 12.5hr 1OOml C 10O
4 ope:D 4.5hr 180ml D 180
5 ope:E 3hr 120ml E 120
6 ope:F 12.5hr 1OOml F 10O
抜き出してから大文字のオーを、数字のゼロに置き換えます。ただし、オーが二つあるデータもあるので、一回だけでは全部置き換えられません。
dat3 %>%
mutate(blood_lost = str_extract(target3,"(?<=hr\\s).+(?=ml)")) %>%
mutate(blood_lost = str_replace(blood_lost,"O","0")) %>%
mutate(blood_lost = str_replace(blood_lost,"O","0"))
# A tibble: 6 × 3
target3 name blood_lost
<chr> <chr> <chr>
1 ope:A 4.5hr 8Oml A 80
2 ope:B 3hr 10ml B 10
3 ope:C 12.5hr 1OOml C 100
4 ope:D 4.5hr 180ml D 180
5 ope:E 3hr 120ml E 120
6 ope:F 12.5hr 1OOml F 100
このように、2回置き換えてもよいですが、それだと何回置き換えてよいかわからないケースも多いので、全部置き換えるstr_replace_allを利用しましょう。
dat3 <- dat3 %>%
mutate(blood_lost = str_extract(target3,"(?<=hr\\s).+(?=ml)")) %>%
mutate(blood_lost = str_replace_all(blood_lost,"O","0"))
dat3
# A tibble: 6 × 3
target3 name blood_lost
<chr> <chr> <chr>
1 ope:A 4.5hr 8Oml A 80
2 ope:B 3hr 10ml B 10
3 ope:C 12.5hr 1OOml C 100
4 ope:D 4.5hr 180ml D 180
5 ope:E 3hr 120ml E 120
6 ope:F 12.5hr 1OOml F 100
この_allが作関数他にもあって
str_view_all(dat3$target3,"O")
Warning: `str_view()` was deprecated in stringr 1.5.0.
ℹ Please use `str_view_all()` instead.
[1] │ ope:A 4.5hr 8<O>ml
[2] │ ope:B 3hr 10ml
[3] │ ope:C 12.5hr 1<O><O>ml
[4] │ ope:D 4.5hr 180ml
[5] │ ope:E 3hr 120ml
[6] │ ope:F 12.5hr 1<O><O>ml
str_extract_all(c("abc","aba","abcba"), "a")
[[1]]
[1] "a"
[[2]]
[1] "a" "a"
[[3]]
[1] "a" "a"
こんな感じでviewやextractにもあります。extractの結果はListというもので、まだ解説していませんので、わからなくて結構です。
また、置き換えるときに、複数個置き換えたいばあいは、str_replace_allを利用すると便利です。
Q3-3 Q3-2で作成した表の、target3列のhr前の時間を抜き出して新しい列、time_hrを作成してください。
dat3 <- dat3 %>%
mutate(time_hr = str_extract(target3,"(?<=\\s)(\\d+\\.\\d+|\\d+)(?=hr)"))
Q3-4 Q3-3で作成した表の、target3列を削除して、name, blood_lost, time_hrの順番に並び替えてください。
dat3 %>%
select(name, blood_lost, time_hr)
# A tibble: 6 × 3
name blood_lost time_hr
<chr> <chr> <chr>
1 A 80 4.5
2 B 10 3
3 C 100 12.5
4 D 180 4.5
5 E 120 3
6 F 100 12.5
以上でstringrの練習問題の解説終了です!