血液型をランダムに生成する
ある調査によると、日本人の ABO式血液型 は、おおよそ
O 型:32%
A 型:37%
B 型:22%
AB 型:9%
のような比率で分布していることが知られています (ただし地域差が大きいので、標本によって比率は僅かに異なるようです)。この比率にしたがって、血液型をランダムに決定する関数を定義してみましょう。
numpy.random.choice() は第 1 引数にオブジェクトのリスト、オプション引数 p に第 1 引数の各要素に対応する確率リストを渡すと、設定した確率で各オブジェクトを返します。
# PYTHON_BLOOD_TYPE
# In[1]
import numpy as np
# 血液型をランダムに決定する関数
def rand_blood(p=[0.32, 0.37, 0.22, 0.09]):
# 血液型(blood type)リスト
bt_list = ["O", "A", "B", "AB"]
# O,A,B,AB型の比率
prob = p
# 血液型をランダムに決定
x = np.random.choice(bt_list, p=prob)
return x
引数 p にはデフォルトの比率を与えていますが、ユーザーが任意の比率を与えることもできます。念のために 10000 個の血液型を生成して発生比率を確認しておきます。
# In[2]
# 血液型(blood type)リスト
bt_list = ["O", "A", "B", "AB"]
# 初期化されていない配列を用意
# データ型(dtype)は最大長2の文字列
arr = np.empty(10000, dtype="U2")
# 乱数シードを設定
np.random.seed(0)
# 血液型を10000個発生
for i in range(10000):
arr[i] = rand_blood()
# 型ごとの個数をカウントして比率を計算
for j in bt_list:
ct = arr[arr==j].size/10000
print("{0}:{1}".format(j, ct))
# O:0.3261
# A:0.3657
# B:0.218
# AB:0.0902
両親の血液型から子供の血液型を決定する
両親の 血液型の組合わせ にしたがって子供の血液型の比率を計算すると、以下の表のようになることが知られています。
たとえば、両親の血液型が O, A であった場合、生まれてくる子供の血液型は、O 型になる確率が 40%, A 型になる確率が 60% であり、それ以外の血液型になる確率は 0% です。
任意の血液型の組み合わせを渡して、子供の血液型を正しい確率で決定する関数を作成してみましょう。両親の血液型を key, 子供の血液型を value とする辞書を作成することを考えるかもしれません。しかし、Python 本体のディクショナリは動作が重いので、やはり NumPy の配列を利用することにします。
ここでも numpy.random.choice() を使うので、最初に両親の血液型の組合わせと、対応する子供の血液型の確率リストを配列に格納しておきます。ただし、たとえば (A, O) と (O, A) のように、両親の血液型の順序が異なっても対応する確率リストは同じなので、重複処理を避けるために両親の血液型は set型で格納しておきます。コードが見やすいように、AB 型は Z で置き換えます。
# PYTHON_BLOOD_TYPE_COMBINATION
# In[1]
import numpy as np
# 両親の血液型の組合わせを配列に格納(ABはZに置き換えてある)
type_parents = np.array([{"O", "O"}, {"O", "A"}, {"O", "B"},
{"O", "Z"}, {"A", "A"}, {"A", "B"},
{"A", "Z"}, {"B", "B"}, {"B", "Z"},
{"Z", "Z"}])
# 子供の血液型の確率
type_child = np.array([[1.00, 0.00, 0.00, 0.00],
[0.40, 0.60, 0.00, 0.00],
[0.43, 0.00, 0.57, 0.00],
[0.00, 0.50, 0.50, 0.00],
[0.16, 0.84, 0.00, 0.00],
[0.17, 0.26, 0.23, 0.34],
[0.00, 0.50, 0.20, 0.30],
[0.19, 0.00, 0.81, 0.00],
[0.00, 0.22, 0.50, 0.28],
[0.00, 0.25, 0.25, 0.50]])
set型は要素の順が異なっても、同じオブジェクトとして扱われます:
{"A", "O"} == {"O", "A"}
# True
データを npzファイルに保存しておきます。
# In[2]
# npzファイルに配列を保存
np.savez('blood_type', x = type_parents, y = type_child)
フォルダに blood_type.npz ファイルがあれば、今後はコード BT_02-1 は必要ありません (消しても大丈夫です)。次回以降に Jupyter を起動するときのために、blood_type.npz からデータを読み込むコードを載せておきます。
# In[3]
# npzファイルを読み込む
data = np.load("blood_type.npz")
# 変数に配列を格納
type_parents = data["x"]
type_child = data["y"]
type_parents は type_child の特定の行を参照するためのインデックスを得るために使います。numpy.where() を使うと、たとえば {"Z", "O"} のインデックス配列を取得できます。
# In[4]
# {"A","B"}のインデックス配列を取得
idx_arr = np.where(type_parents == {"A", "B"})
print(idx_arr)
# (array([5], dtype=int32),)
戻り値が一次元配列とデータ型 (dtype) のタプルなので、idx_arr[0][0] でインデックスを取り出せます。
# In[5]
# idx_arrからインデックスを取り出す
idx = idx_arr[0][0]
print(idx)
# 5
このインデックスを使って、type_child から A 型と B 型の両親から生まれる子供の血液型の確率リストを取得できます。
# In[6]
# {"A","B"}の確率を取得
prop = type_child[idx]
print(prop)
# [0.17 0.26 0.23 0.34]
以上を踏まえて、両親の血液型を受け取って子供の血液型をランダムに生成する inherit_blood() 関数を定義してみます。血液型の選択には上で定義した rand_blood() 関数を使います。
# In[7]
def inherit_blood(b1, b2):
# "AB" が渡されたら "Z" に置き換える
if b1 == "AB":
b1 = "Z"
if b2 == "AB":
b2 = "Z"
# インデックス配列を取得
idx_arr = np.where(type_parents == {b1, b2})
# idx_arrからインデックス番号を取り出す
idx = idx_arr[0][0]
# 子供の血液型の確率を取得
prop = type_child[idx]
return rand_blood(p=prop)
A 型と B 型の両親から生まれる子供の血液型を生成してみます。
# In[8]
x = []
for i in range(10):
b = inherit_blood("A", "B")
x.append(b)
print(x)
# ['O', 'AB', 'O', 'AB', 'A', 'B', 'O', 'AB', 'A', 'B']
ChatGPTに血液型の組み合わせについて質問しました
[Q] 両親の血液型の組み合わせで、子供の血液型がどのように決まるのか、手短に教えてください。
[A] 両親の血液型によって、子供の血液型が決まります。人間の血液型はA、B、AB、Oの4つに分類されます。
両親がともにA型の場合、子供がA型またはO型になる可能性があります。
両親がともにB型の場合、子供がB型またはO型になる可能性があります。
両親がA型とB型の場合、子供がA型、B型、AB型、またはO型になる可能性があります。
両親がA型またはB型とO型の場合、子供がA型またはB型になる可能性があります。
両親がともにAB型の場合、子供がAB型になる可能性があります。
両親の血液型によって、子供がどのような血液型になるかを予測することができますが、稀なケースでは予測が外れることもあります。