BETA

0歳児のねんねと機械学習で向き合う

投稿日:2020-01-21
最終更新:2020-01-24

我が子の睡眠時間が短い

私Taiyoには産まれて2ヶ月半の赤ちゃんがいます
この子が、一日に12時間くらいしかねんねしてくれません。

厚生労働省の科研「未就学児の睡眠指針」 (愛媛大学医学部附属病院ほか) によれば、
0~3ヶ月までの赤ちゃんは14~20時間程度は寝るはずらしく、個人差があるとは言え、ちょっと心配です。
↓未就学児の睡眠指針より抜粋

特に、夜にまとめて寝るのが下手なのか、夜間に3回、4回と泣いて目を覚まします。
そのたびに私や妻が起きて世話をすることになり、子供がいくらかわいいとはいえ、なかなかしんどいものがあります。

こんな時、どうすればいいでしょうか?
この記事を開いている皆さんならおわかりですね。
そう、機械学習の出番です。

ぴよログ

昨今の子育てにおいては、子供の定量的な観測が義務付けられています。
一般的には

  • 子供の入眠、起床時刻
  • 授乳時刻
  • ミルクや搾乳など摂食量
  • 体温
  • おしっこ/うんちの記録

などのお世話イベントが発生するたびに、その詳細を母子手帳というデータベースに書き込むことになっています (※注:病院や自治体の指導にもよります)

我が家では、はじめは母子手帳にペンで書き込みを行っていましたが、すぐさま次の問題が浮上しました

  • いちいち母子手帳を探すのが面倒
  • 記入をミスりやすい(特に時刻の行がずれる)
  • 一覧性が悪い

そこで、スマホの育児ロガーアプリ、 ぴよログ を導入しました。
アプリの導入によって上記の問題がすべてクリアされた他、夫婦の片方だけが外出したときに、外出先からリアルタイムに子供の状況が把握できるという+αメリットも得られました。すごくいいアプリです。

早速ぴよログの記録から、睡眠リズムを確認してみましょう。

青い帯が寝ている時間です。黄線で囲った夜間、何度も帯が途切れている、つまり覚醒していることが確認できます。

さて、ぴよログにはデータのエクスポート機能がついており、
各種のお世話記録を外部出力することができます。
これを使います。

データ加工

ぴよログのテキストに記録しているデータは
以下のようなフォーマットをしています(一部は省略・伏せ字としました)

2019/XX/YY(日)  
赤ちゃんの名前 (0歳0か月10日)  

00:15   寝る   
01:45   起きる(1時間30分)   
01:50   母乳 左15分 ◀ 右20分   
02:10   おしっこ   
02:10   うんち   
02:45   寝る   
...  
22:00   体温 37.2°C   
22:05   起きる(2時間40分)   
22:15   うんち   
22:55   おしっこ   
23:10   うんち   
23:30   うんち   

母乳合計   左 111分 / 右 96分  
ミルク合計  1回 60ml  
睡眠合計   14時間40分  
おしっこ合計 9回  
うんち合計  6回  

母乳合計   左 132分 / 右 93分  
ミルク合計  2回 100ml  
睡眠合計   12時間25分  
おしっこ合計 8回  
うんち合計  3回  

----------  
(以下、翌日以降のデータ)  

シンプルなテキストデータですので、正規表現を使ってパラメータを取り出せます。
0:00~23:59までを1日として、1日ごとの以下のパラメータを抽出し、日付順に並べたDataFrameを作りました。

パラメータ 説明
milk ミルク摂食量
sakubo 搾乳摂食量
junu 授乳時間
osikko おしっこ回数
unchi うんち回数
ofuro お風呂回数
age 日齢
last_unchi_timestamp 最後にしたうんちのタイムスタンプ
night_awaken その日の00:00 ~ 06:00 の区間で起きた回数

また、予測するデータとして、翌日の night_awaken を next_awaken 変数として追加します。
ここまでの作業で、準備完了です。次のDataFrameができました!

   night_awaken milk junu age unchi osikko ofuro sleep_time sakubo last_unchi_timestamp next_awaken  
55            2   60  215  58     4      4     0        710      0                  875           2  
56            2  120  219  59    10     10     0        725      0                 1400           2  
57            2  200  246  60    10      7     0        705      0                 1315           2  
58            2   60  227  61     7     11     1        805      0                 1420           2  
...  
72            2   60  203  75     5      9     1        675      0                 1415           1  
73            1   60  225  76     6      9     1        775      0                 1190           2  
74            2  120  207  77     6     12     1        710      0                 1350           2  
75            2    0  206  78     3     15     1        700      0                  645           2  

早速、「わが子は睡眠時間が短いのでは?」という出発点の疑問を確認してみましょう。

#1ヶ月までの平均睡眠時間  
df["sleep_time"][:30].mean()  
#直近1ヶ月の平均睡眠時間  
df["sleep_time"][-30:].mean()  


1ヶ月までの平均睡眠時間
758.1666666666666
約 12.6時間

直近1ヶ月の平均睡眠時間
734.0
約 12.1時間

厚生労働省の数字と比較し、睡眠時間がかなり短いことが確認できてしまいました...。
乖離の度合いで見れば、日を追うごとに改善されてきているとも言えるので、一応そこは安心です。

