二項分佈的原理、應用及Python實戰

二項分佈是概率統計中非常基礎、非常實用的一種分佈,可以說它在我們的生活中無所不在。它說明了這樣一種現象:在給定的試驗次數中,某一結果會發生多少次。

比如:

這個月有多少天會刮北風?

今年有多少天會下雨?

經過一個路口100次,有多少次會是綠燈?

一年之中會有多少次出門就見狗?



一、伯努利分佈

伯努利分佈是二項分佈的基礎,它只有兩種狀態,比如拋硬幣的時候,結果只有正面和反面兩種情況,且兩種情況的概率之和為1。也就是說,當我們給定​正面朝上的概率的時候,這個分佈的一切就都確定了。

我們以0和1來標識這兩種可能的結果,那麼其概率函數為:


二項分佈的原理、應用及Python實戰

那麼其期望值為:


二項分佈的原理、應用及Python實戰

其方差為:

二項分佈的原理、應用及Python實戰

二、排列組合

1. 排列

從n個對象中有序地挑選出r個對象,我們稱之為排列,我們用以下公式統計其可能產生的排列數:


二項分佈的原理、應用及Python實戰

2. 組合

考慮另一種情況,仍然是從n個對象中抽取​r個對象,但是這次我們不考慮其順序,這種過程我們稱之為組合。我們用以下公式統計其可能產生的組合數:


二項分佈的原理、應用及Python實戰

可以看出,這是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次的場景發生的概率均為:


二項分佈的原理、應用及Python實戰

然後,我們需要考慮結果為1的次數剛好為k的情況有多少種。很明顯,這就是一個伯努利試驗的組合問題,n次實驗中有k次結果為1的情況共有“n選k”種,兩者相乘就是該事件發生的概率。因此:


二項分佈的原理、應用及Python實戰

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>


二項分佈的原理、應用及Python實戰


或者我們使用交互式的繪圖庫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>


二項分佈的原理、應用及Python實戰

可以看到,plotly實現的效果更加靚麗,且額外支持了動態交互,在這裡我就選擇把p=0.8這條線隱藏了起來。

四、一個利用極大似然估計求二項分佈概率參數的例子

我們現在想象一種情況,有一枚分佈不太均勻的硬幣,每次拋向空中後,落地為正面的概率為p,任意兩次實驗之間相互獨立。現在我們做了4次實驗,其中有三次正面朝上,那麼請問p的值為多少?

我們之前曾經提到過極大似然估計,在這裡我們用同樣的思路去估計p​的取值。極大似然估計的思想就是尋找一個參數,使得當前結果發生的概率最大,那麼我們先定義出來當前結果發生的概率公式:


二項分佈的原理、應用及Python實戰

對其求導並使導數為0,有:


二項分佈的原理、應用及Python實戰

可得,當p=0.75時,P(X=3)=0.422達到最大(另一個解​p=0顯然不可能,因為硬幣朝上已經發生了,並不是“不可能事件”;另外考慮不同區間導數的取值也可以得到答案)。


分享到:


相關文章: