Chương 3: Hàm phân bố lũy tích

Trở về Mục lục cuốn sách

Nghịch lý về số sinh viên trong lớp

Trong nhiều trường đại học Hoa Kỳ, tỉ lệ sinh viên so với giảng viên đều vào khoảng 10:1. Nhưng sinh viên thường ngạc nhiên khi biết rằng lớp học của họ trung bình đều hơn 10 sinh viên. Có hai lý do giải thích sự khác biệt này:

  • Sinh viên nói chung đều học 4–5 lớp mỗi kỳ, trong khi giáo sư thường chỉ dạy 1 hoặc 2 lớp.
  • Số sinh viên may mắn được học lớp vắng thì ít, còn sinh viên phải học lớp lớn thì (a hèm!) có rất nhiều.

Điều thứ nhất thì rất dễ thấy (nó đã được nhắc đến ít nhất một lần); điều thứ hai thì tinh vi hơn. Vì vậy ta hãy xét một ví dụ. Chẳng hạn một trường có 65 lớp học trong một kì, với phân bố số học viên mỗi lớp như sau:

số SV     số lớp
 5- 9          8 
10-14          8 
15-19         14 
20-24          4 
25-29          6 
30-34         12 
35-39          8 
40-44          3 
45-49          2

Nếu bạn hỏi hiệu trưởng về số sinh viên trung bình trong một lớp, ông ta sẽ dựng một đường PMF, tính trung bình, và báo rằng số sinh viên trung bình trong mỗi lớp bằng 24.

Nhưng nếu bạn điều tra một nhóm sinh viên, hỏi xem có bao nhiêu sinh viên trong lớp họ, rồi tính trung bình, bạn sẽ nghĩ rằng số sinh viên trung bình sẽ cao hơn.

Hãy dựng đường PMF từ những số liệu trên rồi tính trung bình theo cách nhìn nhận của hiệu trưởng. Vì số liệu đã được chia vào các ngăn, bạn có thể dùng một điểm giữa cho mỗi ngăn.

Bây giờ hãy tính phân bố của số sinh viên từng lớp theo cách nhìn nhận của sinh viên rồi tính trị trung bình.

Giả sử rằng bạn muốn tìm phân bố của số sinh viên từng lớp của một trường, nhưng không thể có số liệu chính xác từ hiệu trưởng. Một cách khác là chọn một mẫu sinh viên ngẫu nhiên rồi hỏi họ về số sinh viên từng lớp mà họ theo học. Sau đó bạn có thể tính PMF từ số liệu thu được qua trả lời của họ.

Kết quả sẽ thiên lệch vì những lớp học lớn sẽ được phản ánh lặp lại trong mẫu, nhưng bạn vẫn có thể ước tính được phân bố thực sự của số sinh viên mỗi lớp bằng cách áp dụng một phép chuyển đổi thích hợp đối với dạng phân bố đã quan sát thấy.

Hãy viết một hàm có tên UnbiasPmf để nhận vào PMF của các giá trị quan sát được và trả lại một đối tượng Pmf mới để ước tính phân bố của số sinh viên mỗi lớp.

Bạn có thể tải về một lời giải cho bài này từ http://thinkstats.com/class_size.py.

Trong đa số các cuộc thi chạy, mọi người đều xuất phát cùng lúc. Nếu bạn chạy nhanh, thường bạn sẽ vượt được nhiều người ngay đầu cuộc đua, nhưng rồi sau một vài dặm đường, mọi người xung quanh bạn sẽ chạy với cùng tốc độ.

Khi lần đầu tiên tham gia cuộc chạy tiếp sức đường dài (209 dặm), tôi nhận thấy một hiện tượng lạ: khi mình vượt một đối thủ khác, tôi thường nhanh hơn rất nhiều, và khi một đối thủ khác vượt tôi, người ta cũng thương nhanh hơn tôi rất nhiều.

Ban đầu tôi cứ nghĩ rằng phân bố tốc độ có lẽ sẽ chứa hai số đông; nghĩa là có nhiều người chạy chậm và nhiều người chạy nhanh, còn ít người chạy cùng tốc độ với tôi.

