はじめに
こんにちは…!
この記事では、自分なりの「腸活スコア」を組み立てて料理と食材のデータセットを用いて、
実際にスコアを算出しその結果を可視化するまでの過程をまとめました。
※ 本記事で紹介するスコアはあくまで独自に定義したもので、科学的に確立された評価基準ではありません。データ分析の題材&アイデアの可視化として楽しんでいただければと思います。
解決したい社会問題
近年、腸内環境と健康の関係が広く注目されており、「腸活」という言葉も一般的になってきました。
しかし実際に、
「腸に良い食事とはどんなものか」
「自分が日常的にどれくらい腸活できているのか」
といった点は分かりにくく、日常生活に取り入れるのが難しいと感じています。
私自身、腸内細菌検査に関わる仕事をする中で、こうした「見えにくさ」が腸活を習慣として続ける上で大きな課題だと実感しました。
そこで、料理や食材から腸活への良し悪しをスコア化し、誰でも直感的に理解できる仕組みを作ることで、日々の健康意識を高めるきっかけになればと考え、取り組んでみることにしました。
分析するデータ
今回の腸活スコア算出には、以下の公開されている料理データセットを利用しました。
料理の栄養価一覧 - 料理の栄養価一覧 - CKAN
また、腸活スコアで利用するプレバイオティクス/プロバイオティクスの食材データは厳密に定められているわけではないため、 今回は「代表例」として論文や記事でよく出てくる食材をExcelでまとめました。
プレバイオティクスのExcelファイルはこちらからダウンロード可能です:
GitHub:プロバイオティクスデータ
プロバイオティクスのExcelファイルはこちらからダウンロード可能です:
GitHub:プレバイオティクスデータ
実行環境
【パソコン】Surface Pro 8
【開発環境】Google Coraboratory
【言語】Python
【ライブラリ】
- データ操作・解析
pandas, numpy - 可視化
matplotlib.pyplot, seaborn, japanize_matplotlib(日本語対応) - 文字列処理・テキスト整形:re, jaconv
Google Colab用ユーティリティ:google.colab.files, io
腸活スコアの設計と計算方法
今回は以下の3つの項目に注目し、腸活スコアを考えました。
- プレバイオティクス/プロバイオティクス
- 野菜量
- 食材多様性
①プレバイオティクス/プロバイオティクス
プレバイオティクスとは、腸内の善玉菌を育てる「エサ」となる成分のことです。
明治:プレバイオティクスとは?健康効果や食品例、効果的なとり方を解説
代表的なものには食物繊維やオリゴ糖があり、これらを多く含む食材を摂取すると腸内環境の改善に役立つとされています。
今回のスコア設計では、料理の食材の中にひとつでもプレバイオティクスを含むものがあれば加点することにしました。
また、似たような言葉に、プロバイオティクスというものがあります。
プロバイオティクスは生きた菌そのものを指し、腸内環境に良い影響を与えるとされています。
代表例は乳酸菌やビフィズス菌で、ヨーグルトや納豆、キムチなどの発酵食品です。
こちらも今回のスコア設計では、料理の食材の中にひとつでもプロバイオティクスを含むものがあれば加点することにしました。
②野菜量
食物繊維を多く摂取すると、腸内の善玉菌が増えるといわれています。
本来であれば「食物繊維摂取量」を指標にしたいところですが、今回のデータセットにはその項目がありません。
そこで代わりに「野菜量」の項目を利用し、食物繊維摂取の目安としました。
厚生労働省では、1日あたりの野菜摂取量の目標を350グラム以上としています(厚生労働省)。
この目標値を基準として、各料理で摂取できる野菜量が全体の何%に相当するかを計算し、点数化しました。
$\text{野菜量スコア} = \dfrac{\text{料理に含まれる野菜量}}{350} \times 40$
③食材多様性指数(シャノンウィナー指数)
腸内細菌は多様性が高いほど腸内環境が良好であるといわれています。
そこで、食材もできるだけ多様に摂取することで腸内細菌の多様性が保たれるのではないかと考え、それぞれの料理に使われる食材の多様性を出すことにしました。
シャノンウィナー指数は単純な「種類の数」だけではなく、それぞれのバランスも反映する指標です。種類が多くそれぞれの割合が均等に近いほど値が大きくなり、逆に、特定の種類に偏っている場合は小さな値になります。
今回は料理ごとの食材の多様性 $H$ を計算し、得られたシャノンウィナー指数の最大値 $H_{\max}$ を基準に正規化を行い、30点満点で「食材多様性スコア」を算出します。
$\text{食材多様性スコア} = \dfrac{H}{H_{\max}} \times 30$
スコアの配点
これらの着目した3つの項目について、合計100点となるよう配点をしました。
最小設計として今回は各項目を等分に配点することを考えましたが、腸活への影響が大きいと考えられる「野菜量」を40点としました。
プレバイオティクスとプロバイオティクスは、どちらか片方だけでなく両方を組み合わせて摂取することが望ましいため、合わせて30点(それぞれ15点ずつ)としました。
No. | 項目 | 配点 | 計算方法 |
---|---|---|---|
1 | プレバイオティクス:15点 プロバイオティクス:15点 |
30 | 1 (食材が含まれている)= + 15点 0 (食材が含まれていない)= + 0点 |
2 | 野菜量 | 40 | $\text{野菜量スコア} = \dfrac{\text{料理に含まれる野菜量}}{350} \times 40$ |
3 | 食材多様性(シャノンウィナー指数) | 30 | $\text{食材多様性スコア} = \dfrac{H}{H_{\max}} \times 30$ |
合計 | 100 |
分析の流れ
では、実際の分析に進みます。
分析は以下の順で行います。各項目の詳細については後述します。
1. データの読み込み
- 料理と食材栄養素のデータ
- プレバイオティクスデータ
- プロバイオティクスデータ
2. データの前処理
2-1. 食材数の算出
2-2. 空白の列・欠損値の処理
2-3. 食材名の正規化
2-4. プレバイオティクス/プロバイオティクスのラベル付け
3. 腸活スコアの算出
3-1. プレバイオティクス/プロバイオティクスの点数処理
3-2. 野菜量の点数処理
3-3. 食材多様性の点数処理
4. 結果の可視化・考察
分析の過程
1. データの読み込み
はじめに、分析で使用するライブラリをインポートします。
# ライブラリのインポート
pip install jaconv
pip install japanize-matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import japanize_matplotlib
from google.colab import files
import io
import jaconv
import re
1-2. データセットの読み込み
以下の3つのデータをそれぞれ、ダウンロードして準備しておいてください。
-
料理と食材栄養素のデータ
料理の栄養価一覧 - 料理の栄養価一覧 - CKAN -
プレバイオティクスデータ
GitHub:プロバイオティクスデータ -
プロバイオティクスデータ
GitHub:プレバイオティクスデータ
ダウンロードしたデータをファイル選択ダイヤログで読み込みます。
料理と食材栄養素のデータの読み込み
# 料理と食材栄養素のデータの読み込み
uploaded = files.upload() # ファイル選択ダイアログを出す
file_name = list(uploaded.keys())[0]
df_recipes_ori = pd.read_csv(io.BytesIO(uploaded[file_name])) # CSVを読み込み
df_recipes = df_recipes_ori.copy() # コピーを作成
# 列をすべて表示させる
pd.set_option('display.max_columns', None)
# 行をすべて表示させる
#pd.set_option('display.max_rows', None)
# データの確認
df_recipes.head()
プレバイオティクスデータの読み込み
# プレバイオティクスデータの読み込み
uploaded = files.upload() # ファイル選択ダイアログを出す
file_name = list(uploaded.keys())[0]
xls = pd.ExcelFile(io.BytesIO(uploaded[file_name]))
df_prebiotics_ori = pd.read_excel(xls)
df_prebiotics = df_prebiotics_ori.copy() # コピーを作成
# データの確認
df_prebiotics.head()
プロバイオティクスデータの読み込み
# プロバイオティクスデータの読み込み
uploaded = files.upload() # ファイル選択ダイアログを出す
file_name = list(uploaded.keys())[0]
xls = pd.ExcelFile(io.BytesIO(uploaded[file_name]))
df_probiotics_ori = pd.read_excel(xls)
df_probiotics = df_probiotics_ori.copy() # コピーを作成
# データの確認
df_probiotics.head()

2. データの前処理
次に、スコアを算出するためにデータを整えていきます。
2-1. 食材数の算出
ぱっと見では各レシピに何種類の食材数が使われているか分からないため、食材数の列を追加します。
# 各レシピに使用されている食材数を出す
df_recipes['食材数'] = df_recipes.iloc[:,8::2].notna().sum(axis=1)
2-2. 空白の列・欠損値の処理
読み込んだデータのままでは空白の列があるため削除します。
# 空白の列を削除する
df_recipes = df_recipes.dropna(axis=1, how="all")
次に、データ中の欠損値の数を確認し、その後の処理がしやすいよう " - " へ変換します。
数値の欠損は、このまま残しておいた方が扱いやすいため、ここでは処理をしていません。
# 欠損値の数の確認
df_recipes.isnull().sum().sum()

# 文字列型の欠損を " - "に変換する
object_cols = df_recipes.select_dtypes(['object']).columns
df_recipes[object_cols] = df_recipes[object_cols].fillna('-')
2-3. 食材名の正規化
食材名が、
「豚・ばら・脂身つき_生」
のように非常に細かく記載されているため、食材名は 最初の部分(左側の表記)だけ を使うことにします。
# 「・」「_」「()」以降の単語を削除する関数
def clean_foodname(name):
"""
IN:鶏卵・全卵_生
OUT:鶏卵
"""
# 「(~)」を削除する
name = re.sub("([^)]*)", "", name)
# 「・」「_」以降も削除する
name = re.sub("[・_].*", "", name)
return name
# clean_foodname関数の実行
for col in df_recipes.iloc[:,8:43:2].columns:
df_recipes[col] = df_recipes[col].map(clean_foodname)
# データの確認
df_recipes.head()
2-4. プレバイオティクス/プロバイオティクスのラベル付け
# プレバイオティクス一覧を参照し、当てはまればOne-Hot-Encordingする
pre_word = df_prebiotics.stack().tolist() # データフレームの列をリスト化する、「NaN」は除外する
# ラベル付け
def check_prebiotics(cell):
"""
それぞれのレシピにプレバイオティクス一覧の食材が含まれているかどうかを判定する
OUT:
含まれている→ 1
含まれていない → 0
※ 材料を個別に判断するのではなく、料理全体(1行の中を判断する)
"""
for x in pre_word:
if x in cell:
return 1
return 0
# プレバイオティクスのラベル付け関数の実行
df_recipes['プレバイオティクス(全体)'] = 0 # 0で初期化
cols_to_check = df_recipes.iloc[:,8:43:2].columns
for col in cols_to_check:
df_recipes['プレバイオティクス(全体)'] = df_recipes['プレバイオティクス(全体)'] | df_recipes[col].map(check_prebiotics)
# プロバイオティクス一覧を参照し、当てはまればOne-Hot-Encordingする
pro_word = df_probiotics.values.ravel().tolist() # データフレームの列をリスト化する、「NaN」は除外する
# プロバイオティクスのラベル付け
def check_probiotics(cell):
"""
それぞれのレシピにプロバイオティクス一覧の食材が含まれているかどうかを判定する
OUT:
含まれている→ 1
含まれていない → 0
※ 材料を個別に判断するのではなく、料理全体(1行の中)を判断する
"""
for x in pro_word:
if x in cell:
return 1
return 0
# プロバイオティクスのラベル付け関数の実行
df_recipes['プロバイオティクス(全体)'] = 0 # 0で初期化
cols_to_check = df_recipes.iloc[:,8:43:2].columns
for col in cols_to_check:
df_recipes['プロバイオティクス(全体)'] = df_recipes['プロバイオティクス(全体)'] | df_recipes[col].map(check_probiotics)
# データの確認
df_recipes.head()
最後の列にプレバイオティクス/プロバイオティクスのラベル付けが追加されました。
3. 腸活スコアの算出
次に、今回定義した腸活スコアを料理データセットに適用していき、各料理に点数をつけていきます。
3-1. プレバイオティクス/プロバイオティクスの点数処理
各料理の食材にプレバイオティクス/プロバイオティクスが含まれているのかを判定します。
# プレプロスコア関数
def prepro_score(row):
score = 0
if row['プレバイオティクス(全体)'] == 1: # プレバイオティクスが 1 (食材が含まれている)とき +15点 する
score += 15
if row['プロバイオティクス(全体)'] == 1: # プロバイオティクスが 1 (食材が含まれている)とき +15点 する
score += 15
return score
# 関数の実行
df_recipes['プレプロスコア'] = df_recipes.apply(prepro_score, axis=1)
3-2. 野菜量の点数処理
各料理が1日あたりの野菜目安(350g)の何%かを計算し、40点満点で評価します。
# 野菜量スコア関数
def veg_score(veg_g, daily_target=350, max_points=40):
frac = veg_g / daily_target
frac_clip = 1.0 if frac > 1.0 else frac
score = frac * max_points
return round(score, 1) # 小数点第1位で返す
# 関数の実行
df_recipes['野菜量スコア'] = df_recipes['野菜量[グラム]'].apply(veg_score)
3-3. 食材多様性の点数処理
各料理のシャノンウィナー多様性を計算し、30点満点で評価します。
# シャノンウィナー指数の関数
def shannon_wiener(row):
"""
それぞれのレシピの食材の多様性をシャノンウィナー指数で算出する
IN:食材のグラムデータが入っている列
OUT:各料理の食材のシャノンウィナー指数
"""
grams = row[gram_cols].dropna().astype(float) # 食材のグラムを抽出・NaNがあれば削除する、またfloat型に変換する
total = grams.sum() # 合計グラムの算出
if total == 0:
return 0
p = grams /total # その料理における各食材の割合を算出
return -np.sum(p * np.log(p)) # シャノンウィナーの計算式
# 各料理のグラムのリストを作成する
gram_cols = [colum for colum in df_recipes.iloc[:, 8:].columns if 'グラム' in colum]
# 関数の実行
df_recipes['食材シャノンウィナー指数'] = df_recipes.apply(shannon_wiener, axis=1)
# 算出したシャノンウィナー指数を30点満点で評価する
"""
IN:各料理の食材のシャノンウィナー指数
OUT:食材多様性の点数
"""
shannon_max = df_recipes['食材シャノンウィナー指数'].max() # 算出したシャノンウィナー指数の最大値を代入
df_recipes['食材多様性スコア'] = (df_recipes['食材シャノンウィナー指数'] / shannon_max * 30).round(1) # 最大値で正規化し点数を出す
総合点数の算出
最後に、総合点を算出します。
# 総合点数を算出する
df_recipes['腸活スコア'] = df_recipes['プレプロスコア'] + df_recipes['野菜量スコア'] + df_recipes['食材多様性スコア']
# スコアを新しいデータフレームに入れる
df_score = df_recipes[['料理名','食材数', 'プレプロスコア','野菜量スコア','食材多様性スコア','腸活スコア']]
# スコアデータの確認
df_score.head()
4. 結果の可視化・考察
算出した腸活スコアをもとに、料理ごとの傾向を見てみます。
各スコアの分布
まずは、各スコアの分布をヒストグラムで可視化します。
df_score.hist(figsize=(15, 10), bins=20)
【このグラフから読み取れること】
- 食材数が「1」の料理が多い
→ ジャムやバターなどの調味料類が含まれていたためと考えられる- 野菜量スコアは最大40点の配点だが、すべて20点未満となっている
→ 各料理の野菜量を1日摂取量目標350gと比較して計算しているため、単品の料理では目標に達しない可能性が高い
各スコアの分布
次に散布図でも可視化しました。
# 各スコアとの散布図
score_cols = ['食材数','プレプロスコア', '野菜量スコア', '食材多様性スコア']
fig, axes = plt.subplots(2, 2, figsize=(15, 10)) # 縦2×横2で描画する
for ax, col in zip(axes.flatten(), score_cols):
ax.scatter(df_score[col], df_score['腸活スコア'])
ax.set_xlabel(col)
ax.set_ylabel('腸活スコア')
ax.set_title(f'{col} vs 腸活スコア')
plt.tight_layout()
plt.show()
【このグラフから読み取れること】
- 野菜量スコアが低いにも関わらず、腸活スコアが高い料理がある
→野菜量が少なくても高得点になることがある- 多様性スコアが腸活スコアにやや直線的に分布しているように見える
→食材の種類が多い料理は野菜量や食物繊維量も多くなる傾向があり腸活スコアも高くなる
腸活スコア上位の料理
最後に腸活スコアの高い順で表示しました。
# 腸活スコアの高い順で表示
df_score.sort_values('腸活スコア', ascending=False)
1位は マカロニグラタン でした!
個人的には意外に感じたため、マカロニグラタンにはどのような食材が使われているのか、データを確認してみます。
# マカロニグラタンに使われている食材・グラムを抽出
pd.set_option('display.max_colwidth', 500)
df_macaroniguratan = df_recipes.loc[df_recipes['料理名'] == 'マカロニグラタン']
# 不要な列を削除する
df_macaroniguratan = df_macaroniguratan.drop(['料理の種類', 'エネルギー[キロカロリー]','たんぱく質[グラム]','脂質[グラム]','炭水化物[グラム]','食塩相当量[グラム]',
'材料15_材料名','材料15_重量[グラム]','材料16_材料名','材料16_重量[グラム]','材料17_材料名','材料17_重量[グラム]','材料18_材料名','材料18_重量[グラム]'],axis=1)
# マカロニグラタンのデータを確認
df_macaroniguratan
- 14種類の食材が使われており、食材多様性スコアも高い
- プレバイオティクス:たまねぎで、15点加点
- プロバイオティクス:ナチュラルチーズで、15点加点
これらが要因で、マカロニグラタンの腸活スコアが高くなったと考えられます。
課題と改善策
可視化した結果、今回の自作の腸活スコアの課題と改善案を整理します。
【課題】
- ジャムやバターなどの調味料類が含まれているため、食材数が「1」の料理が多くなってしまった
- 各料理の野菜量を1日摂取目標350gと比較して計算しているため、野菜量スコアが、すべて20点未満だった
- プレバイオティクス/プロバイオティクススコアは15点刻みの配点であるため、細かな違いがスコアに反映されない
【改善策】
- 調味料・単体トッピングはデータから除外する
- 野菜量の評価を「単品→1食分(または1食あたりの目標)」に変更する、あるいは1日を想定したスコアに調整する
- プレバイオティクス/プロバイオティクススコアを含む食材の数、または量に応じて調整する
- 野菜量ではなく食物繊維量のデータを用いる
まとめ
今回、自分でスコアの設計から実装、データの可視化まで一連の作業を通して、データ分析の流れを実際に体験することができました。
当初はより複雑なスコア設計を目指していましたが、実際に手を動かしてみると、まずは最小限の設計から始めることの大切さを改めて実感しました。また、スコアを一から組み立てる過程自体が、評価の軸をどのように設定すべきかを考える良い機会となりました。
最後までお読みいただき、ありがとうございました。