インテージテクノスフィア技術ブログ

株式会社インテージテクノスフィアの社員達がシステム開発や仕事に関する技術情報を随時掲載いたします

【Tableau】コレスポンデンス分析をしたい - ワークシート編 -

こんにちは、アイダです。みなさんの今年の漢字はなんですか?私は「梨」です。これまでは皮をむくのが手間だったので滅多に食べていなかったのですが、皮むき器と8等分カッターを購入したら、苦労なく食べられることに気づきました。旬が過ぎたのでいまはりんご生活です。

さて、今回は Japan Tableau User Group(JTUG)の冬の総会で、DX共創センターの K さんとグループビジネス推進ユニットの T さんが紹介した内容の詳細解説、後半戦です。残り半分いっちゃってください!

はじめに

前回の記事「環境設定編」では、TabPy の設定、TabPy を使うための設定についてご紹介しました。

www.intage-ts.com

今回は TabPy 連携を用いて、どのようにコレスポンデンス分析をワークシートで実現するかについて紹介していきます。 Tableau の計算フィールドや Python スクリプトも登場しますが、がんばってついてきてください!

Tableau - TabPy のやりとり

計算フィールドに Python スクリプトを入力することで、TabPy での処理内容を定義します。 Tableau の計算フィールドには SCRIPT 関数というものがあり、返したい値によって以下を使い分けます。

  • SCRIPT_BOOL:論理値
  • SCRIPT_INT:整数値
  • SCRIPT_REAL:実数値
  • SCRIPT_STR:文字列

1回のやりとりでバシッと結果を取得したいところですが、 スコアは実数値、表示用のラベルは文字列なので、TabPy サーバーとのやりとりが複数回発生するのは避けることができなそうです。 f:id:intage-tech:20211209190755p:plain

Tableau のデータソースにはアンケート回答データを読み込ませていますが、 TabPy へ渡すデータは以下のようなクロス集計です。

データ:株式会社インテージが保有する自動車に関する調査「Car-kit®」 f:id:intage-tech:20211209190830p:plain

計算フィールド解説

計算フィールドとしては以下の4つを作成します。

  • 第1軸スコア
  • 第2軸スコア
  • ラベル
  • 表側・表頭判定

第1軸スコア

計算フィールドの中身はこちらです。

SCRIPT_REAL("
import mca
import pandas as pd
# data
input_values = _arg1
# ans(image,etc...)
input_rows = _arg2
# loop(brand,etc...)
input_cols = _arg3
 
# 3項目ごとになっているため、転置してレコードごとに変換
data = [list(x) for x in zip(*[input_values,input_rows,input_cols])]
pdata = pd.DataFrame(data)
#各列の名称を定義input_valuesは数値
pdata.columns = ['input_values', 'input_rows', 'input_cols']
# 行にブランド、列にブランドイメーシでクロス集計(値は集計済みのため並び替え)
pdata = pd.pivot_table(pdata, values='input_values', index='input_rows', columns='input_cols', aggfunc='sum')
# null(NaN)など無効値を0に変換
pdata = pdata.fillna(0)
 
# コレスポンデンス集計
mca_counts = mca.MCA(pdata, benzecri=False, TOL=1e-8)
out_rows = mca_counts.fs_r(N=2)# 表側データ
out_cols = mca_counts.fs_c(N=2)# 表頭データ
 
# 第1軸スコアを取得
xrows = out_rows[:, 0]
xcols = out_cols[:, 0]
out_list = (xrows).tolist()+(xcols).tolist()
return out_list+[None]*(len(input_values)-len(out_list))
",[回答数],ATTR([イメージ]),ATTR([ブランド]))

すこし詳しく見ていきましょう。 まず、Python スクリプトの引数として、クロス集計の回答数、表側ラベル、表頭ラベルに該当するものを渡します。 f:id:intage-tech:20211209191303p:plain

第1引数の "回答数" は1次元配列の形で渡されるので、クロス集計の形に変換します。 f:id:intage-tech:20211209191325p:plain

MCAライブラリを使ってコレスポンデンス分析を実行。 f:id:intage-tech:20211209191353p:plain

実行結果から、第1軸のスコアを返します。 このとき、表側と表頭のスコアを連結して1つの配列としてから返します。 f:id:intage-tech:20211209191422p:plain

第2軸スコア

計算フィールドの中身は第1軸スコアのものとほとんど同じです。 異なるのは返す値を取得する部分だけです。

# Y座標の取得
yrows = out_rows[:, 1]
ycols = out_cols[:, 1]
out_list = (yrows).tolist()+(ycols).tolist()
return out_list+[None]*(len(input_values)-len(out_list))

ラベル

ラベル表示用の文字列を取得したいだけではありますが、 スコアの配列と揃えるために、クロス集計の形に変換したデータフレームからラベルを取得しています。

SCRIPT_STR("
import mca
import pandas as pd
input_values = _arg1
input_rows = _arg2
input_cols = _arg3
#3項目ごとになっているため、転置してレコードごとに変換
data = [list(x) for x in zip(*[input_values,input_rows,input_cols])]
pdata = pd.DataFrame(data)
#各列の名称を定義input_valuesは数値
pdata.columns = ['input_values', 'input_rows', 'input_cols']
#行にブランド、列にブランドイメーシでクロス集計(値は集計済みのため並び替え)
pdata = pd.pivot_table(pdata, values='input_values', index='input_rows', columns='input_cols', aggfunc='sum')
#null(NaN)など無効値を0に変換
pdata = pdata.fillna(0)
labelsIdx = pdata.index.values.tolist()
labelsCol = pdata.columns.values.tolist()
out_list = labelsIdx+labelsCol
return out_list+[None]*(len(input_values)-len(out_list))
",[回答数],ATTR([イメージ]),ATTR([ブランド]))

表側・表頭判定

計算フィールド:ラベルの値によって、"イメージ" なのか "ブランド" なのかを判定します。 CASE 文でも IF 文でもいいでしょう。

ワークシート作成解説

計算フィールドが用意できたら、ワークシートを作成します。

ここでひとつ大事な Tips を。 SCRIPT 関数は "表計算" 扱いです。今回は "表計算" の方向をひとつずつ指定しなければならず、かつそのあいだはエラーが発生し続けます。 これを回避するために、[自動更新の一時停止] を設定しておきましょう。

では、いきます。

まずは集計単位となる [イメージ] と [ブランド] を 詳細 にセットします。

次に に [第1軸スコア] を、に [第2軸スコア] を、ラベルに [ラベル] をセットします。 表計算の方向はすべて、特定のディメンション [イメージ] [ブランド] とします。 f:id:intage-tech:20211209191724p:plain

ここまでできたら、[自動更新の再開] を設定します。 最後に表側ラベルと表頭ラベルを識別するために に [表側・表頭判定] をセットします。 f:id:intage-tech:20211209191809p:plain

あとは細かい部分を整えていけば完成です。 f:id:intage-tech:20211209191843g:plain

おわりに

いかがでしたでしょうか。 環境設定や Python スクリプトも必要になるので、"お手軽" とまではいきませんが、 Tableau で自由度の高いコレスポンデンス分析を実現することができました。

しかし、TabPy サーバーで同じ分析を複数回実行せざるをえないのが悩ましいです。 なにかいい解決策があれば教えてください。

この内容は JTUG 冬の総会 2021 でご紹介したものです。 YouTube に動画もありますのでぜひご覧ください。