さて、さっそくデータ分析に取り掛かるとします。
この時期の子供は、日齢によって体の大きさも、可能な食事量・睡眠時間が大きく変わります。
全期間を使って分析するよりは、今と条件の近い最近の期間に絞ったほうが良さそうです。

というわけでこれ以降は直近の約3週間(20日分)を使って分析していきます
データが少なくてちょっと心配ですが、突き進むことにします。
(本当は、全国の赤ちゃんのぴよログデータが...欲しい!)

実験

scikit-learnを使い

  • 線形回帰
  • Lasso回帰 (alpha=0.01)
  • Ridge回帰
  • ElasticNet (alpha=0.01)

の各種法で、係数の最適化を行い、決定係数を比較します。
(各手法の詳細はこの記事で省略です。)

# df には20日分のお世話データが格納されている  
# 相関係数を確認しやすくするため、全パラメータを正規化する  
df = df.apply(lambda x: 0 if (np.max(x) - np.min(x) == 0) else (x - np.mean(x)) / (np.max(x) - np.min(x)))  
print(df.head())  

# 説明変数  
params = df.drop("next_awaken", axis=1)  
params = params.drop("night_awaken", axis=1)  
X = params.as_matrix()  

# 目的変数  
Y = df["next_awaken"].as_matrix()  

for pair in [("線形回帰",LinearRegression()),("Lasso",Lasso(alpha=0.01)),("Ridge",Ridge()),("ElasticNet",ElasticNet(alpha=0.01))]:  
    # 予測モデルを作成  
    clf = pair[1]  
    clf.fit(X, Y)  
    # 偏回帰係数  
    print(pair[0])  
    print(pd.DataFrame({"Name":params.columns,  
                    "Coefficients":clf.coef_}).sort_values(by='Coefficients') )  

    # 切片 (誤差)  
    print(clf.intercept_)  
    print(clf.score(X, Y))  

結果はこちら!

----------  
線形回帰  
                   Name  Coefficients  
8  last_unchi_timestamp     -0.481212  
4                osikko     -0.158962  
5                 ofuro     -0.112393  
0                  milk     -0.097127  
7                sakubo      0.000000  
1                  junu      0.094627  
2                   age      0.302271  
6            sleep_time      0.432513  
3                 unchi      0.495016  
決定係数:0.44878170564962716  
----------  
Lasso  
                   Name  Coefficients  
8  last_unchi_timestamp     -0.148757  
0                  milk     -0.121785  
5                 ofuro     -0.009426  
2                   age      0.000000  
4                osikko     -0.000000  
7                sakubo      0.000000  
1                  junu      0.070973  
3                 unchi      0.096484  
6            sleep_time      0.216156  
決定係数:0.2844070555951874  
----------  
Ridge  
                   Name  Coefficients  
8  last_unchi_timestamp     -0.188714  
0                  milk     -0.133980  
5                 ofuro     -0.061690  
4                osikko     -0.019417  
7                sakubo      0.000000  
2                   age      0.071252  
1                  junu      0.124285  
3                 unchi      0.145687  
6            sleep_time      0.234616  
決定係数:0.34302104932839544  
----------  
ElasticNet  
                   Name  Coefficients  
8  last_unchi_timestamp     -0.260609  
0                  milk     -0.126822  
5                 ofuro     -0.055467  
4                osikko     -0.000000  
7                sakubo      0.000000  
2                   age      0.057514  
1                  junu      0.102200  
3                 unchi      0.206477  
6            sleep_time      0.290440  
決定係数:0.37410413085237393  

単純な線形回帰が決定係数が一番高いという結果になりました。
それでも決定係数が0.44というのは頼りないですが、もともと不確定要素の多い事象を
限られたデータで扱っているのですから、仕方ありません。

せっかくなので、線形回帰に絞ってもう少しスッキリさせます
各回帰分析を横断的に見て、相関が低そうな

  • 日齢(age)
  • 搾母乳(sakubo) ※実は直近20日では全部0mlだった
  • おしっこ回数(osikko)
  • お風呂回数(ofuro)
    を説明変数から削除し、再度線形回帰を行います。

結果はこちら↓

線形回帰  
                   Name  Coefficients  
4  last_unchi_timestamp     -0.447534  
0                  milk     -0.216770  
1                  junu     -0.004459  
3            sleep_time      0.316513  
2                 unchi      0.477999  
決定係数:0.3885376002979878  

見通しが良くなりました。(決定係数は下がりましたが、説明変数を減らすと、たいていそうなります)
結果を読み解いてみると、こういうことになります。

  • しっかりうんちさせてから寝かせると、よく寝てくれるよ!
  • おっぱい(ミルク)が十分足りていると、よく寝てくれるよ!
  • 日中の睡眠時間が長すぎると、夜起きちゃうよ!

それっぽい結果が出ました!
これは期待できるかもしれません!

結果

早速、この知見を妻と共有し、翌日からの子育てで意識してみました。

そんな簡単にいくわけないと思うじゃないですか。
決定係数0.4ですし。

本当に起きる回数が減りました

最後までお読みいただき、ありがとうございました。

※余談ですが、最近妻が子育てブログをはじめました。
応援いただけると子育ての励みになります
よければお立ち寄りください
https://harapan-life-management.hatenablog.com/

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

論文紹介、知った技術、競技プログラミング参戦記書いていきます

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう