
Pythonでシミュレーションするヒゲ脱毛 by 三浦
こんにちは、三浦です。
突然ですが今年9月から、ヒゲ脱毛をはじめました。
この記事では、「レーザー照射ってどれくらい間隔をあけて行ったらいいの?」という自分自身の疑問を解消するためのシミュレーション結果を共有します。
1. そもそも脱毛って?
脱毛経験の無いの方のために簡単に説明すると、脱毛というのは一度のレーザー照射で終わるものではありません。なぜなら、毛というのは成長期・退行期・休止期というサイクルを繰り返しており、レーザーは成長期の毛に照射した時にしか脱毛効果を発揮しないからです。

成長期か退行期か休止期かは毛それぞれによって違うので、多くの毛にレーザーを効かせるには一定期間あけてレーザーを照射する必要があるというわけですね。
そんなわけでクリニックでレーザー照射を行うと「次回はまた1ヶ月以降に来てくださいね〜」と言われます。
「1ヶ月以降でどれぐらいがベストなの?」
「というか、そもそも本当に1ヶ月も間空けたほうがいいの?」
と思う方も多いのではないでしょうか。私はそうでした。
そこで今回は、ヒゲ脱毛の適切なレーザー照射間隔についてPythonでシミュレーションを行って調べてみました。
2. シミュレーションの前提条件
今回のシミュレーションでは、以下の条件を設定します。
シミュレーションするヒゲの本数:3,000本
ヒゲは通常1万〜3万本程度生えているそうですが、バカ正直にやると計算時間がえげつないことになります。ここはおとなしく、10分の1の3,000本として計算しておきましょう。観察期間:レーザー照射前180日 + レーザー照射後365日
この期間で、ヒゲ脱毛をした場合としなかった場合の推移を観察してみます。成長期の期間:平均242.5日(標準偏差61.25日)で正規分布
ヒゲの成長期は一般的に4ヶ月〜1年程度、日数にして120日〜365日程度と言われています。
ヒゲ3,000本それぞれの成長期を120日〜365日でランダムに決めても良かったのですが、世の中の多くの例に従って正規分布にしてみましょう。
平均μは120~365の中心である242.5。標準偏差σは、120~365が2σの範囲と仮定して、標準偏差σ=61.25とします。
「全体の95%が120〜365日になるんだなぁ」程度に思ってもらえたら大丈夫です。退行期+休止期の期間:平均60日(標準偏差15日)で正規分布
退行期+休止期は一般的に1〜3ヶ月程度、日数にして30日〜90日程度と言われています。
あとの決め方は成長期と同様です。レーザー照射の処理:レーザー照射時(=0日)に成長期であるヒゲの状態を、以降すべて0で固定する
3. 結果だけ知りたい人向け
レーザーを照射した時と照射しなかった時のヒゲの本数をグラフにすると、以下のようになります。

