Note of Pediatric Surgery

腸内細菌、R、ときどき小児外科

Rで全角英数記号を半角にするのに{stringi}が役に立った話

はじめに ( 愚痴 )

仕事がらあまりデータ解析をしない人からExcelファイルをもらうことが多く、自分が扱いやすいデータにするための処理が実は解析の中で一番大変だったりします。もちろんExcelの生データをExcelを使って処理してしまえばそんなに大変ではないのですが、インフォマのはしくれとして御法度としています。解析のreoproducibilityというのは昨今の大きなテーマではありますが、後から見てどこをどう変更したのかが可視化できないデータの処理の仕方は、再現性を保証できませんし、何より後で自分が困ります。

なので、Rでスクリプトを書いてExcelを処理するのですが、その中の1つの大きな障壁が " 表記の揺れ " だと思います。半角と全角が混在する、大文字と小文字の使い方がバラバラなどなど。例えば、下でも書きますが AST(GOT )みたいな記載が実際あるんですよ、うちの電子カルテから血液検査の結果を引っ張ってくるとそうなるんです。

そういうデータを作る人って本当に美的感覚がどうにかしてる ( 電子カルテだからプログラマーが関与してるはずなんだが ) 。

前置きが長くなりましたが、今回は全角と半角が混在したデータを半角のみに変換する方法をまとめてみました。久保先生のサイトでも話題になっていますが、割とめんどうくさいようですね。結論からいうと@yutannihilationさんから教えていただいた、stringi::stri_trans_nfkc()がベストだと思います。

KuboLog 2014-10-15

糞データを生成する

まずは怒りの糞データを自分で生成してみます。全角と半角が混ざってますね。丸カッコは全角です、半角スペース + 半角丸カッコにも見えるから余計に紛らわしい。

# MacOSの言語設定を英語にしているとデータが文字化けして生成できない可能性があります
# 必要であればlocaleの設定を変更してください
# Sys.setlocale(category = "LC_ALL",
#              locale ='UTF-8')

library(tidyverse)
df <-
  data.frame(A = rnorm(20,50,10),
             B = rnorm(20,3.0,1.0),
             C = rnorm(20,80,12)) %>%
  rename("`AST(GOT)`"="A",
         "`FT3`"="B",
         "`γーGTP`"="C")
> head(df)
  `AST(GOT)`   `FT3`  `γーGTP`
1           46.28355 3.937829  68.88831
2           34.30544 1.921467  92.23177
3           43.96877 3.358519  82.19175
4           28.67900 2.570615  87.99679
5           48.68699 2.419228  81.34998
6           49.27994 3.241242 101.59917

いい感じの全角と半角が入り混じった糞データが生成できました。

1. Nippon::zen2han()

Googleで検索してみると一番最初に引っかかったのは、こちらの記事。{Nippon}のzen2han()です。早速インストールして使ってみます。

d.hatena.ne.jp

install.packages("Nippon")
library(Nippon)

zen2han("AST(GOT)")
> zen2han("AST(GOT)")
[1] "AST(GOT)"

と英字も記号も半角になっております、おお素晴らしい。ですが、このzen2han()をdplyrのパイプ処理に渡してあげると問題が発生します。

df %>%
  setNames(zen2han(names(.))) %>%
  head(.)
# A tibble: 6 x 3
> df %>%
+     setNames(zen2han(names(.))) %>%
+     head(.)

  `AST(GOT)``FT3``γーGTP`       NA        NA
1                46.28355 3.937829  68.88831
2                34.30544 1.921467  92.23177
3                43.96877 3.358519  82.19175
4                28.67900 2.570615  87.99679
5                48.68699 2.419228  81.34998
6                49.27994 3.241242 101.59917

おわかりでしょうか?一番左の列名に、すべての列名が半角になってつながった文字列が格納され、その他の列名はNAになってしまいます…なんじゃこりゃ。というわけで、こちらは却下。

2. halfwidthr::halfwidthen()

次に引っかかったのは@yutannihilationさんのブログ。{Rccp}というC++をRで動かすパッケージ??を使って作成したようです。インストールして使ってみます。

notchained.hatenablog.com

library(devtools)
install_github("yutannihilation/halfwidthr")
library(halfwidthr)

halfwidthen("AST(GOT)")
> halfwidthen("AST(GOT)")
[1] "AST(GOT)"

これ、はてなの仕様だと思うのですが、はてなに貼り付けたものはカッコが半角になっていますが、実際のRの出力結果はカッコが全角のままになっており、こちらは残念ながら記号に関しては変換ができないようです、残念。

3. stringi::stri_trans_nfkc

さて本命のstringi::stri_trans_nfkc()です。{halfwidthr}で上手く処理できなかったことをつぶやいていたら神のお告げが聞こえてきました。

というわけで{stringi}を使用してみます。どうやらRでのテキスト処理をかなり快適にしてくれるパッケージみたいですね。まだ全角半角問題にしか使用していませんが、もう少し勉強してみます。

qiita.com

install.packages("stringi")
library('stringi')
stri_trans_nfkc("AST(GOT)")
> stri_trans_nfkc("AST(GOT)")
[1] "AST(GOT)"

おお、英語も記号も半角にできました。これを肝心のdplyrのパイプ処理で動かしてみると

df %>%
  setNames(stri_trans_nfkc(names(.))) %>%
  head(.)
> df %>%
+     setNames(stri_trans_nfkc(names(.))) %>%
+     head(.)

  `AST(GOT)`    `FT3`  `γーGTP`
1   46.28355 3.937829  68.88831
2   34.30544 1.921467  92.23177
3   43.96877 3.358519  82.19175
4   28.67900 2.570615  87.99679
5   48.68699 2.419228  81.34998
6   49.27994 3.241242 101.59917

あー惜しい。全角のだけそのままですが、これは他のパッケージ使っても厳しそうなので、gsub("ー","-",.)などを加えて修正するしかなさそうです。が、とっても実用的。もっと早く勉強しておけばよかったテキスト処理。stringi::stri_trans_nfkc()、無茶苦茶良さそうですね。@yutannihilationさん、いつもありがとうございます。