見出し画像

Python初心者がダイエットシミュレーター作ってみた<Numpy・Matplotlib・Pandas>

タイトル通り、Python初心者がPythonでダイエットシミュレーターを作ってみました。Numpy, Matplotlib, Pandasといった標準モジュールを自分で使えるようになりたかったので今回作成しました。
ちなみに、超基本的なスクリプトしか書いていないので、Python使いたての人の参考になるのではないかと思います。上級者からしたら拙いスクリプトだと思いますので甘めにみていただけると助かります。。。

ダイエットシミュレーター

基本的なロジックは、この記事参照してもらうと分かると思います。

一応簡単に説明しますと、体重計に乗る頻度の違いによって、体重がどのように変化するのかをシミュレーションする計算ロジックになっていて、飲み会という外乱や、体重を把握したときに食事制限しようという意思などを考慮しています。

スクリプト

早速スクリプトです。先に全スクリプト紹介してから、セクションごとに説明していきます。
以下、全スクリプト

import numpy as np
import random
from matplotlib import pyplot as plt
import pandas as pd


# 初期値入力
W0=65 #65kgスタート
Wgoal=60 #60kg目標
W_kcal=1/7200 #1kcalあたりの体重増減量kg
kcal_Weight=22.3 #成人男性の基礎代謝kcal/体重kg
Nomi_ritsu=1/7 #外乱(飲み会)が入る確率


#摂取カロリーを決める
Base=2800 #1日のベース摂取カロリーを2800kcalとする
Nomikai=3500 #飲み会の摂取カロリーを3500kcalとする
Seigen_ritsu=3/4 #食事制限時の摂取カロリーをいつもの3/4とする

#通常と飲み会のカロリーリストを用意
Meal=[Base,Nomikai]
#飲み会の確率の重みを追加
Meal_w=[1-Nomi_ritsu,Nomi_ritsu]

#1000日分の配列を用意
days=np.array(range(1,1000)) #日付配列
gain_base=np.empty_like(days, dtype = 'float') #摂取カロリー配列
consump_base=np.empty_like(days, dtype = 'float') #消費カロリー(基礎代謝)配列
weight_base=np.empty_like(days, dtype = 'float') #体重配列
check_base=np.zeros_like(days) #体重計乗ったあと食事制限をするか決める配列を用意
weight_base[0]=W0

#飲み会が入る確率を考慮して、毎日の摂取カロリーを決定
gain_base=np.random.choice(Meal,size=days.shape[0],p=Meal_w)

#体重計に乗らない、毎日乗る、毎週乗る、毎月乗る、それぞれに対してデータフレームを用意
Non_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everyday_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everyweek_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everymonth_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})


for i in range(days.shape[0]-1):
   
   #消費カロリーを計算
   Non_df.at[i,'consump']=Non_df.at[i,'weight']*kcal_Weight
   everyday_df.at[i,'consump']=everyday_df.at[i,'weight']*kcal_Weight
   everyweek_df.at[i,'consump']=everyweek_df.at[i,'weight']*kcal_Weight
   everymonth_df.at[i,'consump']=everymonth_df.at[i,'weight']*kcal_Weight
   
   #一回も体重計チェックしない場合
   Non_df.at[i+1,'weight']=Non_df.at[i,'weight']+(Non_df.at[i,'gain']-Non_df.at[i,'consump']*1.75)*W_kcal
   
   
   #毎日1回体重計チェックする場合
   if everyday_df.at[i,'weight']>Wgoal:
       everyday_df.at[i,'check']=1
   else:
       everyday_df.at[i,'check']=0
           
   if everyday_df.at[i,'check']==1 :
       everyday_df.at[i+1,'weight']=everyday_df.at[i,'weight']+(everyday_df.at[i,'gain']*Seigen_ritsu-everyday_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everyday_df.at[i+1,'weight']=everyday_df.at[i,'weight']+(everyday_df.at[i,'gain']-everyday_df.at[i,'consump']*1.75)*W_kcal

   
    #毎週1回体重計チェックする場合
   if i % 7==0 and everyweek_df.at[i,'weight']>Wgoal:
       everyweek_df.at[i,'check']=1
   else:
       everyweek_df.at[i,'check']=0
           
   if everyweek_df.at[i,'check']==1 or everyweek_df.at[i-1,'check']==1 or everyweek_df.at[i-2,'check']==1:
       everyweek_df.at[i+1,'weight']=everyweek_df.at[i,'weight']+(everyweek_df.at[i,'gain']*Seigen_ritsu-everyweek_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everyweek_df.at[i+1,'weight']=everyweek_df.at[i,'weight']+(everyweek_df.at[i,'gain']-everyweek_df.at[i,'consump']*1.75)*W_kcal
   
   
    #毎月1回体重計チェックする場合
   if i % 30==0 and everyweek_df.at[i,'weight']>Wgoal:
       everymonth_df.at[i,'check']=1
   else:
       everymonth_df.at[i,'check']=0
           
   if everymonth_df.at[i,'check']==1 or everymonth_df.at[i-1,'check']==1 or everymonth_df.at[i-2,'check']==1:
       everymonth_df.at[i+1,'weight']=everymonth_df.at[i,'weight']+(everymonth_df.at[i,'gain']*Seigen_ritsu-everymonth_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everymonth_df.at[i+1,'weight']=everymonth_df.at[i,'weight']+(everymonth_df.at[i,'gain']-everymonth_df.at[i,'consump']*1.75)*W_kcal


#体重の日付推移の可視化
fig = plt.figure()  
ax1=Non_df.plot(x='days', y='weight', label='Non Mesurement')
everyday_df.plot(x='days', y='weight',  label='per day',ax=ax1)
everyweek_df.plot(x='days', y='weight', label='per week',ax=ax1)
everymonth_df.plot(x='days', y='weight', label='per month',ax=ax1)
plt.xlabel('Days')
plt.ylabel('Weight [kg]')
plt.xlim(0, 1000)
plt.ylim(50, 80)
plt.legend()
plt.grid()
plt.show()

それではセクションごとに解説していきます。

<モジュールのインポート>
今回はどうしても、Numpy, Matplotlib, Pandasを使いたかったので無理矢理インポートして使用します。加えて後ほど紹介しますが、飲み会という外乱をランダム関数で入れたかったので、Randomというモジュールもインポートしておきます。

import numpy as np
import random
from matplotlib import pyplot as plt
import pandas as pd

<初期値入力>
まず、初期体重やら、基礎代謝情報、1日の摂取カロリーなどを入力していきます。ちなみに数字の根拠は、日本医師会のページをみたり、職場の同僚の相場を感じとったりして決めています。ここで重要なのは、飲み会と言う外乱が1/7(週1度)の確率で入ること、と食事制限(普段の3/4)というフィードバックをかけれることを設定することです。
前者については、後ほどnp.random.choiceを使ってある日の摂取カロリーを決めています。(6/7の確率で標準摂取カロリー、1/7の確率で飲み会摂取カロリー)
後者についても後ほどで出てきますが、体重計に乗って目標体重に達していなかったら3日間は食事制限を行うという論理にしています。(3日坊主を意識して)

# 初期値入力
W0=65 #65kgスタート
Wgoal=60 #60kg目標
W_kcal=1/7200 #1kcalあたりの体重増減量kg
kcal_Weight=22.3 #成人男性の基礎代謝kcal/体重kg
Nomi_ritsu=1/7 #外乱(飲み会)が入る確率


#摂取カロリーを決める
Base=2800 #1日のベース摂取カロリーを2800kcalとする
Nomikai=3500 #飲み会の摂取カロリーを3500kcalとする
Seigen_ritsu=3/4 #食事制限時の摂取カロリーをいつもの3/4とする

#通常と飲み会のカロリーリストを用意
Meal=[Base,Nomikai]
#飲み会の確率の重みを追加
Meal_w=[1-Nomi_ritsu,Nomi_ritsu]

<配列とデータフレームの用意>
そして次に行うのが、計算準備としての配列とデータフレームの準備です。まずは1000日分の日付配列を用意して、それと同サイズの摂取カロリー配列、基礎代謝配列、体重配列、食事制限意思配列を用意します。次に、np.random.choiceを使って摂取カロリーを決定します。そして、それぞれをPandasを使ってデータフレームに格納していきます。具体的には、体重計にのらない、毎日乗る、毎週乗る、毎月乗る、それぞれのデータフレームに格納します。これで演算準備の完了です。

#1000日分の配列を用意
days=np.array(range(1,1000)) #日付配列
gain_base=np.empty_like(days, dtype = 'float') #摂取カロリー配列
consump_base=np.empty_like(days, dtype = 'float') #消費カロリー(基礎代謝)配列
weight_base=np.empty_like(days, dtype = 'float') #体重配列
check_base=np.zeros_like(days) #体重計乗ったあと食事制限をするか決める配列を用意
weight_base[0]=W0

#飲み会が入る確率を考慮して、毎日の摂取カロリーを決定
gain_base=np.random.choice(Meal,size=days.shape[0],p=Meal_w)

#体重計に乗らない、毎日乗る、毎週乗る、毎月乗る、それぞれに対してデータフレームを用意
Non_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everyday_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everyweek_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})
everymonth_df=pd.DataFrame({'days':days,'gain':gain_base,'consump':consump_base,'weight':weight_base,'check':check_base})

<演算>
いよいよ演算です。今回はfor文を用いて、体重計に乗る頻度別にデータフレーム内の、消費カロリーと体重と食事制限意思にアクセスしてその日の値を計算していきます。体重計に乗って、目標体重に到達していなかったら、食事制限(普段の3/4)をする意思になり、3日間有効となっています。

for i in range(days.shape[0]-1):
   
   #消費カロリーを計算
   Non_df.at[i,'consump']=Non_df.at[i,'weight']*kcal_Weight
   everyday_df.at[i,'consump']=everyday_df.at[i,'weight']*kcal_Weight
   everyweek_df.at[i,'consump']=everyweek_df.at[i,'weight']*kcal_Weight
   everymonth_df.at[i,'consump']=everymonth_df.at[i,'weight']*kcal_Weight
   
   #一回も体重計チェックしない場合
   Non_df.at[i+1,'weight']=Non_df.at[i,'weight']+(Non_df.at[i,'gain']-Non_df.at[i,'consump']*1.75)*W_kcal
   
   
   #毎日1回体重計チェックする場合
   if everyday_df.at[i,'weight']>Wgoal:
       everyday_df.at[i,'check']=1
   else:
       everyday_df.at[i,'check']=0
           
   if everyday_df.at[i,'check']==1 :
       everyday_df.at[i+1,'weight']=everyday_df.at[i,'weight']+(everyday_df.at[i,'gain']*Seigen_ritsu-everyday_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everyday_df.at[i+1,'weight']=everyday_df.at[i,'weight']+(everyday_df.at[i,'gain']-everyday_df.at[i,'consump']*1.75)*W_kcal

   
    #毎週1回体重計チェックする場合
   if i % 7==0 and everyweek_df.at[i,'weight']>Wgoal:
       everyweek_df.at[i,'check']=1
   else:
       everyweek_df.at[i,'check']=0
           
   if everyweek_df.at[i,'check']==1 or everyweek_df.at[i-1,'check']==1 or everyweek_df.at[i-2,'check']==1:
       everyweek_df.at[i+1,'weight']=everyweek_df.at[i,'weight']+(everyweek_df.at[i,'gain']*Seigen_ritsu-everyweek_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everyweek_df.at[i+1,'weight']=everyweek_df.at[i,'weight']+(everyweek_df.at[i,'gain']-everyweek_df.at[i,'consump']*1.75)*W_kcal
   
   
    #毎月1回体重計チェックする場合
   if i % 30==0 and everyweek_df.at[i,'weight']>Wgoal:
       everymonth_df.at[i,'check']=1
   else:
       everymonth_df.at[i,'check']=0
           
   if everymonth_df.at[i,'check']==1 or everymonth_df.at[i-1,'check']==1 or everymonth_df.at[i-2,'check']==1:
       everymonth_df.at[i+1,'weight']=everymonth_df.at[i,'weight']+(everymonth_df.at[i,'gain']*Seigen_ritsu-everymonth_df.at[i,'consump']*1.75)*W_kcal
       
   else:
       everymonth_df.at[i+1,'weight']=everymonth_df.at[i,'weight']+(everymonth_df.at[i,'gain']-everymonth_df.at[i,'consump']*1.75)*W_kcal

<可視化>
さて、最後はMatplotlibを使った可視化です。正確にはPandasのデータフレームからそのままPlotしているので、なんとも言えないですが、使いたてなので許してください。。。凡例を作るためのlegendを用いたり、軸ラベルを定義したり、軸範囲を決定したりしています。

#体重の日付推移の可視化
fig = plt.figure()  
ax1=Non_df.plot(x='days', y='weight', label='Non Mesurement')
everyday_df.plot(x='days', y='weight',  label='per day',ax=ax1)
everyweek_df.plot(x='days', y='weight', label='per week',ax=ax1)
everymonth_df.plot(x='days', y='weight', label='per month',ax=ax1)
plt.xlabel('Days')
plt.ylabel('Weight [kg]')
plt.xlim(0, 1000)
plt.ylim(50, 80)
plt.legend()
plt.grid()
plt.show()

ちなみにこちらが、可視化したグラフになります。

結果、体重計に毎日乗れば、半年くらいで5kg痩せれるし、1回も乗らなかったら9kgくらい増えてしまうシミュレーションになりました。

まとめ

今回の感想ですが、Pandasもしや有能?って思い始めました。今回配列しか格納してないんですが、文字列など、異なるデータ型を格納できるんですね。Matlabでいうtableじゃんと。こりゃどんどん使って、使いこなせるようにならねばと、強く思っています。
以上、Python初心者が作るダイエットシミュレーターでした。

この記事が気に入ったらサポートをしてみませんか?