Force.com と R 言語でビッグデータ統計分析 – Salesforce.com のお客様の声をデータマイニング -

この記事では、統計分析環境 R と R パッケージの RForcecom を用いて、Salesforce.com に格納された顧客情報データからデータマイニングを行う手順についてサンプルケースを用いて説明します。

Force.com (Salesforce.com) とは

Salesforce.com とは、セールスフォース・ドット・コム社によって提供されている SaaS 型の CRM (顧客情報管理システム) です。類似の SaaS サービスでは、Microsoft 社の Dynamics CRM、ソフトウェアパッケージとしては、Oracle 社の Siebel、SAP 社の SAP CRM、OSS の SugerCRM などが有名です。

f:id:hiratake55:20131220013754p:plain Salesforce.com の画面イメージ

Force.com は、Salesforce.com から顧客管理に特化した機能を取り除いた機能削減バージョン (PaaS) で、Force.com と Salesforce.com は基本的な機能 (各種データ管理、Apex, VisualForce, 各種 API 等) が共通化されています。

統計分析環境 R とは

統計分析環境 R とは、データ操作と統計分析、データ可視化のためのオープンソースソフトウェアです。R では、R 言語によるスクリプト操作にて各種の分析を行います。類似の商用製品では SAS 社の SASIBM 社の SPSS などが挙げられます。

R の特徴の1つとして、有志によって作成されたアドオンパッケージが CRAN (The Comprehensive R Archive Network) レポジトリに 5,000 以上登録されており、非常に多くの種類の統計分析が行え、分析機能の網羅性に関しては数ある統計分析ソフトウェアの中でも群を抜いてトップといえます。

f:id:hiratake55:20131221133349p:plain R の統合開発環境 RStudio の画面イメージ

RForcecom とは

RForcecom は、R から Force.com (Salesforce.com) のデータを抽出したり、R 上からのデータの登録、更新、削除、アップサートが可能な R パッケージで、私が開発、メンテナンスを行っています。2012 年 4 月に初回バージョンをリリースして以来、バージョンアップを重ねて現在に至ります。

f:id:hiratake55:20131221141959p:plain RForcecom パッケージの概要

今回の分析ケース (Salesforce.com のお客様の声を統計分析)

分析の概要

今回の分析は、顧客管理システムに Salesforce.com を利用している企業で、コールセンターのオペレーターより登録された「お客様の声」を R で日本語形態素解析自然言語処理し、統計分析により頻出キーワード、急上昇したキーワードを抽出し可視化します。

f:id:hiratake55:20131221124405p:plain 今回の分析ケースのイメージ

今回使用するデータ

本例では、実際のデータを利用することは困難であるため、代替として、Twitter によるアクティブサポート (お客様からのお問い合わせ窓口として Twitter を活用) を実施している企業として、ソフトバンク社の Twitter アカウント (@SBCare) 宛のツイートログを一定期間クロールし、その内容を Salesforce.com に登録しました。

1. Salesforce.com に登録されたデータを確認

クロールしたデータは、以下のように Salesforce.com に登録されています。

f:id:hiratake55:20131221124521p:plain Salesforce.com のレコード一覧画面

f:id:hiratake55:20131221124537p:plain Salesforce.com のレコード詳細画面

2. RForcecom パッケージで Salesforce.com からデータを取得する

RForcecom パッケージの インストール

R から下記コードを実行し、RForcecom パッケージをインストール、ロードします。

# RForcecom パッケージのインストール・ロード
install.packages("RForcecom")
library(RForcecom)

Salesforce.com へのログイン

下記コードを実行し、Salesforce.com にログインします。パスワードの後ろにセキュリティトークンを忘れずに付与してください。

# ユーザ名
username <- "<YOUR_EMAIL_ADDRESS>"
# パスワード+セキュリティトークン
password <- "<PASSWORD><SECURITY_TOKEN>"
# インスタンスのホスト名 (ap0, ap1, na14 等)
instanceURL <- "https://<YOUR_SFDC_INSTANCE>.salesforce.com/"
# API のバージョン (2013/12 時点の最新版は 29.0)
apiVersion <- "29.0"
# ログインし、セッション ID を取得
session <- rforcecom.login(username, password, instanceURL, apiVersion)