Nhưng sau đó tôi nhận thấy rằng đã bị thiên lệch trong việc lựa chọn. Cuộc đua khác thường ở hai điểm: nó xuât phát không đều, các đội xuất phát vào các thời điểm khác nhau; ngoài ra nhiều đội gồm những người có sức chạy không đều nhau.

Kết quả là, các vận động viên sẽ tản ra trong cuộc đua với ít mối tương quan giữa tốc độ và vị trí. Khi tôi bắt đầu phiên chạy, những người chạy gần tôi đều (khá giống) một mẫu ngẫu nhiên của tổng thể các vận động viên trong cuộc đua.

Vậy thì sự thiên lệch này từ đâu mà có? Trong khi tôi chạy thi, cơ may vượt một đối thủ khác, hoặc bị vượt, đều tỉ lệ với hiệu số tốc độ giữa hai người. Để thấy được tại sao, bạn hãy hình dung về hai giới hạn như sau. Nếu có ai đó chạy nhanh bằng tôi thì chúng tôi không vượt được nhau. Nếu ai đó nhanh đến mức có thể chạy hết chặng đường đua trong khi tôi vẫn đang chạy thì chắc chắn họ vượt được tôi.

Hãy viết một hàm tên là BiasPmf để nhận vào một Pmf biểu thị cho phân bố thực sự của tốc độ người chạy, và một tốc độ của một người quan sát đang chạy, rồi trả về một Pmf mới biểu thị cho phân bố tốc độ tương đối của những người chạy so với người quan sát.

Để kiểm tra hàm vừa viết, hãy lấy một phân bố các tốc độ trong một cuộc đua bình thường (không phải là tiếp sức). Tôi đã viết một chương trình có nhiệm vụ đọc vào kết quả cuộc đua James Joyce Ramble 10K ở Dedham MA rồi chuyển đổi tốc độ từng người chạy sang dặm/giờ. Hãy tải về chương trình này từ http://thinkstats.com/relay.py. Chạy nó rồi xem PMF các tốc độ.

Bây giờ hãy tính phân bố các tốc độ mà bạn sẽ quan sát thấy nếu bạn chạy trong cuộc thi tiếp sức với tốc độ 7,5 dặm/giờ cùng nhóm người chạy nói trên. Bạn có thể tải về một lời giải từ http://thinkstats.com/relay_soln.py

Sự hạn chế của các PMF

PMF sẽ phát huy tác dụng nếu có ít các giá trị. Nhưng khi số các giá trị nhiều lên, xác suất ứng với mỗi giá trị sẽ nhỏ đi và hiệu ứng của nhiễu ngẫu nhiên sẽ tăng lên.

Chẳng hạn, chúng ta có thể quan tâm đến phân bố cân nặng trẻ sơ sinh. Trong số liệu NSFG, biến totalwgt_oz ghi lại các cân nặng trẻ sơ sinh tính theo ounce. Hình dưới đây cho thấy PMF của các giá trị này cho trẻ đầu lòng và các trẻ sinh sau.

PMF của cân nặng trẻ sơ sinh. Hình vẽ cho thấy một hạn chế của các PMF: chúng rất khó so sánh với nhau. (nsfg_birthwgt_pm)

Nói chung, các phân bố này đều trông giống hình “quả chuông” quen thuộc, với nhiều giá trị gần số trung bình và ít giá trị quá lớn hay quá nhỏ.

Nhưng có những phần của hình vẽ này rất khó diễn giải. Có nhiều đỉnh nhọn và chỗ trũng, và một số khác biệt trông thấy giữa các phân bố. Thật khó nói được những đặc điểm nào trong số này là có ý nghĩa. Đồng thời, cũng khó nhận thấy những đặc điểm tổng thể; chẳng hạn, bạn nghĩ rằng phân bố nào có trị trung bình cao hơn?

Vấn đề này có thể được giảm nhẹ bằng việc chia số liệu vào các ngăn; nghĩa là chia miền giá trị thành những khoảng không trùng nhau và đếm số giá trị rơi vào mỗi ngăn. Việc chia ngăn có thể có ích, nhưng để chọn kích thước ngăn thích hợp sẽ rất mẹo mực. Nếu ngăn đủ lớn để làm trơn các nhiễu động thì nó cũng có thể làm trơn những thông tin có ích.

