0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python×筋肉分析】PFCバランスが神な食材を探すッ!!!

0
Posted at

はじめに

どうも、マッチョです。💪

世の中では「鶏むね肉が最強」なんて言われていますが、
それが本当だなんてちゃんと確かめたことはないですよね。
なら、ちゃんとデータで決着つけようっちゅう話。

というわけで今回は、
厚労省の「日本食品標準成分表」のデータを使って、
どの食材が最も筋トレ向きなのか?

今回は、筋肉が歓喜する食材ランキングをデータで検証していこうと思います。。

📊 分析方法

  • 使用データ:厚労省「日本食品標準成分表(八訂)」
    https://www.mext.go.jp/content/20260327-mxt_kagsei-mext-000029402_02.xlsx
  • 対象項目:エネルギー、たんぱく質、脂質、炭水化物
  • スコアリング:各食品の3大栄養素比率を合計100%とし、PFCバランスの理想比(2:2:1)との距離をスコア化
  • スコアの基準:スコアが小さいほど「理想比に近い(優秀)」と判定
  • 同点の場合は:カロリーが低い順に並べてランキング
  • 外条件:エネルギー(kcal)が 0 の食品(水、お茶など)や調味料は除外

実行するソースコード

import numpy as np
import pandas as pd

FILE_PATH = "/content/drive/MyDrive/Colab Notebooks/Qiita/筋肉食材ランキング/20260327-mxt_kagsei-mext-000029402_02.xlsx"


def create_muscle_pfc_ranking(
    file_path: str,
    target_pfc: tuple = (2, 2, 1),
    exclude_non_food: bool = True,
    min_protein_g: float = 8.0,
):
    # 1. シートの読み込み
    raw_df = pd.read_excel(file_path, sheet_name="表全体", header=None)

    # 2. 成分識別子行の特定
    id_row_idx = None
    for idx, row in raw_df.head(15).iterrows():
        row_str_list = (
            row.astype(str)
            .str.replace(r"\s+", "", regex=True)
            .str.upper()
            .tolist()
        )
        if "ENERC_KCAL" in row_str_list:
            id_row_idx = idx
            break

    if id_row_idx is None:
        raise ValueError("成分識別子が見つかりませんでした。")

    identifiers = (
        raw_df.iloc[id_row_idx]
        .astype(str)
        .str.replace(r"\s+", "", regex=True)
        .str.upper()
        .tolist()
    )

    # 3. 列インデックスの特定
    col_indices = {}
    for col_idx in range(raw_df.shape[1]):
        column_str = "".join(raw_df.iloc[0 : id_row_idx, col_idx].astype(str))
        column_str_clean = column_str.replace(" ", "").replace(" ", "")
        if "食品名" in column_str_clean:
            col_indices["食品名"] = col_idx
            break

    def find_col_index(target_ids):
        for t_id in target_ids:
            if t_id in identifiers:
                return identifiers.index(t_id)
        return None

    col_indices["エネルギー"] = find_col_index(["ENERC_KCAL"])
    col_indices["たんぱく質"] = find_col_index(["PROT-", "PROT"])
    col_indices["脂質"] = find_col_index(["FAT-", "FAT"])
    col_indices["炭水化物"] = find_col_index(["CHO-", "CHO", "CHOAVL"])

    # 4. データの抽出とクレンジング
    data_start_idx = id_row_idx + 1
    clean_df = pd.DataFrame()

    clean_df["食品名"] = (
        raw_df.iloc[data_start_idx:, col_indices["食品名"]]
        .astype(str)
        .str.strip()
    )
    clean_df["エネルギー(kcal)"] = raw_df.iloc[
        data_start_idx:, col_indices["エネルギー"]
    ]
    clean_df["たんぱく質(g)"] = raw_df.iloc[
        data_start_idx:, col_indices["たんぱく質"]
    ]
    clean_df["脂質(g)"] = raw_df.iloc[data_start_idx:, col_indices["脂質"]]
    clean_df["炭水化物(g)"] = raw_df.iloc[
        data_start_idx:, col_indices["炭水化物"]
    ]

    clean_df = clean_df[
        clean_df["食品名"].notna()
        & (clean_df["食品名"] != "")
        & (~clean_df["食品名"].str.contains("単位|成分識別子|食品名"))
    ]

    calc_cols = ["エネルギー(kcal)", "たんぱく質(g)", "脂質(g)", "炭水化物(g)"]
    for col in calc_cols:
        clean_df[col] = pd.to_numeric(
            clean_df[col]
            .astype(str)
            .str.replace(r"[^\d\.]", "", regex=True),
            errors="coerce",
        ).fillna(0)

    # 調味料などの除外
    if exclude_non_food:
        exclude_keywords = [
            "みそ",
            "味噌",
            "しょうゆ",
            "ソース",
            "ドレッシング",
            "カレーパウダー",
            "ジャム",
            "マヨネーズ",
            "だし",
            "スープ",
            "乾燥",
            "こしょう",
            "プレミックス",
        ]
        pattern = "|".join(exclude_keywords)
        clean_df = clean_df[~clean_df["食品名"].str.contains(pattern)]

    # たんぱく質下限による足切り
    clean_df = clean_df[clean_df["たんぱく質(g)"] >= min_protein_g]

    # 5. PFCカロリー比率の計算
    p_cal = clean_df["たんぱく質(g)"] * 4
    f_cal = clean_df["脂質(g)"] * 9
    c_cal = clean_df["炭水化物(g)"] * 4

    total_cal = p_cal + f_cal + c_cal
    total_cal = total_cal.replace(0, np.nan)

    clean_df["pct_p_cal"] = p_cal / total_cal
    clean_df["pct_f_cal"] = f_cal / total_cal
    clean_df["pct_c_cal"] = c_cal / total_cal

    target_sum = sum(target_pfc)
    ideal_p = target_pfc[0] / target_sum
    ideal_f = target_pfc[1] / target_sum
    ideal_c = target_pfc[2] / target_sum

    # 6. ペナルティスコアの計算
    p_penalty = np.maximum(0, ideal_p - clean_df["pct_p_cal"])
    f_penalty = np.maximum(0, clean_df["pct_f_cal"] - ideal_f)
    c_penalty = np.maximum(0, clean_df["pct_c_cal"] - ideal_c)

    clean_df["score"] = np.sqrt(
        p_penalty**2 + f_penalty**2 + c_penalty**2
    )
    clean_df["score"] = clean_df["score"].fillna(np.inf)

    # 7. ランキングのソートと確定
    ranked_df = clean_df.sort_values(
        by=["score", "たんぱく質(g)", "エネルギー(kcal)"],
        ascending=[True, False, True],
    ).reset_index(drop=True)
    ranked_df["ranking"] = ranked_df.index + 1

    # 8. 表記の丸め処理
    for col in ["pct_p_cal", "pct_f_cal", "pct_c_cal"]:
        ranked_df[col] = (ranked_df[col] * 100).round(1).astype(str) + "%"

    ranked_df["score"] = ranked_df["score"].round(4)

    return ranked_df