データセットの取得

rforcecom.retrieve 関数にて、Salesforce.com のデータをロードします。

# CustomerVoice__c オブジェクトの "Tweet__c", "TweetDate__c" フィールドを取得
CustomerVoice <- rforcecom.retrieve(session,"CustomerVoice__c",c("Tweet__c","TweetDate__c"))

head 関数にて取得出来たことを確認します。

# 取得したデータの先頭 6 行を表示
head(CustomerVoice$Tweet__c)

f:id:hiratake55:20131221133350p:plain Salesforce.com より取得したデータ

データサイズによっては数分かかる場合があります。データサイズが大きすぎる場合は、抽出期間を設定してデータを絞り込むような工夫が必要かと思われます。

3. MeCab を利用して日本語形態素解析を行う

MeCab は工藤拓氏によって開発された日本語形態素解析を行うためのソフトウェアです。本例では、MeCab を用いて、文章から単語への品詞分解を行います。

MeCab のインストール

MeCab のサイトから、ページ中ほどの mecab-0.996.exe をダウンロード、インストールします。

f:id:hiratake55:20131220011513p:plain MeCab ダウンロードページ

RMeCab パッケージのインストール

続いて R から MeCab を実行する RMeCab パッケージ をインストールします。

# RMeCab パッケージのインストール・ロード
install.packages ("RMeCab", repos = "http://rmecab.jp/R")
library(RMeCab)

取得したデータセットから Twitter ID と URLを除去

前手順にて Salesforce.com から取得したデータセットには本文中に Twitter ID や URL が含まれていますが、本分析では不要ですので gsub 関数を利用して正規表現で除去します。

# Twitter ID を除去
CustomerVoice$Tweet__c <- gsub("@[0-9a-zA-Z_]+\\s*","",CustomerVoice$Tweet__c)
# 「RT」の文言を除去
CustomerVoice$Tweet__c <- gsub("RT\\s*:\\s+","",CustomerVoice$Tweet__c)
# 本文中の URL を除去
CustomerVoice$Tweet__c <- gsub("https?://t.co/[0-9a-zA-Z\\._]*","",CustomerVoice$Tweet__c)

データセットを一時ディレクトリに書き出し

RMeCab を利用するにはには、分析用のデータセットを一旦、一時ディレクトリに書き出す必要があります。

# 一時ファイル名を取得
filename <- tempfile("CustomerVoice")
# ファイルに書き出し
write.table(CustomerVoice$Tweet__c,
            file=filename,
            row.names=F,
            col.names=F,
            fileEncoding="shift-jis",
            quote=F)

形態素解析と頻出語の抽出

RMeCabFreq 関数を用いて品詞分解を行い、その出現頻度をカウントします。本例では、名詞に限定して品詞抽出を行いました。

# 品詞分解と単語の出現頻度をカウント
freq <- RMeCabFreq(filename)
# 名詞のみを抽出
freq <- freq[freq$Info1=="名詞",]

抽出結果は以下のようになりました。

# 抽出結果の先頭 12 行を表示
head(freq[order(freq$Freq, decreasing=T),], n=12)

f:id:hiratake55:20131221133351p:plain 抽出結果のイメージ

この状態では、記号や数字など無意味な単語が並んでおり分析に適さないため、データクレンジングを行い不要な単語を除去し意味のある単語のみを残します。

# 数名詞、非自立名詞、接尾名詞を除去
freq <- freq[!freq$Info2 %in% c("数","非自立","接尾"),]
# 2 文字以上の単語のみを抽出
freq <- freq[nchar(freq$Term)>2,]
# 異なる品詞、同じ単語を整理
freq.term<-unique(freq$Term)
freq.sum <- lapply(freq.term,function(x){sum(freq[freq$Term==x,]$Freq)})
# データフレームに格納
freq <- data.frame(Term=freq.term,Freq=unlist(freq.sum))