Một cách khác để tránh vấn đề trên là dùng hàm phân bố lũy tích, hay CDF (cumulative distribution function). Nhưng trước khi làm được điều này, ta phải nói về số phần trăm.

Số phần trăm

Nếu bạn đã từng làm một bài thi tiêu chuẩn, có thể bạn sẽ được chấm điểm dưới dạng điểm gốc kèm theo một hạng phần trăm. Ở đây, hạng phần trăm là tỉ lệ số người có điểm thấp hơn bạn (hoặc bằng). Vì vậy, nếu bạn đứng “hạng 90 phần trăm”, thì bạn đã làm bài tốt bằng hoặc hơn 90% số người đã làm bài thi.

Sau đây là cách để bạn có thể tính hạng phần trăm của một giá trị có tên your_score, so với các giá trị trong dãy scores:

def PercentileRank(scores, your_score): 
    count = 0 
    for score in scores: 
        if score <= your_score: 
            count += 1 

    percentile_rank = 100.0 * count / len(scores) 
    return percentile_rank

Chẳng hạn, nếu các giá trị trong dãy là 55, 66, 77, 88 và 99, bạn đạt điểm 88, thì hạng phần trăm của bạn sẽ là 100 * 4 / 5 tức là bằng 80.

Nếu cho trước một giá trị, thì sẽ tìm được hạng phần trăm của nó; còn làm ngược lại thì hơi khó hơn. Nếu bạn được cho một hạng phần trăm và muốn tìm giá trị tương ứng, một cách làm là sắp xếp các giá trị và tìm lấy giá trị mà bạn muốn:

def Percentile(scores, percentile_rank): 
    scores.sort() 
    for score in scores: 
        if PercentileRank(scores, score) >= percentile_rank: 
            return score 

Kết quả của phép tính này là một số phần trăm. Chẳng hạn, số phần trăm thứ 50 là giá trị ứng với hạng phần trăm 50. Trong phân bố điểm thi, số phần trăm thứ 50 là 77.

Đoạn chương trình thực hiện Percentile như trên không được hiệu quả. Một cách hay hơn là dùng hạng phần trăm để tính chỉ số của số phần trăm tương ứng. Hãy viết một phiên bản Percentile có dùng thuật toán này.

Bạn có thể tải về một lời giải từ http://thinkstats.com/score_example.py.

Tùy chọn: Nếu bạn chỉ muốn tính một số phần trăm, thì cách sắp xếp các điểm thi sẽ không hiệu quả. Một cách làm hay hơn là thuật toán lựa chọn, mà bạn có thể đọc từ http://wikipedia.org/wiki/Selection_algorithm.

Hãy viết (hoặc tìm) một đoạn chương trình thực hiện thuật toán lựa chọn và dùng nó để viết một phiên bản hiệu quả cho Percentile.

Hàm phân bố lũy tích

Bây giờ khi đã hiểu được số phần trăm, chúng ta sẵn sàng xét đến hàm phân bố lũy tích (cumulative distribution function, CDF). CDF là một hàm với ánh xạ từ các giá trị đến hạng phần trăm của chúng trong một phân bố.

CDF là một hàm theo x, trong đó x là một giá trị bất kì có thể xuất hiện trong phân bố. Để tính CDF(x) cho một giá trị cụ thể của x, ta cần tính tỉ lệ của các giá trị trong mẫu mà nhỏ hơn (hoặc bằng) x.

Sau đây là một hàm như vậy, nhận vào một mẫu t, và một giá trị, x:

def Cdf(t, x): 
    count = 0.0 
    for value in t: 
        if value <= x: 
            count += 1.0 

    prob = count / len(t) 
    return prob

Hàm này sẽ trông giống; nó gần như y hệt PercentileRank, chỉ khác là kết quả lại là một tần suất nằm trong khoảng từ 0–1 thay vì một hạng phần trăm trong khoảng từ 0–100.

