Up

チョコレートの選好比率

エントロピーによる比較

2025.11CmdStanPy対応に改訂

 

 

StanPythonインターフェースCmdStanPy対応に改訂した。CmdStanPyの簡単な説明を、このウェブサイトに用意した。

 

3種類のチョコレートの嗜好を、ノーズクリップの脱着によりレトロネーザルアロマの影響をコントロールして調べたデータが星野ら(2020に報告されている。3種のチョコレート、ガーナ、ベネズエラ、エクアドルからの選択を、ノーズクリップなしの条件とノーズクリップあり(レトロネーザルアロマの関与の除外)の2条件で行われた結果が星野ら(2020Table 4に掲載されているが、Table 4から各条件ごとのチョコレートの選択頻度を抜き出して表1にまとめた。各条件におけるチョコレートの選択頻度は、多項分布に従うと見なすことができる。

 

表1 チョコレートの2条件における選択(星野ら(2020)より)

 

ガーナ

ベネズエラ

エクアドル

ノーズクリップなし

55

24

16

ノーズクリップあり

38

27

30

 

 多項分布は、2項分布の拡張と考えることができる。3種のチョコレートから1つを選ぶという実験において、それぞれのチョコレートが選ばれた頻度をとおく。それぞれのチョコレートが選ばれる確率をとおけば、多項分布は次式で表される。

 

 

ここで、

 

 

である。

式(2)の条件を満たすパラメータは、事前分布としてDirichlet分布を用いると得ることができる。Dirichlet分布は3変量の場合、次式で表される。

 

 

ここで、

 

 

である。

パラメータの値を

 

 

と置けば、確率分布(3)は一様分布を表す。

 表1のデータにおいて、「ノーズクリップなし」条件と「ノーズクリップあり」条件の2条件における多項分布のそれぞれの3つの確率の比較をエントロピーで行う。

エントロピーは、次式

 

 

で与えられ、3つの確率の値が等しいとき最大値をとり、どれか1つに確率が集まっている(すなわち、どれかの確率が1である)とき最小値をとる。

 

 

上の確率モデルのベイズ分析を行うStanスクリプトをリスト1のように用意した。StanPyStan2)については、岡本(2019で説明している。

 

 

リスト1 多項分布によるベイズ分析のStanスクリプト(choco_choice.stan

 

 

data {

    array[3] int freq_1;

    array[3] int freq_2;

}

transformed data {

        vector[3] alpha;

        alpha[1] = 1; alpha[2] = 1; alpha[3] = 1;

}

parameters {

    simplex[3] p1;

    simplex[3] p2;

}

model {

    p1 ~ dirichlet(alpha);

    p2 ~ dirichlet(alpha);

    freq_1 ~ multinomial(p1);

    freq_2 ~ multinomial(p2);

}

 

 

 

リスト1のStanスクリプトを用いて表1のデータを分析するPythonスクリプトを、リスト2のように用意した。スクリプトファイルは圧縮ファイルfiles_freq.zipにまとめたので、ダウンロード・展開すれば自由に利用できる。

 

 

リスト2 表1のデータのベイズ分析スクリプト(AnalFreq.py

 

 

from cmdstanpy import CmdStanModel

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sb

import arviz as az

import japanize_matplotlib

 

model = CmdStanModel(stan_file='choco_choice.stan')

 

freq_noclip = [55, 24, 16]

freq_clip = [38, 27, 30]

 

Data = {'freq_1': freq_noclip, 'freq_2':freq_clip}

 

fit = model.sample(data=Data)  

 

print(fit.summary())

 

inf_data = az.from_cmdstanpy(fit)      #   Arviz DataFrame型に変換

az.plot_trace(inf_data) 

plt.tight_layout()

plt.show()

 

fit = fit.draws_pd()                   #   Pandas DataFrame型に変換

 

p1_smpls = []

p2_smpls = []

for i in range(3):

    p1_smpls.append(fit[f'p1[{i+1}]'])

    p2_smpls.append(fit[f'p2[{i+1}]'])

p1_smpls = np.array(p1_smpls).T

p2_smpls = np.array(p2_smpls).T

 

 

def calc_entropy(p):

    return -p.T[0]*np.log2(p.T[0]) -p.T[1]*np.log2(p.T[1]) \

            -p.T[2]*np.log2(p.T[2])

 

entropies1 = calc_entropy(p1_smpls)

entropies2 = calc_entropy(p2_smpls)

 

ent1_hat = np.median(entropies1)

ent2_hat = np.median(entropies2)

 

prob_e2_e1 = np.mean(entropies2 - entropies1 > 0)

 

sb.kdeplot(entropies1, label = 'No-clip')

sb.kdeplot(entropies2, label = 'Clip')

plt.title(('Entropy: No-clip={0:.2f}, Clip={1:.2f}\n' +

           'Prob(Clip > No-clip)={2:.3f}').

          format(ent1_hat, ent2_hat, prob_e2_e1))

plt.legend()

plt.savefig('FigEntropies.png')

plt.show()

 

p1_1_hat = np.median(p1_smpls.T[0]) 

p1_2_hat = np.median(p1_smpls.T[1])

p1_3_hat = np.median(p1_smpls.T[2]) 

sb.kdeplot(p1_smpls.T[0], label = 'p1[1]:ガーナ')

sb.kdeplot(p1_smpls.T[1], label = 'p1[2]:ベネズエラ')

sb.kdeplot(p1_smpls.T[2], label = 'p1[3]:エクアドル')

plt.legend()

plt.title('No-clip\nガーナ={0:.2f},  ベネズエラ={1:.2f},  エクアドル={2:.2f}'.format(

            p1_1_hat, p1_2_hat, p1_3_hat))

plt.savefig('Figp1.png')

plt.show()

 

p2_1_hat = np.median(p2_smpls.T[0]) 

p2_2_hat = np.median(p2_smpls.T[1]) 

p2_3_hat = np.median(p2_smpls.T[2])

sb.kdeplot(p2_smpls.T[0], label = 'p2[1]:ガーナ')

sb.kdeplot(p2_smpls.T[1], label = 'p2[2]:ベネズエラ')

sb.kdeplot(p2_smpls.T[2], label = 'p2[3]:エクアドル')

plt.legend()

plt.title('Clip\nガーナ={0:.2f},  ベネズエラ={1:.2f},  エクアドル={2:.2f}'.format(

            p2_1_hat, p2_2_hat, p2_3_hat))

plt.savefig('Figp2.png')

plt.show()

 

 

リスト2において、グラフにおいて日本語表記するためにjapanize_matplotlibimportしていることに注意。

 

リスト2のPythonスクリプトファイル(AnalFreq.py)とリスト1のStanスクリプトファイル(choco_choice.stan)を同じフォルダに置く。そのフォルダにカレントディレクトリ(フォルダ)を移して、CmdStanPyのインストールされている環境で、次のコマンドを実行する。CmdStanPyの簡単な説明を、このウェブサイトに用意した。

 

(stan) *****/files_freq$ python AnalFreq.py

 

上のコマンドを実行すると、Stanスクリプトのコンパイルが始まる。このコンパイルには、多少時間が掛かる。コンパイル後、MCMCサンプリングが始まり、終了後、トレース図が表示される(図1)。

 

図1

 

左に事後分布のグラフ、右にサンプリングのトレース図が表示されている。

図1のWindowを閉じると、図2のグラフが表示される。

 

図2

 

ノーズクリップなし(No-clip)とノーズクリップあり(Clip)のそれぞれの条件におけるエントロピーの事後分布のグラフである。ノーズクリップありのエントロピーの方がノーズクリップなしのエントロピーより大きい事後確率が0.992であることが示されている。ノーズクリップあり条件のエントロピーの方が大きいので、ノーズクリップあり条件での3つのチョコレートの選好確率の違いは、ノーズクリップなし条件の場合より顕著ではないと言える。

図2のWindowを閉じると、図3のグラフが表示される。

 

図3

 

「ノーズクリップなし」条件での3つのチョコレートの選好確率の事後分布のグラフが表示されている。ガーナが他の2つより好まれていることが明瞭に示されている。

図3のWindowを閉じると、図4のWindowが表示される。

 

図4

 

「ノーズクリップあり」条件での3つのチョコレートの選好確率の事後分布のグラフが示されている。図3の分布と比べると、3つのチョコレートの選好確率の違いは不明瞭になっている。このことは、図2のエントロピーの事後分布の違いに対応するものである。

 

図4のWindowを閉じると、スクリプトの実行終了である。

 

 

 

引用文献

星野亜由美、石川華衣、岡田綾、飯田文子(2020).カカオ豆の香がチョコレート嗜好に及ぼす影響.日本官能評価学会誌、2429-42

 

 

Up