4. ワードクラウドとして特徴語を可視化する

wordcloud パッケージのインストール

ワードクラウドを出力する wordcloud パッケージ をインストール、ロードします。

# wordcloud パッケージのインストール・ロード
install.packages("wordcloud")
library(wordcloud)

ワードクラウドの出力

# フォントリストに Meiryo UI を追加
windowsFonts(MeiryoUI = "Meiryo UI")
# 頻度の高い順に並び替え
freq <- freq[order(freq$Freq, decreasing=T),]
# 頻度の上位 100 件を抽出
freq.head<-head(freq, n=100)
# カラースケールを Dark2 に設定
pal <- brewer.pal(8, "Dark2")
# wordcount 関数でプロット
wordcloud(freq.head$Term, freq.head$Freq, random.color=T, colors=pal, family="MeiryoUI")

Wordcloud の出力結果

以下のように頻出語が大きな文字で出力され一目で頻出語が把握できます、なお文字色はランダムで選択しているため意味は持ちません。

f:id:hiratake55:20131221133352p:plain

5. TF-IDF 法による特徴語抽出

TF-IDF 法とは文書分類や特徴語の抽出に用いられるアルゴリズムで、文書中の単語 i の出現頻度と単語 i を含む文書数の関係から、特定の文書に高い頻度で出現する単語を抽出します。TF-IDF の計算方法は以下サイトに詳しく書かれています。

tf–idf - Wikipedia, the free encyclopedia

本例では、2013-12-10 ~ 2013-12-16 の 1 週間分の Tweet ログを全ての文書、各日の Tweet ログを対象の文書として、各日に頻出したキーワードを抽出します。

各日のデータセットの取得

RForcecom パッケージの rforcecom.query 関数を利用して、SOQL クエリを実行しデータを取得します。

# 12/10 - 12/16 の各日のデータセットを取得
CustomerVoice.1210 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-10T00:00:00+09:00 and TweetDate__c < 2013-12-11T00:00:00+09:00")
CustomerVoice.1211 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-11T00:00:00+09:00 and TweetDate__c < 2013-12-12T00:00:00+09:00")
CustomerVoice.1212 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-12T00:00:00+09:00 and TweetDate__c < 2013-12-13T00:00:00+09:00")
CustomerVoice.1213 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-13T00:00:00+09:00 and TweetDate__c < 2013-12-14T00:00:00+09:00")
CustomerVoice.1214 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-14T00:00:00+09:00 and TweetDate__c < 2013-12-15T00:00:00+09:00")
CustomerVoice.1215 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-15T00:00:00+09:00 and TweetDate__c < 2013-12-16T00:00:00+09:00")
CustomerVoice.1216 <- rforcecom.query(session,"select Tweet__c, TweetDate__c from CustomerVoice__c where TweetDate__c > 2013-12-16T00:00:00+09:00 and TweetDate__c < 2013-12-17T00:00:00+09:00")
# 7 日分のデータセットを結合したデータセットを作成
CustomerVoice.all <- rbind(CustomerVoice.1210,
                           CustomerVoice.1211,
                           CustomerVoice.1212,
                           CustomerVoice.1213,
                           CustomerVoice.1214,
                           CustomerVoice.1215,
                           CustomerVoice.1216)

単語の品詞分解と出現頻度のカウント

# 単語を品詞分解し、出現頻度を出力する関数
calc.wordfreq <- function(CustomerVoice){
  # Twitter ID を除去
  CustomerVoice$Tweet__c <- gsub("@[0-9a-zA-Z_]+\\s*","",CustomerVoice$Tweet__c)
  # 「RT」の文言を除去
  CustomerVoice$Tweet__c <- gsub("RT\\s*:\\s+","",CustomerVoice$Tweet__c)
  # 本文中の URL を除去
  CustomerVoice$Tweet__c <- gsub("https?://t.co/[0-9a-zA-Z\\._]*","",CustomerVoice$Tweet__c)
  # 一時ファイル名を取得
  filename <- tempfile("CustomerVoice")
  # ファイルに書き出し
  write.table(CustomerVoice$Tweet__c,
              file=filename,
              row.names=F,
              col.names=F,
              fileEncoding="shift-jis",
              quote=F)
  # 品詞分解と単語の出現頻度をカウント
  freq <- RMeCabFreq(filename)
  # 名詞のみを抽出
  freq <- freq[freq$Info1=="名詞",]
  # 数名詞、非自立名詞、接尾名詞を除去
  freq <- freq[!freq$Info2 %in% c("数","非自立","接尾"),]
  # 2 文字以上の単語のみを抽出
  freq <- freq[nchar(freq$Term)>2,]
  # 異なる品詞、同じ単語を整理
  freq.term<-unique(freq$Term)
  freq.sum <- lapply(freq.term,function(x){sum(freq[freq$Term==x,]$Freq)})  
  # データフレームに格納
 freq <- data.frame(Term=freq.term,Freq=unlist(freq.sum),stringsAsFactors=F)
  return(freq)
}
# 各日のデータフレームに対して単語の出現頻度を算出
freq.1210 <- calc.wordfreq(CustomerVoice.1210)
freq.1211 <- calc.wordfreq(CustomerVoice.1211)
freq.1212 <- calc.wordfreq(CustomerVoice.1212)
freq.1213 <- calc.wordfreq(CustomerVoice.1213)
freq.1214 <- calc.wordfreq(CustomerVoice.1214)
freq.1215 <- calc.wordfreq(CustomerVoice.1215)
freq.1216 <- calc.wordfreq(CustomerVoice.1216)
freq.all <- calc.wordfreq(CustomerVoice.all)

IDF 値を求める

# 各単語が出現する文書の数をカウント
IDF.documents <- sapply(freq.all$Term,function(x){
  sum(
    nrow(freq.1210[freq.1210$Term==x,])>0,
    nrow(freq.1211[freq.1211$Term==x,])>0,
    nrow(freq.1212[freq.1212$Term==x,])>0,
    nrow(freq.1213[freq.1213$Term==x,])>0,
    nrow(freq.1214[freq.1214$Term==x,])>0,
    nrow(freq.1215[freq.1215$Term==x,])>0,
    nrow(freq.1216[freq.1216$Term==x,])>0
  )
})
# データフレームに格納する
IDF<-data.frame(Term=freq.all$Term,IDF=log(7/IDF.documents))

TF-IDF 値を求める

# TF 値を求め、TF-IDF 値を計算して返す関数を作成
calc.tfidf <- function(freq){
  # TF 値を求める
  freq$TF <- freq$Freq/sum(freq$Freq)
  # TF-IDF 値を求める
  tfidf.val <- lapply(freq$Term,function(x){
    tf_i <- freq[freq$Term==x,]$TF
    idf_i <- IDF[IDF$Term==x,]$IDF
    return(tf_i * idf_i)
  })
  # 結果をデータフレームに格納する
  tdidf <- data.frame(Term=freq$Term, TFIDF=unlist(tfidf.val))
  return(tdidf)
}
# 各日ごとの TF-IDF 値を求める
tfidf.1210 <- calc.tfidf(freq.1210)
tfidf.1211 <- calc.tfidf(freq.1211)
tfidf.1212 <- calc.tfidf(freq.1212)
tfidf.1213 <- calc.tfidf(freq.1213)
tfidf.1214 <- calc.tfidf(freq.1214)
tfidf.1215 <- calc.tfidf(freq.1215)
tfidf.1216 <- calc.tfidf(freq.1216)

ワードクラウドを出力する

各日のワードクラウドを出力し、PNG 形式で保存します。

# フォントリストに Meiryo UI を追加
windowsFonts(MeiryoUI="Meiryo UI")
# Wordcloud を出力する関数を作成
draw.wordcloud <- function(tfidf,title=""){
  # ファイル名、出力サイズの指定
  png.filename <- paste("wordcloud-", title,".png", sep="")
  png(png.filename,width=7,height=7,units="in", res=600)
  # データセットの上位 100 件を抽出
  tfidf <- tfidf[order(tfidf$TFIDF, decreasing=T),]
  tfidf.head <- head(tfidf, n=200)
  # 上部の余白を設定
  par(oma = c(0, 1, 2, 1))
  # Wordcloud を出力
  wordcloud(tfidf.head$Term,tfidf.head$TFIDF,random.color=T,colors=pal,family="MeiryoUI",main=title)
  # 上部の余白を元に戻す
  par(oma = c(0, 0, 0, 0))
  # タイトルを付与
  title(title)
  # ファイルに保存
  dev.off()
}
# 各日の Wordcloud を描画
draw.wordcloud(tfidf.1210,title="2013-12-10")
draw.wordcloud(tfidf.1211,title="2013-12-11")
draw.wordcloud(tfidf.1212,title="2013-12-12")
draw.wordcloud(tfidf.1213,title="2013-12-13")
draw.wordcloud(tfidf.1214,title="2013-12-14")
draw.wordcloud(tfidf.1215,title="2013-12-15")
draw.wordcloud(tfidf.1216,title="2013-12-16")

ワードクラウド出力結果

ワードクラウドの出力結果は以下で、図からいくつかの各日ごとに特徴的な傾向があることが推測されます。

f:id:hiratake55:20131220121851p:plain:w480 12/10(火) は、「待ち時間」「一括払い」などのショップ窓口に関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121852p:plain:w480 12/11(水) は、「フレッツ」「スティック(データ通信端末?)」などの固定インターネット、データ通信に関する相談が多かったことが推測されます

f:id:hiratake55:20131220121853p:plain:w480 12/12(木)は、「トンネル」、「ネットワーク」などの回線品質、通話エリアに関する相談が多かったことが推測されます

f:id:hiratake55:20131220121854p:plain:w480 12/13(金)は、「お子さま」など、キッズ向けケータイに関する相談が多かったことが推測されます

f:id:hiratake55:20131220121855p:plain:w480 12/14(土)は、「三ノ宮」、「出来島」、「新鎌ヶ谷」など特定のエリアまたはショップに関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121856p:plain:w480 12/15(日)は、「スリープ」、「Wifi」など、スマートフォンの利用方法に関する相談が多かったことが推測されます。

f:id:hiratake55:20131220121857p:plain:w480 12/16(月)は、「セールス」、「直営店」、「値引き」など対面での応対に関する相談が多かったことが推測されます。

まとめ

本分析で行ったこと

本記事では、R 言語を利用して Salesforce.com に格納されたデータを MeCab により品詞分解し、TF-IDF 法にて特徴語を抽出しました。その結果をワードクラウドとして可視化したところ、日毎に異なる結果が得られました。

今回は 1 週間分の Tweet ログをもとに各日の特徴語を抽出しましたが、数か月以上長期間のデータを収集し週毎、曜日毎の分割により今回とは異なった角度から傾向や更に深い内容の示唆が得られる可能性があります。

辞書 (コーパス) について

今回は MeCab バンドルの辞書を使って形態素解析を行いましたが、独自に辞書を作成し、用語やサービス名を登録しておくことでより高精度な分析、有用な情報が得られると考えます。

(「フィーチャーフォン、ガラケー」「アップル、Apple」を同じ言葉としてカウントする、「"パケットし放題 for 4G", "パケットし放題フラット for 4G"等」をそれぞれ別々の 1 つの言葉として扱うなど)

本分析の応用例

応用例としては、以下の様な分析シナリオが考えられます。

  • センチメント分析(感情分析)などによるクレーム顧客の自動判定
  • 新商品、新サービスに関するお客様からのフィードバックのサマリレポート作成
  • 不良品、リコール対象となりうる可能性のある製品の早期発見
  • お客様からの返信内容の分析による担当者、サービスの対応品質の評価

さいごに

※この記事は Force.com advent Calendar 2013 の 12/21 分の記事です。

※今回利用した @SBCare 宛の Twitter の発言履歴は著作権保護の観点から分析後、Salesforce.com からデータを削除しております。