Lấy ví dụ, chẳng hạn có một mẫu gồm các giá trị sau {1, 2, 2, 3, 5}. Dưới đây là một số giá trị lấy từ CDF của nó:

CDF(0) = 0
CDF(1) = 0.2
CDF(2) = 0.6
CDF(3) = 0.8
CDF(4) = 0.8
CDF(5) = 1

Ta có thể lượng giá CDF cho bất kì giá trị x bất kì, chứ không chỉ các giá trị có xuất hiện trong mẫu. Nếu x nhỏ hơn giá trị nhỏ nhất trong mẫu, CDF(x) bằng 0. Nếu x lớn hơn giá trị lớn nhất trong mẫu, CDF(x) bằng 1.

Ví dụ về một CDF.

Hình trên biểu diễn cho CDF này. CDF của một mẫu là một hàm bậc thang. Trong chương tiếp theo ta sẽ thấy các phân bố có CDF là các hàm liên tục.

Biểu diễn CDF

Tôi đã viết một module có tên Cdf trong đó có một lớp tên là Cdf để biểu diễn các CDF. Bạn có thể đọc tài liệu hướng dẫn cách dùng module này tại http://thinkstats.com/Cdf.html và tải module về từ http://thinkstats.com/Cdf.py.

Cdf được viết trong chương trình với hai danh sách đã sắp xếp: xs để chứa các giá trị, và ps để chứa các tần suất. Những phương thức quan trọng nhất mà Cdf cung cấp là:

Prob(x):
Với giá trị x cho trước, tính xác suất p = CDF(x).
Value(p):
Với xác suất p cho trước, tính giá trị x tương ứng; nghĩa là CDF nghịch đảo của p.

xsps đều đã được sắp xếp nên các phương thức trên có thể dùng thuật toán phân đôi, vốn rất hiệu quả. Thời gian chạy tỉ lệ với loga của số các giá trị; xem http://wikipedia.org/wiki/Time_complexity.

Cdf cũng cung cấp Render, vốn trả lại 2 danh sách, xsps, phù hợp để vẽ CDF. Vì CDF là một hàm bậc thang nên các danh sách này có hai phần tử ứng với mỗi giá trị duy nhất trong phân bố.

Module Cdf cung cấp một số hàm để tạo ra các Cdf, bao gồm MakeCdfFromList, vốn nhận vào một dãy các giá trị rồi trả lại Cdf của chúng.

Sau cùng, myplot.py có các hàm tên là CdfCdfs để vẽ đồ thị các Cdf dưới dạng đường.

Hãy tải về Cdf.pyrelay.py (xem Bài tập relay) rồi phát sinh một đồ thị biểu diễn CDF của các tốc độ chạy. Cách nào cho bạn hình dung tốt hơn hình dạng của phân bố, PMF hay CDF? Bạn có thể tải về một lời giải từ http://thinkstats.com/relay_cdf.py.

Quay trở về số liệu điều tra

Hình dưới đây biểu diễn các CDF cân nặng trẻ sơ sinh đối với trẻ đầu lòng và trẻ sinh sau trong bộ số liệu NSFG.

CDF của cân nặng trẻ sơ sinh. (nsfg_birthwgt_cdf)

Hình vẽ này làm cho hình dạng của các phân bố, và sự khác biệt giữa chúng, trở nên rõ ràng hơn nhiều. Ta có thể thấy được trên suốt phân bố, trẻ đầu lòng luôn nhẹ hơn một chút, sự khác biệt lớn hơn ở trên giá trị trung bình.

Lúc mới sinh bạn nặng bao nhiêu? Nếu bạn không biết, hãy hỏi mẹ hoặc một người khác biết thông tin này. Dùng kho số liệu (tất cả ca sinh thành công) để tính phân bố cân nặng trẻ sơ sinh rồi từ đó tính hạng phần trăm của bạn. Nếu bạn là con đầu, hãy tính hạng phần trăm trong phân bố dành cho trẻ đầu lòng. Còn nếu không thì dùng dạng phân bố của trẻ sinh sau. Sự khác biệt giữa hai hạng phần trăm của cân nặng bạn giữa hai phân bố là bao nhiêu?

