118  実践 str_detectとfilter

library(tidyverse)

それではだいぶ間があきましたがfilter関数で文字列をひっかけるための処理をここで実践していきましょう

filter関数の練習問題の最後で解説した内容を改めてここで記載します。

temp <- tibble(
  name = c("apple","apricot","banana","cherry"),
  price = c(120,200,98,300)
)
temp
# A tibble: 4 × 2
  name    price
  <chr>   <dbl>
1 apple     120
2 apricot   200
3 banana     98
4 cherry    300

で、aで始まるname列のみに絞りこみたい、aで終わるname列のみに絞りこみたいという場合はどうしたらよいでしょうか?

ここまで学んでいただいた内容からは、

という3つのステップを違和感なく理解することができるはずです。

この3つのステップをスクリプトで記載すると

aで始まる行のみに絞り込む:

temp %>% filter( str_detect(name,"^a") )
# A tibble: 2 × 2
  name    price
  <chr>   <dbl>
1 apple     120
2 apricot   200

aで終わる行のみに絞り込む

temp %>% filter( str_detect(name, "a$"))
# A tibble: 1 × 2
  name   price
  <chr>  <dbl>
1 banana    98

です。

他の活用例も確認しておきましょう。

str_detectを使うと、数字のはずなのに、文字列が混ざっているような行を見つけることも簡単です。

temp2 <-
   tibble(
     kensa = c("a","a","b","b","c","c"),
     value = c(100,200,"3OO","400","未","500")
   )

temp2
# A tibble: 6 × 2
  kensa value
  <chr> <chr>
1 a     100  
2 a     200  
3 b     3OO  
4 b     400  
5 c     未   
6 c     500  

こんなデータがあったとして、仮にこれが数万行のデータで、手入力されたものであった場合、入力時点での規制がかけられていない場合、多くの場合に、「想定外」のものが紛れ込んでいます。

本来、valueの列には数字しか入っていてほしくないはずなのですが、本当にそうか確認したい場合、

temp2 %>%  filter( str_detect(value, "^\\d+$") )
# A tibble: 4 × 2
  kensa value
  <chr> <chr>
1 a     100  
2 a     200  
3 b     400  
4 c     500  

そうあってほしい正規表現をかくとこのように数字ではじまり、数字でおわり、数字以外の文字列がない行を拾うことができます。

!c(TRUE,FALSE)
[1] FALSE  TRUE

で、TRUEとFALSEを逆にできること、覚えていますか?

 str_detect(c("ab","ba","bb"),"^a")
[1]  TRUE FALSE FALSE
!str_detect(c("ab","ba","bb"),"^a")
[1] FALSE  TRUE  TRUE

このように、str_detectはロジカルベクトルを返すので前に!をつけることで結果をひっくり返すことができます。なので、

 temp2 %>%  filter( !str_detect(value, "^\\d+$") ) 
# A tibble: 2 × 2
  kensa value
  <chr> <chr>
1 b     3OO  
2 c     未   

こんな感じで、「!そうあってほしい」結果をfilter関数の中に投入すると、実は「3オーオー」と「未」という数字以外の結果が紛れていることが判明します。

他にも、汚いデータの処理で注意が必要なのが全角数字の処理です。

全角数字は、\d+では認識されてしまうのですが

str_view("1234","\\d+")
[1] │ <1234>
str_detect("1234","\\d+")
[1] TRUE

数値変換しようとすると失敗します

as.numeric("1234")
[1] 1234
as.numeric("1234")
Warning: NAs introduced by coercion
[1] NA

なので全角数字が混じっている可能性があるデータなのであれば、\d+よりも、[0-9]と書いてあげる方が適切です。

str_view("1234","[0-9]")
str_detect("1234","[0-9]")
[1] FALSE
str_view("1234","[0-9]+")
[1] │ <1234>
temp3 <- tibble(
  num = c(111,"112",213,"132",323,"433",445,"445",358,"857",654,"766",778,"978",865,"867",596,"769",506,"607")
)

temp3
# A tibble: 20 × 1
   num  
   <chr>
 1 111  
 2 112 
 3 213  
 4 132 
 5 323  
 6 433 
 7 445  
 8 445 
 9 358  
10 857 
11 654  
12 766 
13 778  
14 978 
15 865  
16 867 
17 596  
18 769 
19 506  
20 607 
temp3 %>% filter(  str_detect(num,"^[0-9]+$"))
# A tibble: 10 × 1
   num  
   <chr>
 1 111  
 2 213  
 3 323  
 4 445  
 5 358  
 6 654  
 7 778  
 8 865  
 9 596  
10 506  
temp3 %>% filter( !str_detect(num,"^[0-9]+$"))
# A tibble: 10 × 1
   num  
   <chr>
 1 112 
 2 132 
 3 433 
 4 445 
 5 857 
 6 766 
 7 978 
 8 867 
 9 769 
10 607 

以上、str_detectとfilterを組み合わせて利用する方法を紹介しました。