# ランキングの実行
try:
    result_df = create_muscle_pfc_ranking(
        FILE_PATH, target_pfc=(2, 2, 1), exclude_non_food=True, min_protein_g=8.0
    )

    print("\n--- 筋肉食材!PFCカロリーバランス(2:2:1) パターンAランキング TOP 30 ---")
    display(
        result_df[
            [
                "ranking",
                "食品名",
                "エネルギー(kcal)",
                "score",
                "たんぱく質(g)",
                "脂質(g)",
                "炭水化物(g)",
                "pct_p_cal",
                "pct_f_cal",
                "pct_c_cal",
            ]
        ].head(20)
    )
except Exception as e:
    print(f"エラーが発生しました: {e}")

ソースコードの説明です。

1. 成分標準成分表の自動パース(前処理)
Excel(「表全体」シート)から、成分識別子(ENERC_KCAL や PROT など)の行を検出し、必要な列インデックス(食品名・カロリー・3大栄養素)を特定してクレンジングします。

2. ノイズデータの除外
エネルギーが 0 の食品や、日常の摂取量が少なくデータ上の数値が跳ねやすい「調味料・スープ」などをキーワードで除外。さらに、たんぱく質が極端に少ない食材を足切りします。

3. カロリーベースのPFC比率と「理想との距離」の計算
各栄養素をグラム数からカロリーに換算(P=4, F=9, C=4)し、その合計に対する各自の割合を算出。目標とする理想比(2:2:1 = 40%:40%:20%)からの乖離度をユークリッド距離(ベクトルの長さ)を用いてスコア化します。

4. 筋肉仕様のソートによるランキング確定
スコアが小さい順(=理想比に近い順)を最優先し、同点の場合は「たんぱく質含有量(g)が多い順」、さらに同点なら「総エネルギー(kcal)が低い順」の3段階でソートをかけます。

ということで、結果を見てみましょう。

💪 結果(TOP20)💪

$\text{順位}$ $\text{食品名}$ $\text{kcal}$ $\text{score}$ $\text{P(g)}$ $\text{F(g)}$ $\text{C(g)}$ $\text{P%}$ $\text{F%}$ $\text{C%}$
$\text{1}$ $\text{<畜肉類> ぶた [その他] ゼラチン}$ $\text{347}$ $\text{0.0}$ $\text{87.6}$ $\text{0.3}$ $\text{0.0}$ $\text{99.2%}$ $\text{0.8%}$ $\text{0.0%}$
$\text{2}$ $\text{<牛乳及び乳製品> (その他) カゼイン}$ $\text{358}$ $\text{0.0}$ $\text{86.2}$ $\text{1.5}$ $\text{0.0}$ $\text{96.2%}$ $\text{3.8%}$ $\text{0.0%}$
$\text{3}$ $\text{<魚類> (さめ類) ふかひれ}$ $\text{344}$ $\text{0.0}$ $\text{83.9}$ $\text{1.6}$ $\text{0.0}$ $\text{95.9%}$ $\text{4.1%}$ $\text{0.0%}$
$\text{4}$ $\text{<魚類> とびうお 煮干し}$ $\text{325}$ $\text{0.0}$ $\text{80.0}$ $\text{2.2}$ $\text{0.1}$ $\text{94.1%}$ $\text{5.8%}$ $\text{0.1%}$
$\text{5}$ $\text{だいず [その他] 大豆たんぱく 分離大豆たんぱく 塩分無調整タイプ}$ $\text{335}$ $\text{0.0}$ $\text{79.1}$ $\text{3.0}$ $\text{1.0}$ $\text{91.1%}$ $\text{7.8%}$ $\text{1.2%}$
$\text{6}$ $\text{だいず [その他] 大豆たんぱく 分離大豆たんぱく 塩分調整タイプ}$ $\text{335}$ $\text{0.0}$ $\text{79.1}$ $\text{3.0}$ $\text{1.0}$ $\text{91.1%}$ $\text{7.8%}$ $\text{1.2%}$
$\text{7}$ $\text{<魚類> (さけ・ます類) しろさけ サケ節 削り節}$ $\text{346}$ $\text{0.0}$ $\text{77.4}$ $\text{3.4}$ $\text{0.2}$ $\text{90.8%}$ $\text{9.0%}$ $\text{0.2%}$
$\text{8}$ $\text{<魚類> (かつお類) 加工品 かつお節}$ $\text{332}$ $\text{0.0}$ $\text{77.1}$ $\text{2.9}$ $\text{0.7}$ $\text{91.4%}$ $\text{7.7%}$ $\text{0.8%}$
$\text{9}$ $\text{<魚類> (かつお類) 加工品 削り節}$ $\text{327}$ $\text{0.0}$ $\text{75.7}$ $\text{3.2}$ $\text{0.4}$ $\text{90.9%}$ $\text{8.6%}$ $\mathbf{0.5%}$
$\text{10}$ $\text{<魚類> (いわし類) たたみいわし}$ $\text{348}$ $\text{0.0}$ $\text{75.1}$ $\text{5.6}$ $\text{0.6}$ $\text{85.1%}$ $\text{14.3%}$ $\text{0.7%}$

💡 分析結果と考察

1. プロテイン原材料が上位を独占

ゼラチン?カゼイン?なんのこっちゃ分からないですが、どうやら市販されているプロテインパウダーの原材料に使われているものらしいです。そりゃ1、2位になりますね。

2. 乾燥・加工魚介類のポテンシャル

水分を徹底的に飛ばす(乾燥させる)ことで、100gあたりの栄養密度を限界まで高めてるってことですかね。

💡 考察

1、2位はなるべくしてなったと言えますね。魚介系は乾燥してるが故のランクインのため、今回の検証は微妙だったかも、、、と感じでいます。
ちなみに、鶏むね肉(皮なし)は40位、卵は100位以下でした。

まとめ

個人的には3位のフカヒレが意外でした。すごいタンパク質量ですね。栄養面で見ても高級なだけのことはある。
今回は食材の栄養価のみで判定し、食べやすさなどの現実味はなかったため、そこは改善の余地ありと感じました。

では💪

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?