1709 words
9 minutes
Python 实战笔记:从 Pandas 描述性统计、SciPy 假设检验到 FastAPI 服务端开发

前言#

在数据科学的实际工作中,我们经常需要在“统计分析”和“应用开发”两种思维模式间切换。今天的实战笔记将通过具体且独立的业务场景,串联起 Python 数据生态中的三个核心库:Pandas(用于数据概览)、SciPy(用于假设验证)和 FastAPI(用于服务部署)。

本文将深入剖析代码背后的统计学原理和 Python 语法细节,帮助你掌握从数据分析到 API 开发的完整流程。

第一部分:Pandas 中的描述性统计 (Descriptive Statistics)#

在处理矩阵数据(DataFrame)时,理清“基于行”和“基于列”的统计逻辑至关重要。我们可以以股票市场数据多店铺销售数据为例。

1. 核心知识点#

  • 均值 (Mean): 数据的中心趋势。

  • 标准差 (Standard Deviation, SD): 反映数据的波动性(风险)。

  • 标准误差 (Standard Error of the Mean, SEM): 反映样本均值的估算精确度。

  • 区别点:SD 衡量数据本身的离散度(例如股票每天涨跌多剧烈),SEM 衡量均值的可靠性。

  • 轴 (Axis) 的概念:

    • axis=0: 跨行操作(沿着纵向切),通常用于计算某一特征(列)在不同样本间的表现。
    • axis=1: 跨列操作(沿着横向切),通常用于计算某一样本(行)在不同维度下的综合表现。

2. 实战案例:多店铺销售分析#

假设我们有一个 DataFrame,每一行代表一天,每一列代表一个分店的销售额。

import pandas as pd

def analyze_sales_metrics(df):
    """
    输入: df (Pandas DataFrame) - 包含多店销售数据
    输出: 
        daily_average: 每一天的全店平均销量 (基于行)
        daily_volatility: 每一天的销量波动 (基于行)
        store_average: 每个店铺的平均销量 (基于列)
        store_reliability: 每个店铺销量均值的标准误 (基于列)
    """
    # 1. 计算单日全店平均业绩 (Row Mean)
    # axis=1: 横向计算,聚合所有店铺当天的数据
    daily_average = df.mean(axis=1)
    
    # 2. 计算单日业绩差异 (Row Standard Deviation)
    # 如果标准差大,说明当天有的店卖得好,有的卖得差
    # Pandas 默认 ddof=1 (样本标准差)
    daily_volatility = df.std(axis=1)
    
    # 3. 计算店铺长期平均表现 (Column Sample Mean)
    # axis=0: 纵向计算,算出该列(该店铺)的历史平均值
    store_average = df.mean(axis=0)
    
    # 4. 计算店铺表现的稳定性 (Column Sample Error)
    # 使用 sem() 函数直接计算标准误差
    store_reliability = df.sem(axis=0)
    
    return daily_average, daily_volatility, store_average, store_reliability


第二部分:SciPy 中的假设检验 (Hypothesis Testing)#

统计检验用于判断观测到的现象是否具有显著性。

1. 卡方检验 (Chi-square Test) —— 网站流量来源是否异常?#

场景: 假设我们要验证某网站在一周 7 天内的访问量分布是否均匀,或者是否符合特定的预期比例(例如周末流量通常更高)。

核心逻辑:

  • 观测频数 (Observed): 实际统计到的每天访问人数。
  • 期望频数 (Expected): 按照理论模型(如均匀分布)计算出的预期人数。

代码解析:

from scipy.stats import chisquare

def check_traffic_distribution(daily_visitors):
    """
    输入: daily_visitors (list) - 一周7天的实际访问量列表
    """
    total_traffic = sum(daily_visitors)
    days_count = len(daily_visitors) # 应当为 7
    
    # 1. 设定“尺子”:假设流量应该是均匀分布的
    # 理论概率 = 1/7
    expected_prob = 1.0 / days_count
    
    # 2. 计算期望频数 (Expected Frequencies)
    # 如果完全均匀,每天应该有多少人
    f_exp = [total_traffic * expected_prob] * days_count
    
    # 3. 观测频数即为输入数据
    f_obs = daily_visitors
    
    # 4. 执行卡方检验
    # 对比“实际分布”和“均匀分布”的差异
    result = chisquare(f_obs=f_obs, f_exp=f_exp)
    
    # 如果 p_value < 0.05,说明流量分布显著不均匀
    return result.pvalue, result.statistic

2. 配对检验 —— 算法优化前后的效果对比#

场景: 我们对同一组服务器进行了系统升级,想要对比升级前(Column A)和升级后(Column B)的响应时间。由于是同一组服务器的前后对比,这属于配对数据。

代码解析:

from scipy.stats import ttest_rel, wilcoxon

def compare_system_performance(df):
    """
    假设 df 第一列是 Upgrade_Before,第二列是 Upgrade_After
    """
    response_time_before = df.iloc[:, 0]
    response_time_after = df.iloc[:, 1]
    
    # 方法1: 配对 T 检验 (Paired T-test)
    # 适用条件:假设响应时间的差值服从正态分布
    p_ttest = ttest_rel(response_time_before, response_time_after).pvalue
    
    # 方法2: Wilcoxon 符号秩检验
    # 适用条件:不确定分布形态,或存在离群点(非参数检验)
    # 它通过比较“排名”而非具体数值来判断差异
    p_wilcoxon = wilcoxon(response_time_before, response_time_after).pvalue
    
    return p_ttest, p_wilcoxon


第三部分:FastAPI 服务端开发 (Web Implementation)#

做完模型或逻辑验证后,我们常需要将其封装为微服务。这里演示如何使用 FastAPI 构建一个商品折扣计算器(Discount Calculator)。

1. 核心知识点#

  • Pydantic (BaseModel): 定义数据契约。确保传入的价格和折扣率都是合法的数字。
  • 表单处理 (Form): 处理传统的 HTML 表单提交。
  • 模板渲染 (Jinja2Templates): 实现前后端分离的简单页面展示。

2. 代码实战:商品折扣计算 API#

这是一个完整的后端服务脚本。

# 引入必要的库
from typing import Annotated
from fastapi import FastAPI, Form, Request
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.encoders import jsonable_encoder
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
import uvicorn

# --- 知识点 1: 定义数据模型 ---
# 规定前端提交的数据结构
class ProductInfo(BaseModel):
    original_price: float
    discount_rate: float  # 例如 0.9 代表 9折

app = FastAPI()
# 设置存放 HTML 文件的文件夹 (需手动创建 templates 目录)
templates = Jinja2Templates(directory='templates')

# --- 知识点 2: 编写 API 接口 ---

# 接口 A: 计算折后价格 (POST 请求)
# 当用户在网页表单点击“计算”时触发
@app.post("/calculate_discount/")
async def calculate(data: Annotated[ProductInfo, Form()]):
    price = data.original_price
    rate = data.discount_rate
    
    # 简单的业务逻辑校验
    if price < 0 or rate < 0:
        response_data = {"final_price": 0, "message": "Price or rate cannot be negative."}
    else:
        final_price = price * rate
        response_data = {
            "final_price": round(final_price, 2), 
            "message": "Success"
        }

    # 返回 JSON 格式的数据
    return JSONResponse(content=jsonable_encoder(response_data), status_code=200)

# 接口 B: 渲染前端页面 (GET 请求)
# 访问根路径时显示计算器页面
@app.get('/', response_class=HTMLResponse)
async def home(request: Request):
    # 返回 calculator.html 页面
    # 确保 templates/calculator.html 存在
    return templates.TemplateResponse('calculator.html', {'request': request})

# --- 知识点 3: 启动服务器 ---
if __name__ == "__main__":
    # 启动命令:python main.py
    # 访问地址:http://localhost:8000
    uvicorn.run(app, host="0.0.0.0", port=8000)

总结#

通过这三个独立案例,我们完成了一次技术闭环:

  1. 利用 Pandas 对业务数据进行多维度的描述统计。
  2. 利用 SciPy 科学地验证数据变化的显著性。
  3. 利用 FastAPI 将核心计算逻辑快速转化为可交互的 Web 服务。

建议在本地环境复现上述代码,并尝试修改其中的业务逻辑(例如将折扣计算改为房贷计算),以加深对框架的理解。

Python 实战笔记:从 Pandas 描述性统计、SciPy 假设检验到 FastAPI 服务端开发
https://mj3622.github.io/posts/学习笔记/数据科学/数据科学1/
Author
Minjer
Published at
2026-01-14