Giả sử là bạn và những người cùng lớp đều tính hạng phần trăm của cân nặng sơ sinh mỗi người rồi sau đó tính CDF của các hạng phần trăm này. Bạn sẽ chờ đợi kết quả CDF có dạng như thế nào? Gợi ý: bạn nghĩ rằng tỉ lệ bao nhiêu trong lớp sẽ cao hơn số trung vị?

Phân bố theo điều kiện

Phân bố theo điều kiện là phân bố của một tập con số liệu được chọn lựa theo một điều kiện nào đó.

Chẳng hạn, nếu bạn nặng hơn mức trung bình, nhưng cao hơn nhiều so với mức trung bình, thì bạn sẽ tương đối nhẹ so với chiều cao hiện có. Sau đây là cách làm cho lời khẳng định đó chuẩn xác hơn.

  1. Chọn ra một nhóm người có cùng chiều cao với bạn (hiểu theo nghĩa xấp xỉ trong khoảng nào đó).
  2. Tìm CDF về cân nặng của những người đó.
  3. Tính hạng phần trăm cân nặng của bạn trong phân bố này.

Hạng phần trăm rất có ích trong việc so sánh những kết quả đo từ các phép thử khác nhau, hoặc những phép thử đối với các nhóm khác nhau.

Chẳng hạn, những người thi chạy thường được nhóm theo tuổi tác và giới tính. Để so sánh những người trong các nhóm khác nhau, bạn có thể chuyển đổi thời gian chạy về hạng phần trăm.

Gần đây tôi có thi chạy giải James Joyce Ramble 10K ở Dedham MA. Kết quả được đăng trên http://coolrunning.com/results/10/ma/Apr25_27thAn_set1.shtml. Hãy đến trang đó và tìm thời gian chạy của tôi. Tôi xếp thứ 97 trong tất cả là 1633 người tham gia, vậy hạng phần trăm của tôi là bào nhiêu?

Ở nhóm độ tuổi của mình (M4049 có nghĩa là “nam giới từ 40 đến 49 tuổi”), tôi xếp thứ 26 trong tổng số 256. Hạng phần trăm của tôi là bao nhiêu trong nhóm này?

Nếu tôi vẫn chạy trong 10 năm tới (và tôi hi vọng là vẫn chạy được), tôi sẽ được xếp vào nhóm M5059. Giả dụ rằng hạng phần trăm của tôi trong nhóm vẫn không đổi, thì tôi sẽ chạy chậm đi bao nhiêu?

Tôi duy trì ganh đua lành mạnh với một cô sinh viên trong nhóm F2039. Cô ấy sẽ phải chạy nhanh mức nào trong cuộc thi 10K sắp tới để vượt tôi về hạng phần trăm?

Số ngẫu nhiên

CDF rất có ích trong việc phát sinh ra các số ngẫu nhiên từ một phân bố cho trước. Sau đây là cách làm:

  • Chọn một xác suất ngẫu nhiên trong khoảng từ 0–1.
  • Dùng Cdf.Value để tìm trong phân bố giá trị tương ứng với xác suất đã chọn.

Có thể không hiển nhiên là tại sao cách này lại cho kết quả, nhưng vì sẽ dễ thực hiện hơn là giải thích, nên ta hãy làm thử.

Hãy viết một hàm có tên Sample, để nhận vào một Cdf và một số nguyên, , rồi trả lại một danh sách giá trị chọn ngẫu nhiên từ Cdf đó. Gợi ý: hãy dùng random.random. Bạn sẽ tìm thấy một lời giải bài này ở Cdf.py.

Dùng phân bố cân nặng trẻ sơ sinh từ NSFG, hãy phát sinh một mẫu ngẫu nhiên gồm 1000 phần tử. Tính CDF của mẫu đó. Vẽ biểu đồ cho thấy cả CDF ban đầu và CDF của mẫu ngẫu nhiên. Với các giá lớn, hai phân bố sẽ phải giống nhau.

Quá trình này, phát sinh một mẫu ngẫu nhiên dựa trên một mẫu đo đạc, được gọi là lấy mẫu lại.

