生年月日から年齢を計算する

生年月日から年齢を計算する

生年月日から年齢を計算する

 生年月日 と現在の日付を与えて正確な 年齢を計算 させるプログラムを作ってみましょう。この手の計算は閏年などが絡んできて案外面倒なのですが、dateutil をインポートすると簡単に実装できます。

# PYTHON_AGE_01-1

from datetime import date
from dateutil.relativedelta import relativedelta

 datetime は標準ライブラリです。dateutil はサードパーティのライブラリですが、Anaconda には同梱されています。もし dateutil がない場合は pip コマンドでインストールしておいてください。

 たとえば、1998 年 5 月 12 日から現在までの経過時間を計算する場合は次のようなコードを書きます。

# PYTHON_AGE_01-2

# 生年月日
d0 = date(1998, 5, 12)

# 現在の日付
d1 = date.today()

# 経過時間
dy = relativedelta(d1, d0)

print(dy)
relativedelta(years=+21, months=+3, days=+8)

 この結果は 2019 年 8 月 20 日に実行して得たものです。
 コードを実行する日時によって、計算結果が異なります。

 実行結果にあるように、relativedelta() は日付の差分を何年 (years)、何ヶ月 (months)、何日 (days) という形で返します。

 relativedelta オブジェクトの years 属性で経過年数 (年齢) を取り出すことができます。

# PYTHON_AGE_01-3

# years属性で経過年数を取得
print(dy.years)
21

 生年月日と任意の日付を与えて、指定した日付における年齢を計算する関数 age_calculator() を定義してみます。

# PYTHON_AGE_01-4

# 年齢計算関数
def age_calculator(d0, d1=date.today()):
    dy = relativedelta(d1, d0)
    return dy.years

 たとえば、2013 年 9 月 15 日生まれの人の、2020 年 1 月 10 日における年齢は次のように計算できます。

# PYTHON_AGE_01-5

# 生年月日
d0 = date(2013, 9, 15)

# 年齢を計算する日付
d1 = date(2030, 1, 10)

# 指定日付における年齢
age = age_calculator(d0, d1)

print(age)
16

 

カラー図解 最新 Raspberry Piで学ぶ電子工作 作って動かしてしくみがわかる (ブルーバックス)

新品価格
¥1,296から
(2019/8/21 23:40時点)

生年月日をランダムに生成する

rand_birth()

 生年月日をランダムに生成する rand_birth() 関数です。

# PYTHON_AGE_02

import numpy as np
from datetime import date
from dateutil.relativedelta import relativedelta

# 生年月日生成関数
def rand_birth(d1=date.today(), upper=100):

    # 現在の年数
    year_now = d1.year

    # 乱数を発生
    rd = np.random.randint(1, upper+1)

    # 現在の年数から乱数を差し引いた日付を
    # 生年月日の初期値とする
    d0 = date(year_now - rd, 1, 1)

    # 追加日数をランダムに決定
    d = np.random.randint(0, 366)

    # 生年月日を決定
    birthday = d0 + relativedelta(days=+d)

    # 年齢を決定
    age = age_calculator(birthday, d1=d1)
    
    return birthday, age

 第 1 引数には計算の起点となる日付を渡します (デフォルトは現在の日付)。第 2 引数 upper には年齢上限値を渡します (デフォルトは 100)。戻り値は生年月日と指定した日付における年齢です。

 年齢上限値を 80 歳として、生年月日と 2015 年 8 月 10 日における年齢を計算する場合は次のように記述します。

# 年齢を計算する日付
d1 = date(2015, 8, 10)

# 生年月日を自動生成
b = rand_birth(d1 = d1, upper=80)

print(b)
(datetime.date(1958, 8, 10), 56)

 乱数を使っているので、実行するたびに結果は異なります。
 

rand_age()

 rand_birth() はどの年齢も等しい確率で生成されますが、日本の年齢別人口分布を反映して年齢を生成しようとすると、かなり高度な統計学の知識を必要とします。ここでは詳細に深入りせずに、結論だけ簡単にまとめておきます。

 以下の関数を使うと、現実の人口分布に近い確率で年齢を生成できます。
 
\[\begin{align*}a_m(x)&=2704x^6-7412x^5+7574x^4-3469x^3+624x^2+79x\\[6pt]
a_f(x)&=2168x^6-6090x^5+6426x^4-3078x^3+590x^2+85x\end{align*}\]
 $a_m(x),\ a_f(x)$ は、$[0,1]$ の一様乱数 $x$ を使って、それぞれ男性と女性の年齢を決定する関数です。多項式近似とよばれる手法を使って導いた 6 次近似関数に若干の修正を加えています。多項式近似について詳しく学んでみたい人は、当サイトの 機械学習 の記事を参照してください。

 このようにして得られた年齢 $y$ の小数部に一年間の平均日数 365.2425 日を掛けて、日数 $d$ に変換します。
 
\[d=365.2425(y-[y])\]
 ここで $[y]$ は $y$ の整数部であり、$y-[y]$ は $y$ の小数部だけを取り出すことを意味します。指定した日付から年数 $y$ と日数 $d$ を差し引けば生年月日を取得できます。

# PYTHON_AGE_03

import numpy as np
from datetime import date
from dateutil.relativedelta import relativedelta

# 年齢関数
def rand_age(sex="m", d1=date.today()):

    # 0以上1未満の乱数を生成
    x = np.random.rand()

    # 累積確率分布近似曲線を使って年齢を決定
    if sex == "m":
        y =   2704 * x**6 - 7412 * x**5 + 7574 * x**4\
            - 3469 * x**3 +  624 * x**2 +   79 * x

    elif sex == "f":
        y =   2168 * x**6 - 6090 * x**5 + 6426 * x**4\
            - 3078 * x**3 +  590 * x**2 +   85 * x

    # 年齢の端数を日数に変換
    d = 365.2425 * (y - int(y))

    # 年齢を年数と日数で表す
    age = relativedelta(years = +int(y), days = +int(d))

    # 生年月日を算出
    birth = d1 - age

    return birth, age.years

 rand_age() で女性の年齢と生年月日をランダム生成してみます。

np.random.seed(10)

# 生年月日と年齢をランダムに生成
birth, age = rand_age("f")

print(birth)
print(age)
1947-03-05
72