横軸が日数、縦軸が成長期のヒゲの本数、緑の点線が照射日を表しています。青線がレーザー照射しなかった場合の成長期のヒゲの本数、赤線がレーザー照射した場合の成長期のヒゲの本数です。照射した瞬間に本数がガクッと落ちているのがわかりますね。
ガクッと落ちた後は退行期・休止期でレーザーをまぬがれた毛が生えてくるわけですが、だいたい2〜3ヶ月くらいでいわゆる「サチってる」状態になります。
したがって、レーザー照射の間隔は3ヶ月くらいあけておけばよさそうということになります。
4. コード
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 乱数を固定
np.random.seed(42)
# パラメータ設定
N_HAIRS = 3000 # ヒゲの本数
GROWTH_PERIOD_LOWER_LIMIT = 120 # 成長期の下限
GROWTH_PERIOD_UPPER_LIMIT = 365 # 成長期の上限
REST_PERIOD_LOWER_LIMIT = 30 # 退行・休止期の下限
REST_PERIOD_UPPER_LIMIT = 90 # 退行・休止期の下限
DAYS_BEFORE = 180 # レーザー照射前の日数
DAYS_AFTER = 365 # レーザー照射後の日数
TOTAL_DAYS = DAYS_BEFORE + DAYS_AFTER + 1 # 総日数(レーザー照射日を含む)
TREATMENT_DAY = DAYS_BEFORE # レーザー照射日(30日目)
# 成長期の期間を生成(正規分布)
growth_period_mean = np.mean([GROWTH_PERIOD_LOWER_LIMIT, GROWTH_PERIOD_UPPER_LIMIT])
growth_period_std = (GROWTH_PERIOD_UPPER_LIMIT - growth_period_mean) / 2
growth_periods = np.clip(np.random.normal(growth_period_mean, growth_period_std, N_HAIRS), 1, float('inf')).astype(int)
# 退行・休止期の期間を生成(正規分布)
rest_period_mean = np.mean([REST_PERIOD_LOWER_LIMIT, REST_PERIOD_UPPER_LIMIT])
rest_period_std = (REST_PERIOD_UPPER_LIMIT - rest_period_mean) / 2
rest_periods = np.clip(np.random.normal(rest_period_mean, rest_period_std, N_HAIRS),1, float('inf')).astype(int)
# 各ヒゲの総サイクル期間
total_cycles = growth_periods + rest_periods
# 初期状態をランダムにする
initial_phases = np.random.randint(0, total_cycles)
# データフレームを作成(レーザー照射なし)
hair_states_without = pd.DataFrame(0, index=range(N_HAIRS), columns=range(-DAYS_BEFORE, DAYS_AFTER + 1))
# データフレームを作成(レーザー照射あり)
hair_states_with = pd.DataFrame(0, index=range(N_HAIRS), columns=range(-DAYS_BEFORE, DAYS_AFTER + 1))
# 各ヒゲの状態をシミュレーション(レーザー照射なし)
for hair in range(N_HAIRS):
cycle_length = total_cycles[hair]
growth_length = growth_periods[hair]
for day in range(-DAYS_BEFORE, DAYS_AFTER + 1):
current_phase = (initial_phases[hair] + day + DAYS_BEFORE) % cycle_length
if current_phase < growth_length:
hair_states_without.iloc[hair, day + DAYS_BEFORE] = 1
else:
hair_states_without.iloc[hair, day + DAYS_BEFORE] = 0
# 各ヒゲの状態をシミュレーション(レーザー照射あり)
removed_hairs = set() # 除去されたヒゲを記録
for hair in range(N_HAIRS):
cycle_length = total_cycles[hair]
growth_length = growth_periods[hair]
# レーザー照射日の状態をチェック
treatment_phase = (initial_phases[hair] + DAYS_BEFORE) % cycle_length
is_growing_at_treatment = treatment_phase < growth_length
for day in range(-DAYS_BEFORE, DAYS_AFTER + 1):
if hair in removed_hairs:
# 除去されたヒゲは常に0
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
else:
current_phase = (initial_phases[hair] + day + DAYS_BEFORE) % cycle_length
is_growing = current_phase < growth_length
# レーザー照射日に成長期だったヒゲを記録
if day == 0 and is_growing_at_treatment: # レーザー照射日(day=0)のチェック
removed_hairs.add(hair)
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
else:
if is_growing:
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 1
else:
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
# 日ごとの成長期のヒゲの本数を計算
daily_growing_without = hair_states_without.sum()
daily_growing_with = hair_states_with.sum()
# グラフの作成
plt.figure(figsize=(15, 7))
plt.plot(range(-DAYS_BEFORE, DAYS_AFTER + 1), daily_growing_without,
color='blue', linewidth=2, label='Without Treatment')
plt.plot(range(-DAYS_BEFORE, DAYS_AFTER + 1), daily_growing_with,
color='red', linewidth=2, label='With Treatment')
plt.axvline(x=0, color='green', linestyle='--', alpha=0.5, label='Treatment Day')
plt.title('Beard Hair Removal Treatment Simulation', fontsize=14)
plt.xlabel('Days from Treatment', fontsize=12)
plt.ylabel('Number of Growing Hairs', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
項目ごとに解説していきます。
必要なライブラリとパラメータの設定
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 乱数を固定
np.random.seed(42)
# パラメータ設定
N_HAIRS = 3000 # ヒゲの本数
GROWTH_PERIOD_LOWER_LIMIT = 120 # 成長期の下限
GROWTH_PERIOD_UPPER_LIMIT = 365 # 成長期の上限
REST_PERIOD_LOWER_LIMIT = 30 # 退行・休止期の下限
REST_PERIOD_UPPER_LIMIT = 90 # 退行・休止期の下限
DAYS_BEFORE = 180 # レーザー照射前の日数
DAYS_AFTER = 365 # レーザー照射後の日数
TOTAL_DAYS = DAYS_BEFORE + DAYS_AFTER + 1 # 総日数(レーザー照射日を含む)
TREATMENT_DAY = DAYS_BEFORE # レーザー照射日(30日目)
成長サイクルの生成
# 成長期の期間を生成(正規分布)
growth_period_mean = np.mean([GROWTH_PERIOD_LOWER_LIMIT, GROWTH_PERIOD_UPPER_LIMIT])
growth_period_std = (GROWTH_PERIOD_UPPER_LIMIT - growth_period_mean) / 2
growth_periods = np.clip(np.random.normal(growth_period_mean, growth_period_std, N_HAIRS), 1, float('inf')).astype(int)
# 退行・休止期の期間を生成(正規分布)
rest_period_mean = np.mean([REST_PERIOD_LOWER_LIMIT, REST_PERIOD_UPPER_LIMIT])
rest_period_std = (REST_PERIOD_UPPER_LIMIT - rest_period_mean) / 2
rest_periods = np.clip(np.random.normal(rest_period_mean, rest_period_std, N_HAIRS),1, float('inf')).astype(int)
各ヒゲの成長期と退行・休止期の期間を正規分布に従って生成します。
平均値(mean)と標準偏差(std)を計算
正規分布を使って、ランダムな期間を生成
万が一期間が0以下になる場合を防ぐため、1以上の値になるようclipで制限
小数点を切り捨てて整数に変換
# 各ヒゲの総サイクル期間と初期状態の設定
total_cycles = growth_periods + rest_periods
initial_phases = np.random.randint(0, total_cycles)
各ヒゲの周期の合計を計算し、初期状態が成長・退行・休止期のうち何日目なのかランダムに設定します。
データフレームの作成
# データフレームを作成(レーザー照射なし)
hair_states_without = pd.DataFrame(0, index=range(N_HAIRS), columns=range(-DAYS_BEFORE, DAYS_AFTER + 1))
# データフレームを作成(レーザー照射あり)
hair_states_with = pd.DataFrame(0, index=range(N_HAIRS), columns=range(-DAYS_BEFORE, DAYS_AFTER + 1))
レーザー照射ありとなしの2つの状況をシミュレーションするため、2つのデータフレームを用意します。
行:各ヒゲ(3,000本)
列:日数(レーザー照射前180日からレーザー照射後365日)
値:0(退行・休止期)または1(成長期)
レーザー照射なしの場合のシミュレーション
# 各ヒゲの状態をシミュレーション(レーザー照射なし)
for hair in range(N_HAIRS):
cycle_length = total_cycles[hair]
growth_length = growth_periods[hair]
for day in range(-DAYS_BEFORE, DAYS_AFTER + 1):
current_phase = (initial_phases[hair] + day + DAYS_BEFORE) % cycle_length
if current_phase < growth_length:
hair_states_without.iloc[hair, day + DAYS_BEFORE] = 1
else:
hair_states_without.iloc[hair, day + DAYS_BEFORE] = 0
レーザー照射を行わない場合の各ヒゲの状態をシミュレーションします。
各ヒゲについて、その周期の長さと成長期の長さを取得
各日について、成長サイクルの何日目を計算
成長期なら1、退行・休止期なら0を記録
レーザー照射ありのシミュレーション
# 各ヒゲの状態をシミュレーション(レーザー照射あり)
removed_hairs = set() # 除去されたヒゲを記録
for hair in range(N_HAIRS):
cycle_length = total_cycles[hair]
growth_length = growth_periods[hair]
# レーザー照射日の状態をチェック
treatment_phase = (initial_phases[hair] + DAYS_BEFORE) % cycle_length
is_growing_at_treatment = treatment_phase < growth_length
for day in range(-DAYS_BEFORE, DAYS_AFTER + 1):
if hair in removed_hairs:
# 除去されたヒゲは常に0
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
else:
current_phase = (initial_phases[hair] + day + DAYS_BEFORE) % cycle_length
is_growing = current_phase < growth_length
# レーザー照射日に成長期だったヒゲを記録
if day == 0 and is_growing_at_treatment: # レーザー照射日(day=0)のチェック
removed_hairs.add(hair)
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
else:
if is_growing:
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 1
else:
hair_states_with.iloc[hair, day + DAYS_BEFORE] = 0
レーザー照射を行う場合のシミュレーションです。
除去されたヒゲを記録するセットを用意
各ヒゲについて:
レーザー照射日に成長期だったかチェック
既に除去されているヒゲは常に0
レーザー照射日に成長期だったヒゲは除去して0に
それ以外は通常の周期を継続
結果の集計
# 日ごとの成長期のヒゲの本数を計算
daily_growing_without = hair_states_without.sum()
daily_growing_with = hair_states_with.sum()
各日における成長期のヒゲの本数を集計します。
5. グラフの作成
plt.figure(figsize=(15, 7))
plt.plot(range(-DAYS_BEFORE, DAYS_AFTER + 1), daily_growing_without,
color='blue', linewidth=2, label='Without Treatment')
plt.plot(range(-DAYS_BEFORE, DAYS_AFTER + 1), daily_growing_with,
color='red', linewidth=2, label='With Treatment')
plt.axvline(x=0, color='green', linestyle='--', alpha=0.5, label='Treatment Day')
plt.title('Beard Hair Removal Treatment Simulation', fontsize=14)
plt.xlabel('Days from Treatment', fontsize=12)
plt.ylabel('Number of Growing Hairs', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend()
plt.show()
結果をグラフで表示します:
青線:レーザー照射なしの場合の成長期のヒゲの数
赤線:レーザー照射ありの場合の成長期のヒゲの数
緑の点線:レーザー照射日
6. まとめ
「なんとなく」でクリニックの予約をとっていましたが、確信をもって2ヶ月以上あける決意ができました。
私は次は2025年1月に通おうと思います。ヒゲ脱毛erの皆さんの参考になれば幸いです。
7. 今後の展望
「まぁ2〜3ヶ月くらいでサチってるっしょ」というふわっとした結論にもっていってしまいましたが、y = a(1-exp(-t/τ))みたいな数式でフィッティングして客観的なサチる時間を計算したほうがいいですよね。気が向いたらやろうと思います。
また、根拠ある来院期間を提案したい脱毛機関にお勤めの皆様、ヒゲ脱毛のシミュレーションはぜひハイテックシステムズにお任せください。