Có hai cách lấy mẫu từ một tổng thể: có thay thế và không thay thế. Nếu bạn hình dung lấy những viên bi từ một lọ1, “có thay thế” nghĩa là sau khi chọn bi lại bỏ vào lọ (rồi trộn lẫn), để cho tổng thể không thay đổi sau mỗi lần lấy bi. “Không thay thế” nghĩa là mỗi viên bi chỉ có thể lấy ra một lần, vì vậy tổng thể còn lại sẽ khác đi sau mỗi lần lấy bi.

Trong Python, việc lấy mẫu có thay thế được thực hiện bằng random.random để chọn một hạng phần trăm, hoặc random.choice để chọn một phần tử từ một dãy. Việc lấy mẫu không thay thế được thực hiện với random.sample.

Những số được phát sinh bởi random.random được thiết kế để phân bố đều trong khoảng từ 0 đến 1; nghĩa là mỗi giá trị trong khoảng cần phải có cùng xác suất.

Hãy phát sinh 1000 số bằng random.random rồi vẽ các đồ thị PMF và CDF của các số này. Bạn có thể nói rằng chúng phân bố đều không?

Bạn có thể tìm hiểu về phân bố đều ở http://wikipedia.org/wiki/Uniform_distribution_(discrete).

Quay về đặc trưng thống kê

Một khi bạn đã tính được CDF, sẽ dễ dàng tính được các đặc trưng thống kê khác. Số trung vị chính là số phần trăm thứ 502. Các số phần trăm thứ 25 và 75 thường được dùng để kiểm tra xem liệu một phân bố có đối xứng không, và hiệu số giữa chúng, vốn được gọi là khoảng tứ phân vị, để đo độ phân tán.

Hãy viết một hàm có tên Median nhận vào một Cdf rồi tính số trung vị, và một hàm có tên Interquartile để tính khoảng tứ phân vị.

Tính các số phần trăm thứ 25, 50, và 75 từ CDF cân nặng trẻ sơ sinh. Những giá trị này có dấu hiệu cho thấy rằng phân bố có tính đối xứng không?

Thuật ngữ

hạng phần trăm:
Số phần trăm của những giá trị trong một phân bố nhỏ hơn hoặc bằng một giá trị cho trước.
CDF:
Hàm phân bố lũy tích, một hàm ánh xạ từ các giá trị đến hạng phần trăm của chúng.
số phần trăm:
Giá trị ứng với một hạng phần trăm cho trước.
phân bố theo điều kiện:
Phân bố được tính dựa trên giải thiết rằng một điều kiện nào đó được thỏa mãn.
lấy mẫu lại:
Quá trình phát sinh một mẫu ngẫu nhiên từ một phân bố đã được tính từ một mẫu.
thay thế:
Trong quá trình lấy mẫu, “có thay thế” để chỉ rằng tổng thể vẫn giữ nguyên giữa các lần lấy mẫu. “Không thay thế” để chỉ rằng mỗi phần tử chỉ có thể được chọn một lần.
khoảng tứ phân vị:
Độ đo của sự phân tán, tính bằng hiệu số giữa các số phần trăm thứ 75 và 25.

  1. “Bi trong lọ” là một mô hình chuẩn cho các quá trình lấy mẫu ngẫu nhiên (xem http://wikipedia.org/wiki/Urn_problem).
  2. Bạn có thể đã thấy các định nghĩa khác về số trung vị. Đặc biệt, có tài liệu nói rằng nếu bạn có mẫu chứa một số chẵn các phần tử thì trung vị là trung bình cộng của hai phần tử trung tâm. Trường hợp đặc biệt này thật không cần thiết, và nó có một hiệu ứng kì quặc là phát sinh ra một giá trị không nằm trong mẫu. Đến giờ tôi chỉ cần biết rằng trung vị là số phần trăm thứ 50. Chấm hết.

1 Phản hồi

Filed under Think Stats

One response to “Chương 3: Hàm phân bố lũy tích

  1. Pingback: Think Stats: Xác suất thống kê dành cho người lập trình | Blog của Chiến

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s