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で試算、生成AIに渡す比較表

0
Posted at

「反応のよい施策に予算を寄せる」。販促会議では通りやすい話です。ただ、最初の1万円で8件集まっても、次の1万円で同じ8件が増えるとは限りません。僕なら、追加でいくら使うと何件増えるかを表にしてから配分を決めます。

今回は6万円を3施策に分ける架空の例です。Pythonで予算内の54通りを比べると、通常は36件の案が首位。不振時を優先すると、別の配分が残りました。生成AIと販促案を練るとき、この2案があるだけで依頼を具体的に書けます。

件数は検証用の仮定です。広告実績や生成AIの予測値ではありません。検証はローカルのPythonで完結します。

1万円追加するたびに、同じ件数が増える?

集めたいのは翌月の相談申込。広告、ウェビナー、紹介施策に、それぞれ最大3万円まで使える設定にします。制作費も含めた想定です。

投入額 広告 ウェビナー 紹介施策
0万円 0件 0件 0件
1万円 8件 4件 7件
2万円 13件 10件 11件
3万円 15件 16件 13件

各セルは、その額まで使ったときの合計件数です。広告なら1万円追加するごとの増分は8件、5件、2件。2万円の13件に、1万円の8件を足すと二重計上になります。

広告は追加分の反応が鈍る設定。ウェビナーは開催準備を済ませてから集客を増やせる想定で、増分を4件、6件、6件にしています。最初の8件だけを見ると、ウェビナーの後半を取り逃がします。

表の「件数」は先に揃えたいところです。広告だけクリック数で、紹介だけ相談申込数なら足せません。ウェビナーの参加人数も今回は使いません。会議資料にある「反応数」、あの列名は便利すぎます。

Pythonで配分候補を全部並べる

1万円単位なら、候補は各施策で0、1、2、3の4個。組み合わせは4×4×4で64通りです。合計が6を超える候補を外せば、予算内の54通りになります。

ウェビナーの相談申込が伸びなかった場合も置きます。ウェビナーの件数だけを0、3、6、9件に差し替え、他の施策は同じにしました。

同じ配分を、通常時とウェビナー不振時の両方で計算します。

架空の販促予算6万円の配分を54通り比較する流れ。通常の件数を優先すると広告2・ウェビナー3・紹介1万円、不振時を優先すると各2万円が残る。

次を allocation.py として保存します。追加ライブラリは不要です。手元ではPython 3.14.6で実行しました。

from itertools import product

# 位置は広告、ウェビナー、紹介施策。値は合計件数。
normal = ([0, 8, 13, 15], [0, 4, 10, 16], [0, 7, 11, 13])
low = (normal[0], [0, 3, 6, 9], normal[2])

def compare(budget, normal, low):
    rows = []
    for plan in product(*(range(len(v)) for v in normal)):
        if sum(plan) > budget:
            continue
        base = sum(v[n] for v, n in zip(normal, plan))
        weak = sum(v[n] for v, n in zip(low, plan))
        rows.append((plan, base, weak))
    # 同点なら支出が少ない案、その次に配分の辞書順。
    usual = sorted(rows, key=lambda r: (-r[1], sum(r[0]), r[0]))
    cautious = sorted(
        rows, key=lambda r: (-min(r[1:]), sum(r[0]), r[0])
    )
    return usual, cautious

usual, cautious = compare(6, normal, low)
print(f"予算内: {len(usual)}通り")
print("配分は万円。順に広告、ウェビナー、紹介施策")
for plan, base, weak in usual[:3]:
    print(f"通常上位: {plan} / 通常{base}件 / 不振{weak}")
plan, base, weak = cautious[0]
print(f"下振れ優先: {plan} / 通常{base}件 / 不振{weak}")

ターミナルで実行します。

python3 allocation.py

実行結果です。

予算内: 54通り
配分は万円。順に広告、ウェビナー、紹介施策
通常上位: (2, 3, 1) / 通常36件 / 不振29件
通常上位: (1, 3, 2) / 通常35件 / 不振28件
通常上位: (2, 2, 2) / 通常34件 / 不振30件
下振れ優先: (2, 2, 2) / 通常34件 / 不振30件

product は複数の候補から全組み合わせを作ります。検証環境に同梱された itertools.product の説明でも、入れ子のforループに相当すると確認できます。

min(r[1:]) は、その配分の通常時と不振時のうち、少ないほうの件数です。それを大きい順に並べています。不振になる確率は置いていません。2つの想定だけを比べる方法で、あらゆる不振に耐える保証もありません。

予算を使い切る制約は入れていません。合計が6以下なら候補に残り、同点なら支出の少ない案を先に出します。検証では予算0のときに配分が (0, 0, 0) だけ残ることと、全件数を0にすると支出0の案が先頭になることも確かめました。

36件の案を、そのまま採用する前に

出力では、通常1位と3位が入れ替わりました。

広告2万円・ウェビナー3万円・紹介1万円なら、通常は36件。不振時は29件です。ウェビナーから紹介へ1万円移すと、通常は34件へ減りますが、不振時は30件になります。

通常の2件を取るか、不振時の1件を取るか。

下振れ優先がいつも正解とは言いません。ウェビナーの見込みに十分な根拠があるなら、通常36件の案にも理由があります。

僕はまず、この2案を残します。ここ、地味に効きます。配分を丸ごと考え直す話から、「ウェビナーへの最後の1万円をどうするか」まで絞れるので。

ただし、計算には割り切りがあります。同じ人が広告とウェビナーの両方から申し込む重複や、広告を見て紹介につながる相乗効果は含めていません。足し算するには、施策別の件数を重複しない基準で数えるか、重複分を除いた仮定が必要です。粗い元データなら、1件差で決める精度はありません。

施策を10個、各11候補に増やすと11の10乗、259億3742万4601通り。この全探索をそのまま広げるのは厳しいです。まずは今回のように少数の施策と粗い金額刻みで使います。

生成AIには、比較する材料ごと渡す

次は会議用の説明を作る依頼文の記入例です。生成AIへの送信や、回答品質の検証はしていません。

販促会議で使う比較メモを作成してください。
以下は架空データによる試算です。実績として書かないでください。
予算6万円、各施策の上限3万円。金額は広告/ウェビナー/紹介施策の順。
A案: 2/3/1万円。通常36件、ウェビナー不振時29件。
B案: 2/2/2万円。通常34件、ウェビナー不振時30件。
件数は相談申込数。重複と相乗効果は計算に含めていません。

A案からB案への配分変更と件数差を説明し、各案が合う条件を書いてください。
不振の発生確率、売上、費用対効果は不明です。数字を補わないでください。
最後に、採用を決めるため担当者に確認したいことを2つ挙げてください。

配分の数字はコードの出力と照合できます。生成AIの説明を読んで「売上が増える」と書かれていたら、今回の表には売上の根拠がないと戻せます。

会議へ持っていくのは通常36件の案と、不振時30件を残す案です。配分差はウェビナーから紹介へ移す1万円。その1万円分の相談申込を見込める根拠を、担当者に確認します。次月に実績が出たら表の件数を更新して、同じコードでもう一度比べられます。

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?