以前,エクセルで英文の中の特定の単語までの語数を数えるという記事を書きました。今回はそれよりもっと単純で,Rのデータフレームの中の1部に英文が入っていて,その英文の語数を数えてデータフレームに付け足したいなっていう作業です。

こんな感じでロング型のデータがdatという変数にあるとします。このデータフレームのなかの,sentenceという列には英文が入っていますが,それぞれ長さが違います。文が長いほうが反応時間も長くなっちゃいますから,そのために英文に含まれる語数を数えてそれも分析に使いましょうということです。僕がやっているような反応時間を扱う実験ではよくありがちな手続きです。本来なら刺激文作るときにそれもちゃんと数えておいて(エクセルで語数数えるのは簡単です。前掲の記事の一部がそれです),結果が出力される際にちゃんと語数の情報も含めておけばいいんですけど,まぁ今回はそれ忘れちゃったんでRの中でやってしまおうという事後的な対処法です。
さてさて前置きが長くなりました。Rでの文字列処理の基本は「分ける」・「数える」だと思います。僕はそんなにRで文字列処理しないんですが,今回の場合で言うと,データフレームの要素に含まれる英文をスペースで区切り,区切ったときにいくつかに分けられた要素の数を数えることで語数をカウントするという手続きになります。
今回使う関数は次の4つです。
- (as.character関数)
- strsplit関数
- length関数
- sapply関数
順番に説明していきます。strsplit関数は
strsplit(分けたいもの,区切り文字)
という感じで使います。今回の場合,分けたいのはdatの中のsentenceという列(dat$sentence)で,スペースで区切りますから,
strsplit(dat$sentence, ” “)
とすれば,

こんな感じで単語ごとに区切ってくれます。この出力は1文ずつのリスト形式になっていて,そのリストの要素が英文を構成する単語になっています。もしも,このstrsplit関数がうまくいかなければ,
is.character(dat$sentence)
として,データフレーム中の英文が文字列形式で格納されているかどうか確かめてみてください。因子型(factorial)になっていると,strsplit関数は適用されません。これを変えるのが,as.character関数です。
strsplit(as.character(dat$sentence))
または,
dat$sentence<-as.character(dat$sentence)
strsplit(dat$sentence, ” “)
としてみてください。
さて,これでひとまず「区切る」作業はできましたが,まだ「数える」にはいたっていませんよね。数えるためによく使われるのが要素の数を数えるlength関数です。ただし,例えば
length(strsplit(as.character(dat$sentence)))
としてもうまくいきません。これでは出力されたリストの要素を数えるので,元のデータフレームの行数が表示されるはずです。ここでは,「リストのそれぞれの要素に対してlength関数を適用したい」わけなので,もうひと工夫必要です。そこで使うのがapply系列のsapply関数です。この関数は,
sapply(リスト,関数)
という形で引数を与えてあげると,リストの各要素に関数を適用してくれます。これと同じことができるのがlapply関数です。lapply関数を使うと,結果の出力もリスト形式で返されます。ただ,ここでは語数が並んだベクトル形式のものを手に入れて,それをもとのデータフレーム(dat)に追加したいわけなので,sapply関数のほうがベターです。lapply関数にすると,そのあとリストをベクトルに直すunlist関数を使う必要があって二度手間になってしまうので。というわけで,
sapply(strsplit(as.character(dat$sentence), ” “), length)
こうしてあげれば「スペースで区切る-> 語数を数える -> ベクトル形式で結果をゲット」という作業が完了します。あとは,
dat$num.words<-sapply(strsplit(as.character(dat$sentence), ” “), length)
としてあげれば,datに新しくnum.wordsという列が追加され,そこにはsentenceの列に入っている英文の語数が格納されることになります。めでたしめでたし。といきたいところなのですが,最後におまけでもうひとつ。
上の書き方だと,いくつもの関数が入れ子状態になっていて,一見して作業がわかりにくいですよね?そこでパイプ演算子を使って同じことをもう少しわかりやすく書き直してあげます。
dat$sentence%>% #datの中のsentenceという列を取ってくる
as.character%>% #文字列型に変換
strsplit(. , ” “)%>% #スペースで区切る
sapply(., length) -> dat$num.words #リストの要素ごとに語数を数えてdatというデータフレームのnum.wordsという列にいれる
こうやって書くと,作業の過程が見えてわかりやすいですよね。ちなみにこのパイプ演算子はパッケージ依存です。dplyrやtidyrを使うときに使うやつですね。たぶんこれらのパッケージを読み込んでいればパイプ演算子も使えるようになっているはずです。もともとはmagrittrというパッケージに入っているのがパイプ演算子が%>%なんですよね(たしか)?
というわけでlapplyの出力をunlistしてできたできたと思っていたらsapply関数の存在に気づいてなんだよ二度手間じゃんかよって思ったのでブログ記事にしました。
なにをゆう たむらゆう。
おしまい。