1. 连续型概率分布:从直觉到实操的完整拆解
你有没有遇到过这样的情况:手头有一组测量数据——比如某条产线上零件的直径、某款App用户单次会话的停留时长、某城市每天的降雨量——它们看起来“连续不断”,取值似乎可以无限细分,不像抛硬币或掷骰子那样只有几个明确结果。这时候,离散分布就彻底失效了。你不能说“零件直径恰好等于25.347812毫米的概率是0.03”,因为理论上,这个精确到小数点后无穷位的点概率,在连续世界里永远是零。真正有意义的是:“直径落在25.3到25.4毫米之间的概率是多少?”——这正是连续型分布要回答的核心问题。本文不讲教科书定义,而是以一个在工业质检、金融建模和生物统计一线摸爬滚打十年的从业者视角,带你把八种最常用的连续分布掰开揉碎。我们不堆公式,而是先问“它到底在模拟什么现实场景”,再看“它的形状为什么长这样”,最后落到“我怎么在Python里快速画出来、算出来、用对它”。你会发现,Uniform不是“均匀得无聊”,而是建模不确定性的起点;Normal不只是“钟形曲线”,它是中心极限定理赋予我们的终极简化工具;Exponential的“无记忆性”不是数学游戏,而是解释为什么你排队时前面的人永远刚办好业务的底层逻辑。无论你是刚学完微积分想上手数据分析的学生,还是需要给业务方讲清模型假设的算法工程师,这篇内容都直接对应你明天就要面对的真实问题。
2. 八大连续分布的设计哲学与核心动机
2.1 为什么必须区分“离散”与“连续”?——从测量本质说起
在Part 1里,我们处理的是像“今天客服接到多少个投诉电话”(只能是0,1,2,3…)或“这批货里有几个次品”(只能是整数)这类问题。它们的样本空间是可数的,每个结果都有明确的、非零的概率质量。但当你切换到“零件的实际重量”、“用户点击按钮到页面完全加载的时间”、“某只股票收盘价的波动幅度”时,事情就变了。这些变量的理论取值范围是一个区间,比如[0, ∞)或(-∞, +∞),其中包含无穷多个实数点。如果你强行给每一个可能的毫秒级时间点都分配一个概率,那么所有这些无穷多个“点概率”加起来,要么是无穷大,要么是零,永远无法满足“总概率为1”的基本公理。这就是数学上必须引入概率密度函数(PDF)的根本原因:PDF本身不是概率,而是一个“密度”。它在某一点的值可以大于1,但你必须把它在一个小区间上积分,得到的面积才是那个区间的概率。这就像说“长江某一点的水流速度是2米/秒”,这个数值本身不告诉你有多少水经过,但如果你知道“从南京到镇江这段江面的平均流速和宽度”,就能估算出单位时间流过的水量。理解这一点,是避免后续所有误用的第一道门槛。很多初学者一上来就盯着PDF公式看,却忘了问“这个密度函数,是在为哪一类现实过程建模?”——这才是本节要解决的核心。
2.2 Uniform Distribution:不确定性建模的“白板”与基准线
Uniform分布常被误解为“最简单所以最没用”。恰恰相反,它在实践中扮演着两个不可替代的角色。第一个是建模完全无知。比如,你接手一个新项目,对某个关键参数X(如某种新材料的熔点下限)没有任何先验知识,只知道它必然落在1200°C到1350°C之间。此时,Uniform U(1200, 1350)就是最诚实、最不带偏见的初始假设。它不暗示1250°C比1201°C更可能,也不暗示1349°C比1300°C更不可能。第二个角色是随机数生成的基石。几乎所有现代编程语言的random()函数,其原始输出都是U(0,1)。然后,通过逆变换采样法(Inverse Transform Sampling),我们能把它“扭曲”成任何其他分布。比如,要生成一个Normal分布的随机数,你先生成一个u~U(0,1),再计算x = Φ⁻¹(u),其中Φ⁻¹是标准正态分布的分位数函数(即z-score表的反函数)。这个过程之所以可行,正是因为Uniform的CDF F(u)=u 是最简单的线性函数,它的反函数也最简单。所以,当你看到一个复杂的蒙特卡洛模拟在后台疯狂运行时,它的源头很可能就是这一张看似平淡无奇的矩形图。它的PDF是常数1/(b-a),意味着在(a,b)内任意等长的子区间,其概率完全相等;它的CDF是斜率为1/(b-a)的直线,意味着累积概率随x线性增长。这种极致的“公平性”,正是它作为一切随机性起点的价值所在。
2.3 Normal Distribution:中心极限定理赐予我们的“万能压缩包”
如果说Uniform是建模无知的起点,那么Normal(高斯)分布就是建模“已知复杂性”的终点。它的核心魔力不在于它长得像钟,而在于中心极限定理(CLT)。CLT告诉我们:无论原始总体是什么分布(哪怕是极度歪斜的指数分布或双峰的混合分布),只要你从其中独立地、大量地抽取样本,并计算每个样本的均值,那么这些样本均值的分布,将无限趋近于一个Normal分布。这个结论强大到令人震撼——它意味着,即使你对底层物理过程一无所知,只要你的数据是大量独立微小效应的叠加(比如,一个零件的尺寸误差,是由机床振动、刀具磨损、材料热胀冷缩、环境湿度等成百上千个微小因素共同作用的结果),那么它的最终表现,大概率就是Normal。这就是为什么它在质量控制(六西格玛)、金融风险(VaR模型)、甚至心理学测试(IQ分数)中无处不在。它的两个参数μ和σ²,分别代表了“整体趋势”和“离散程度”,而标准化变换z=(x-μ)/σ,则是剥离具体量纲、进行跨尺度比较的通用语言。你不需要记住所有公式,但必须刻在脑子里的是:当有人说“这个指标服从正态”,他真正想表达的是“它背后有大量独立、微小、同质的扰动源”。一旦这个前提被破坏(比如,数据里混入了异常大的系统性偏差),强行套用Normal就会导致灾难性的误判。
2.4 Exponential Distribution:刻画“等待”与“失效”的无记忆性
Exponential分布是描述“时间间隔”的王者。它回答的问题是:“距离上一次事件发生,还要等多久,下一次事件才会发生?”这里的“事件”必须满足两个严苛条件:一是独立性(上一次事件何时发生,完全不影响下一次);二是恒定速率(单位时间内,事件发生的平均次数λ是固定的)。典型的例子包括:放射性原子核的衰变、客服热线的来电间隔、机器设备的无故障运行时间。它的PDF是f(x)=λe^(-λx),呈经典的“陡峭下降”形态。但真正让它独一无二的,是无记忆性(Memoryless Property)。数学表达为:P(X > s + t | X > s) = P(X > t)。翻译成大白话就是:“如果一个灯泡已经亮了1000小时还没坏,那么它再亮500小时的概率,和一个全新的灯泡亮500小时的概率,完全一样。”这听起来反直觉,因为日常经验告诉我们,旧东西更容易坏。但Exponential恰恰建模的是那种“不会老化”的理想化过程。现实中,电子元件的早期失效(婴儿死亡率)和后期磨损(耗损失效)都不符合它,但它在设备的“偶然失效期”(即浴盆曲线的中间平坦段)是极佳的近似。这也是为什么它在可靠性工程和队列论中是绝对核心。当你看到一个系统MTBF(平均无故障时间)被标为10000小时,并声称其失效服从Exponential,你就该立刻意识到:这个数字背后,隐含着“该设备没有‘年龄’概念”的强假设。
2.5 Chi-squared (χ²) Distribution:从“平方和”到“统计检验”的桥梁
χ²分布的名字就暴露了它的出身:它就是k个独立的标准正态随机变量的平方和。即,若Z₁, Z₂, ..., Zₖ ~ i.i.d. N(0,1),则Q = Z₁² + Z₂² + ... + Zₖ² ~ χ²(k)。这个定义看似抽象,但它在统计学中架起了一座至关重要的桥梁。最直接的应用,是方差的抽样分布。假设你从一个正态总体中抽取n个样本,计算其样本方差s²,那么量(n-1)s²/σ² 就严格服从χ²(n-1)分布。这使得我们能够构造关于总体方差σ²的置信区间,或者进行方差齐性检验(比如Levene检验的前身)。另一个更广为人知的应用是卡方拟合优度检验(Goodness-of-Fit Test),它用来判断一组观测频数是否与某个理论分布(如均匀、二项、泊松)相符。其核心思想,是将每个类别的观测频数Oᵢ与期望频数Eᵢ的差异,标准化为(Oᵢ - Eᵢ)²/Eᵢ,然后将所有类别加总,这个总和在原假设成立时,就近似服从χ²分布。自由度k在这里,本质上是你在计算期望频数时,所使用的、由样本数据估计出的独立参数的个数。例如,在检验一枚硬币是否均匀时,你只有一个参数p(正面概率),而它由样本比例p̂估计,所以自由度是类别数2减去1,即1。理解k的来源,远比死记硬背公式重要,因为它直接关系到检验的效力和犯错的风险。
2.6 Gamma Distribution:Exponential的“升级版”与等待时间的精雕细琢
Gamma分布可以被看作是Exponential分布的自然推广。Exponential描述的是“等到第一次事件发生需要多久”,而Gamma描述的是“等到第α次事件发生需要多久”。这里的α(通常记为k或shape)是一个正实数,当α是整数时,Gamma分布就退化为Erlang分布,即α个独立同分布的Exponential随机变量之和。这在建模更复杂的等待过程时极为有用。比如,一个呼叫中心有3个坐席,客户到达服从速率为λ的泊松过程,那么从空闲状态开始,等到第3个客户都被服务完毕所需的总时间,就服从Gamma(3, 1/λ)分布(注意参数化方式,这里用的是scale=1/λ)。Gamma的另一个关键应用是作为共轭先验(Conjugate Prior)出现在贝叶斯统计中。例如,当你想对泊松过程的速率λ进行贝叶斯推断时,Gamma分布是λ的天然共轭先验。这意味着,如果你的先验是Gamma(α, β),而观测到了n个事件,总时间为t,那么后验分布仍然是Gamma,只是参数更新为Gamma(α+n, β+t)。这种“先验+数据=同类型后验”的数学优雅性,让Gamma成为贝叶斯工作流中的常客。它的PDF中那个Γ(α)函数,看起来吓人,但它只是一个归一化常数,确保整个PDF下的面积为1。在实际计算中,我们几乎从不手动计算Γ函数,而是依赖SciPy等库的内置函数,这正是工具解放生产力的体现。
2.7 Student’s t-Distribution:小样本时代的“正态救星”
t分布的诞生,源于一个朴素的困境:在真实世界中,我们几乎永远不知道总体的标准差σ。我们只能用样本标准差s来估计它。而s本身是一个随机变量,它有自己的抽样变异性。当样本量n很大时(比如n>30),s非常稳定,接近σ,此时用z=(x̄-μ)/(σ/√n)做推断问题不大。但当n很小时(比如n=5),s的波动性就非常大,用它代替σ会导致z统计量的分布严重偏离标准正态,尾部会变得异常肥厚。William Sealy Gosset(笔名“Student”)在吉尼斯啤酒厂工作时,为了解决小批量啤酒质量检测的难题,推导出了这个修正后的分布——t分布。它的PDF与Normal相似,也是对称钟形,但有一个关键区别:它有一个自由度参数ν=n-1,且ν越小,其尾部越厚。这意味着,t分布承认并量化了“用s代替σ所带来的额外不确定性”。当你查t分布表时,你会发现,对于95%的置信水平,当ν=4(即n=5)时,临界值是2.776,远大于标准正态的1.96。这个更大的“安全边际”,正是为了覆盖小样本下s的巨大波动风险。因此,t检验(如单样本t检验、配对t检验、两独立样本t检验)是小样本推断的黄金标准。它的存在,让统计学从“大样本理论”真正走下了神坛,进入了工程师、医生、农学家们日常工作的车间、诊室和田间地头。
2.8 F-Distribution与Log-Normal Distribution:方差比较与右偏数据的终极方案
F分布和Log-Normal分布,一个专攻“方差之比”,一个专治“右偏之痛”,它们共同构成了处理非标准数据形态的利器。F分布的定义是:若U ~ χ²(d₁) 且 V ~ χ²(d₂),且U与V独立,则 (U/d₁)/(V/d₂) ~ F(d₁, d₂)。这个定义直接指向了它的核心使命:比较两个独立正态总体的方差。在方差分析(ANOVA)中,F统计量就是“组间方差”与“组内方差”的比值。如果这个比值显著大于1,就说明不同组之间的差异,超出了单纯由随机误差所能解释的范围,从而拒绝“所有组均值相等”的原假设。F分布天生就是右偏的,且其形状完全由两个自由度d₁和d₂决定,这使得它对实验设计的平衡性(即各组样本量是否相等)非常敏感。而Log-Normal分布,则是处理强烈右偏(Positive Skew)数据的首选。它的定义是:如果Y ~ Log-Normal(μ, σ²),那么X = ln(Y) ~ N(μ, σ²)。这意味着,Y本身永远为正,且其取值范围是(0, +∞),其PDF在左侧紧贴y轴,然后向右拖出一条长长的尾巴。这完美契合了大量现实数据:个人收入、房屋价格、癌症患者的生存时间、化学反应的完成时间。对这些数据直接做Normal假设,会导致严重的模型失真。而对其取对数后,数据往往奇迹般地变得对称、集中,此时再应用基于正态的统计方法,就水到渠成了。Log-Normal的均值和方差公式虽然复杂,但其背后的逻辑极其清晰:它不是在强行“拉直”数据,而是尊重了数据内在的乘性(Multiplicative)结构——即,影响因素是以“倍数”而非“加数”的方式作用于结果的。
3. 核心参数、图形特征与实操要点深度解析
3.1 Uniform Distribution:参数、图形与易错点
Uniform分布的参数看似简单,仅有a(下界)和b(上界),但正是这种简单,埋藏着最容易被忽视的陷阱。首先,a和b必须是确定的、已知的常数。你不能说“a是某个未知参数,我需要估计它”,因为Uniform的MLE(最大似然估计)会给出一个反直觉的结果:a的估计值是样本最小值,b的估计值是样本最大值。这意味着,你的估计区间会永远“紧贴”你的观测数据,对未来的极端值毫无预警能力。这在风险管理中是致命的。其次,Uniform的PDF在(a,b)内是常数1/(b-a),这个值本身可以远大于1。例如,U(0.9, 1.1)的PDF高度是5,这完全没问题,因为它下面的面积(5 * 0.2 = 1)才是关键。新手常犯的错误是,看到PDF>1就以为自己算错了。第三,Uniform的CDF是一个分段函数:当x<a时为0;当a≤x<b时为(x-a)/(b-a);当x≥b时为1。这个“阶梯式上升”的特性,决定了它在生成随机数时的逆变换采样是完美的线性映射。在Python中,np.random.uniform(a, b, size=n)是最直接的生成方式。但如果你想手动实现以加深理解,代码如下:
import numpy as np def uniform_sample(a, b, n): u = np.random.random(n) # 生成n个U(0,1) return a + u * (b - a) # 线性变换到(a,b)这个短短三行代码,揭示了Uniform作为“随机性母体”的本质。最后,一个重要的实操心得:Uniform是检验其他分布随机数生成器质量的“金标准”。你可以用Kolmogorov-Smirnov(KS)检验,来验证一个声称是Normal的随机数序列,其CDF是否真的与理论Normal CDF一致。而KS检验本身,就需要一个高质量的Uniform随机数源作为基础。所以,别小看这个矩形,它是整个随机模拟大厦的地基。
3.2 Normal Distribution:参数解读、标准化与可视化技巧
Normal分布的两个参数μ和σ,是理解一切的钥匙。μ是位置参数,它决定了整个钟形曲线的“重心”在哪里。改变μ,曲线会沿着x轴平移,但形状丝毫不变。σ是尺度参数,它决定了曲线的“胖瘦”。σ越大,数据越分散,曲线越矮胖;σ越小,数据越集中,曲线越高瘦。一个常被忽略的细节是,Normal分布的拐点(Inflection Point)恰好位于x = μ ± σ处。这是曲线从“凸”变为“凹”的转折点,也是标准差在几何上的直观体现:从均值出发,向左右各走一个σ的距离,你就到达了曲线形态发生根本变化的位置。标准化(z-score)是Normal分布的灵魂操作。公式z = (x - μ) / σ,其物理意义是“x距离均值有多少个标准差”。这个操作的强大之处在于,它抹平了所有Normal分布的个体差异,将它们全部映射到同一个标准正态分布N(0,1)上。这意味着,你只需要一张z-score表,就能解决所有Normal分布的概率计算问题。在Python中,scipy.stats.norm.cdf(x, loc=mu, scale=sigma)可以一步到位,但理解其内部调用的是scipy.stats.norm.cdf((x-mu)/sigma),会让你对标准化有更深的敬畏。可视化时,一个高级技巧是使用分位数-分位数图(Q-Q Plot)。它将你的数据的分位数与理论Normal分布的分位数一一对应画点。如果所有点都紧密地落在一条直线上,就强有力地证明了你的数据服从Normal。这比单纯看直方图要严谨得多,因为直方图的形状受分箱数量影响极大。Q-Q图是诊断数据分布形态的“X光片”。
3.3 Exponential Distribution:速率λ的双重身份与生存分析
Exponential分布的参数λ,拥有双重身份:它既是事件发生的瞬时速率(Hazard Rate),也是平均等待时间的倒数(Mean = 1/λ)。这两个身份统一于“无记忆性”这一核心性质。λ越大,意味着事件发生得越“急迫”,因此平均等待时间1/λ就越短,PDF曲线也就越陡峭。反之亦然。在生存分析(Survival Analysis)中,Exponential是构建Kaplan-Meier估计量和Cox比例风险模型的基础。它的生存函数S(t) = P(T > t) = e^(-λt),直接给出了“存活”超过时间t的概率。这个函数是单调递减的,且其下降速度由λ决定。一个关键的实操要点是:在拟合Exponential模型前,必须进行指数性检验。最常用的方法是绘制累积风险图(Cumulative Hazard Plot)。如果数据确实服从Exponential,那么-ln(S(t))(即累积风险)应该是一条完美的直线。如果不是,那就说明“无记忆性”假设不成立,你可能需要转向Weibull分布等更灵活的模型。在Python中,lifelines库提供了完整的生存分析工具链。一个简单的拟合示例:
from lifelines import ExponentialFitter import numpy as np # 假设T是观测到的生存时间数组,E是事件指示(1=事件发生,0=删失) T = np.array([1.2, 2.5, 3.1, 4.0, 5.2]) E = np.array([1, 1, 0, 1, 1]) ef = ExponentialFitter() ef.fit(T, E) print(f"Estimated lambda: {ef.lambda_:.4f}") print(f"Mean survival time: {1/ef.lambda_:.4f}")这段代码不仅给出了λ的估计值,还自动处理了删失数据(Censored Data),这是真实世界数据的常态。
3.4 Chi-squared & Gamma Distributions:自由度与形状参数的实践意义
χ²和Gamma分布的自由度k(或形状参数α),是它们最核心的“性格”标签。对于χ²(k),k直接等于其均值,而方差是2k。这意味着,k越小,分布越偏斜,尾部越重;k越大,分布越接近对称,越像一个Normal分布。一个实用的经验法则是:当k > 50时,χ²(k)可以用N(k, 2k)来近似,这大大简化了大样本下的计算。在卡方检验中,k的计算必须一丝不苟。例如,在一个r行c列的列联表中,用于检验独立性的χ²统计量的自由度是(r-1)*(c-1)。这是因为,一旦你固定了(r-1)行和(c-1)列的频数,最后一行和最后一列的频数就被行和与列和唯一确定了,它们不再是“自由”的。Gamma分布的形状参数α,控制着分布的形态。当α=1时,Gamma退化为Exponential;当α是整数时,它是α个Exponential的和;当α<1时,PDF在x=0处趋向无穷大,呈现“J”形;当α>1时,PDF有一个峰值(众数),且α越大,峰值越明显,分布越集中。尺度参数β(或1/λ)则控制着分布的“伸展”程度。在贝叶斯框架下,Gamma(α, β)作为泊松率λ的先验,其超参数α和β可以被解释为“先前观测到的α个事件,总时间为β”。这种解释让先验的设定变得无比直观和有依据。
3.5 Student’s t & F Distributions:自由度的“双刃剑”效应
t分布和F分布的自由度,是它们威力与局限性的根源。t分布的自由度ν=n-1,它像一个“调节旋钮”:ν越小,t分布的尾部越厚,临界值越大,检验越保守(更难拒绝原假设);ν越大,t分布越接近标准正态,临界值越小,检验越激进。这完美体现了统计学的审慎哲学:证据越少(n越小),我们就越不敢轻易下结论。在实际应用中,一个重要的注意事项是,t检验要求数据来自正态总体。当n很小时,这个假设至关重要;但当n足够大(如n>30),中心极限定理开始起作用,样本均值的分布本身就趋于正态,此时t检验的稳健性很强。F分布则有两个自由度:分子自由度d₁和分母自由度d₂。d₁通常与“信号”(如组间变异)相关,d₂与“噪声”(如组内变异)相关。F统计量的值越大,说明信号相对于噪声越强。F分布的临界值表是二维的,查找起来比t分布麻烦,但其逻辑是统一的:在给定的显著性水平α下,你需要找到一个阈值,使得F统计量超过它的概率恰好是α。在Python中,scipy.stats.f.ppf(q, dfn, dfd)可以轻松获取这个临界值。一个常见的误区是认为F检验只能用于方差齐性。事实上,ANOVA中的F检验,其分子是“处理效应”的均方,分母是“误差”的均方,它检验的是“处理是否有显著效应”,其背后依然是F分布的定义。
3.6 Log-Normal Distribution:对数转换的威力与陷阱
Log-Normal分布的参数μ和σ,是其对数Y=ln(X)的均值和标准差,而非X本身的。这是一个极易混淆的点。X的均值是exp(μ + σ²/2),其方差是[exp(σ²) - 1] * exp(2μ + σ²)。这些公式看起来复杂,但它们揭示了一个深刻事实:Log-Normal的均值不仅取决于μ,还被σ²“抬升”了。σ越大,数据越分散,其均值被拉得越高。这解释了为什么收入分布的均值总是远高于中位数——因为那条长长的右尾,把均值拽向了远方。在实操中,判断一个数据集是否适合Log-Normal,最可靠的方法是绘制对数直方图或Q-Q图。如果对数后的数据直方图是钟形的,或者Q-Q图上的点大致在一条直线上,那么Log-Normal就是一个强有力的候选。一个关键的陷阱是:Log-Normal要求所有X>0。如果你的数据中包含零或负数,你不能强行取对数。此时,你可能需要考虑零膨胀模型(Zero-Inflated Model)或Box-Cox变换等更复杂的工具。在Python中,scipy.stats.lognorm的参数化方式有点特殊,它使用s=σ作为形状参数,scale=exp(μ)作为尺度参数。一个完整的拟合与绘图示例:
import numpy as np import matplotlib.pyplot as plt from scipy import stats # 生成一些Log-Normal数据 np.random.seed(42) mu, sigma = 2.0, 0.5 X = np.random.lognormal(mean=mu, sigma=sigma, size=1000) # 拟合 shape, loc, scale = stats.lognorm.fit(X, floc=0) # 强制loc=0,因为Log-Normal从0开始 # 绘图 x = np.linspace(X.min(), X.max(), 100) pdf_fitted = stats.lognorm.pdf(x, shape, loc=loc, scale=scale) plt.hist(X, bins=30, density=True, alpha=0.6, label='Data') plt.plot(x, pdf_fitted, 'r-', lw=2, label='Fitted Log-Normal') plt.legend() plt.show()这段代码展示了从数据生成、参数拟合到可视化验证的完整闭环。
4. Python实操:从生成、拟合到可视化的全流程
4.1 环境准备与核心库详解
在开始编码之前,确保你的Python环境已安装好以下核心库。我推荐使用Anaconda发行版,它预装了大部分科学计算包。
pip install numpy scipy matplotlib seaborn pandas statsmodels lifelines- NumPy:提供高效的数组运算,是所有科学计算的基石。
np.random模块是生成各种分布随机数的入口。 - SciPy:
scipy.stats是本次实战的绝对主角。它为每一种分布都提供了.rvs()(生成随机数)、.pdf()(概率密度)、.cdf()(累积分布)、.ppf()(分位数函数,即逆CDF)等全套方法。它的API设计极其一致,学会一个,就通晓全部。 - Matplotlib & Seaborn:前者是底层绘图引擎,后者是基于前者的高级封装,能用更少的代码绘制更美观的统计图。
seaborn.histplot()和seaborn.kdeplot()是探索数据分布形态的利器。 - Statsmodels:提供更专业的统计模型,如
statsmodels.stats.diagnostic.kstest_normal可以进行正态性检验。 - Lifelines:专为生存分析设计,对Exponential、Weibull等分布有专门的拟合器。
一个重要的经验是:永远不要自己从头实现PDF或CDF的数学公式。SciPy的实现经过了数十年的优化和验证,精度和速度都远超个人代码。你的精力应该放在理解数据、选择模型、解释结果上,而不是重复造轮子。
4.2 生成八大分布的随机样本并可视化对比
下面的代码将一次性生成所有八种分布的样本,并用统一的风格绘制出来,让你直观感受它们的形态差异。
import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats # 设置全局样式 sns.set_style("whitegrid") plt.figure(figsize=(16, 12)) # 1. Uniform np.random.seed(42) uniform_data = np.random.uniform(0, 10, 1000) plt.subplot(3, 3, 1) sns.histplot(uniform_data, kde=True, stat="density", bins=30, color="skyblue") plt.title("Uniform(0, 10)") plt.xlabel("x") # 2. Normal normal_data = np.random.normal(loc=5, scale=2, size=1000) plt.subplot(3, 3, 2) sns.histplot(normal_data, kde=True, stat="density", bins=30, color="lightgreen") plt.title("Normal(μ=5, σ=2)") plt.xlabel("x") # 3. Exponential exp_data = np.random.exponential(scale=2, size=1000) # 注意:scale=1/λ plt.subplot(3, 3, 3) sns.histplot(exp_data, kde=True, stat="density", bins=30, color="salmon") plt.title("Exponential(λ=0.5)") plt.xlabel("x") # 4. Chi-squared (k=3) chi2_data = np.random.chisquare(df=3, size=1000) plt.subplot(3, 3, 4) sns.histplot(chi2_data, kde=True, stat="density", bins=30, color="gold") plt.title("Chi-squared(k=3)") plt.xlabel("x") # 5. Gamma (α=2, β=2) gamma_data = np.random.gamma(shape=2, scale=2, size=1000) plt.subplot(3, 3, 5) sns.histplot(gamma_data, kde=True, stat="density", bins=30, color="orchid") plt.title("Gamma(α=2, β=2)") plt.xlabel("x") # 6. Student's t (ν=5) t_data = np.random.standard_t(df=5, size=1000) plt.subplot(3, 3, 6) sns.histplot(t_data, kde=True, stat="density", bins=30, color="teal") plt.title("t(ν=5)") plt.xlabel("x") # 7. F (d1=5, d2=10) f_data = np.random.f(dfnum=5, dfden=10, size=1000) plt.subplot(3, 3, 7) sns.histplot(f_data, kde=True, stat="density", bins=30, color="coral") plt.title("F(d1=5, d2=10)") plt.xlabel("x") # 8. Log-Normal (μ=1, σ=0.5) lognorm_data = np.random.lognormal(mean=1, sigma=0.5, size=1000) plt.subplot(3, 3, 8) sns.histplot(lognorm_data, kde=True, stat="density", bins=30, color="plum") plt.title("Log-Normal(μ=1, σ=0.5)") plt.xlabel("x") plt.tight_layout() plt.show()运行这段代码,你会得到一张九宫格图(左上角为空白),八种分布的形态跃然纸上。仔细观察:Uniform是完美的矩形;Normal是对称的钟;Exponential从y轴开始陡降;Chi-squared和Gamma都从0开始,但Chi-squared更偏斜;t分布比Normal更“胖尾”;F分布完全在正半轴,且不对称;Log-Normal则在左侧堆积,右侧拖尾。这种视觉对比,比任何文字描述都来得直接和深刻。
4.3 对真实数据进行分布拟合与检验
现在,让我们用一个真实的、带有挑战性的数据集来练习。我们将使用著名的iris数据集中的petal_length(花瓣长度)特征,并尝试用Normal和Log-Normal两种分布去拟合它。
from sklearn.datasets import load_iris import pandas as pd # 加载数据 iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) data = df['petal length (cm)'].values # 1. 绘制原始数据的直方图和KDE plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) sns.histplot(data, kde=True, stat="density", bins=20, color="steelblue") plt.title("Iris Petal Length: Empirical Distribution") # 2. 拟合Normal分布 mu_norm, std_norm = stats.norm.fit(data) x_norm = np.linspace(data.min(), data.max(), 100) pdf_norm = stats.norm.pdf(x_norm, mu_norm, std_norm) # 3. 拟合Log-Normal分布 # 注意:Log-Normal要求数据>0,iris数据满足 shape, loc, scale = stats.lognorm.fit(data, floc=0) x_lognorm = np.linspace(data.min(), data.max(), 100) pdf_lognorm = stats.lognorm.pdf(x_lognorm, shape, loc=loc, scale=scale) # 4. 绘制拟合结果 plt.subplot(1, 2, 2) sns.histplot(data, kde=False, stat="density", bins=20, color