二項分佈是概率統計中非常基礎、非常實用的一種分佈,可以說它在我們的生活中無所不在。它說明了這樣一種現象:在給定的試驗次數中,某一結果會發生多少次。
比如:
這個月有多少天會刮北風?
今年有多少天會下雨?
經過一個路口100次,有多少次會是綠燈?
一年之中會有多少次出門就見狗?
一、伯努利分佈
伯努利分佈是二項分佈的基礎,它只有兩種狀態,比如拋硬幣的時候,結果只有正面和反面兩種情況,且兩種情況的概率之和為1。也就是說,當我們給定正面朝上的概率的時候,這個分佈的一切就都確定了。
我們以0和1來標識這兩種可能的結果,那麼其概率函數為:
那麼其期望值為:
其方差為:
二、排列組合
1. 排列
從n個對象中有序地挑選出r個對象,我們稱之為排列,我們用以下公式統計其可能產生的排列數:
2. 組合
考慮另一種情況,仍然是從n個對象中抽取r個對象,但是這次我們不考慮其順序,這種過程我們稱之為組合。我們用以下公式統計其可能產生的組合數:
可以看出,這是n選r的排列數除以r的排列數。上述公式又被稱作二項係數,通常用“n選r”表示。
3. Python計算
那麼接下來我們用Python來寫一個函數,用來計算不同參數下的排列與組合的數量。在排列組合的計算中,我們可能會輸入兩個參數:總樣本量n、需要抽取的樣本數k。
那麼我們就定義如下函數:
<code>from functools import reduce def PC(n, k): """ 計算並返回排列組合數 """ # 非法輸入返回空 if n <= 0 or k < 0 or n < k: print('Wrong Input!') return None # k為0時,排列組合的情況恆為1 if k == 0: return 1, 1 # 生成正序及倒序的序列 series_asc = list(range(1, n+1)) series_desc = sorted(series_asc, reverse=True) # 排列 permutation = reduce(lambda x, y: x*y, series_desc[:k]) # 組合 perm2 = reduce(lambda x, y: x*y, series_asc[:k]) combination = int(permutation / perm2) return permutation, combination/<code>
隨手測試幾個:
<code>for n in range(1, 5): for k in range(1, 3): print('-'*10) print(n, k) print(PC(n, k))/<code>
結果是正確的:
<code>---------- 1 1 (1, 1) ---------- 1 2 Wrong Input! None ---------- 2 1 (2, 2) ---------- 2 2 (2, 1) ---------- 3 1 (3, 3) ---------- 3 2 (6, 3) ---------- 4 1 (4, 4) ---------- 4 2 (12, 6)/<code>
三、二項分佈
回顧伯努利分佈的情況:一次實驗只有可能有兩種結果,分別用0和1來表示,其中結果1發生的概率為p。那麼在n次獨立實驗中,不考慮順序的情況下,結果1出現k次的概率是多少?
首先,因為n次實驗相互獨立,所以根據乘法定律,任何一種結果1出現k次的場景發生的概率均為:
然後,我們需要考慮結果為1的次數剛好為k的情況有多少種。很明顯,這就是一個伯努利試驗的組合問題,n次實驗中有k次結果為1的情況共有“n選k”種,兩者相乘就是該事件發生的概率。因此:
Python計算
那麼我們來用Python實現一個計算二項分佈概率的小工具,在這裡,我們的輸入參數包含總試驗次數n、正樣本發生的次數k以及正樣本發生的概率p:
<code>def binominal_prob(n, k, p): """ 計算並返回二項分佈中某結果發生的概率 """ # 任一k次成功的序列出現的概率 p_base = p ** k * (1-p) ** (n-k) # n次試驗中k次成功的組合數 # 直接用上邊我們編寫的排列組合函數來求解 combination = PC(n, k)[1] p_result = p_base * combination return p_result/<code>
那麼接下來,我們利用我們剛剛寫好的小工具,來看一下在10次試驗中,不同的概率對應的二項分佈是什麼樣的。
<code>probs = [binominal_prob(10, i, 0.5) for i in range(11)]/<code>
我們將結果畫出來看看:
<code>%matplotlib inline import numpy as np import matplotlib.pyplot as plt import seaborn as sns sns.set() probs = [round(i/10,1) for i in range(1, 10)] n = 20 plt.figure(figsize=(16, 6)) for p in probs: dist_probs = [binominal_prob(n, i, p) for i in range(n+1)] plt.plot(range(n+1), dist_probs, label='p={0}'.format(p)) plt.legend() plt.title('Binominal Distributions of Different P value') plt.savefig('binominal.jpg') plt.show()/<code>
或者我們使用交互式的繪圖庫plotly來嘗試同樣的事情:
<code>import plotly.graph_objects as go probs = [round(i/10,1) for i in range(1, 10)] n = 20 fig = go.Figure() for p in probs: dist_probs = [binominal_prob(n, i, p) for i in range(n+1)] fig.add_trace(go.Scatter( x=list(range(n+1)), y=dist_probs, name='p={0}'.format(p) )) fig.show()/<code>
可以看到,plotly實現的效果更加靚麗,且額外支持了動態交互,在這裡我就選擇把p=0.8這條線隱藏了起來。
四、一個利用極大似然估計求二項分佈概率參數的例子
我們現在想象一種情況,有一枚分佈不太均勻的硬幣,每次拋向空中後,落地為正面的概率為p,任意兩次實驗之間相互獨立。現在我們做了4次實驗,其中有三次正面朝上,那麼請問p的值為多少?
我們之前曾經提到過極大似然估計,在這裡我們用同樣的思路去估計p的取值。極大似然估計的思想就是尋找一個參數,使得當前結果發生的概率最大,那麼我們先定義出來當前結果發生的概率公式:
對其求導並使導數為0,有:
可得,當p=0.75時,P(X=3)=0.422達到最大(另一個解p=0顯然不可能,因為硬幣朝上已經發生了,並不是“不可能事件”;另外考慮不同區間導數的取值也可以得到答案)。