Pandas: 项目:数据分析

最后更新:2026-08-26

学了 22 课的工具,现在是时候把所有技能串起来了。本课模拟真实数据分析项目:接到任务"分析 Q1-Q3 用户购买行为",从 CSV 加载→探查→清洗→特征→分组→可视化→结论,完整走一遍。真实数据分析 80% 时间在清洗,20% 在分析,但结论全靠那 20%。

⚠️ 注意: 以下代码需在本地 Python 环境中运行。

1. 你将学到


2. 项目背景:电商用户购买行为分析

(1) 任务

Charlie 接到任务:"分析我们 Q1-Q3 的用户购买行为,找出高价值用户、热门品类、消费趋势,给运营建议。"

(2) 分析流程

100%
graph TB
    A["1. 加载数据"] --> B["2. 探查(EDA)"]
    B --> C["3. 清洗流水线"]
    C --> D["4. 特征工程"]
    D --> E["5. 分组聚合"]
    E --> F["6. 可视化报告"]
    F --> G["7. 结论与建议"]
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

3. 数据加载与探查

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:数据加载与 EDA(难度⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# Step 1-2: Load and Explore
# ============================================

# Simulate e-commerce data (in real project: pd.read_csv)
np.random.seed(42)
n = 2000
users = pd.DataFrame({
    'user_id': range(1, n + 1),
    'name': [f'User_{i:04d}' for i in range(1, n + 1)],
    'age': np.random.randint(18, 70, n),
    'gender': np.random.choice(['M', 'F'], n),
    'city': np.random.choice(['NYC', 'LA', 'Chicago', 'Houston', 'Phoenix'], n),
    'join_date': pd.to_datetime(np.random.choice(
        pd.date_range('2023-01-01', '2024-09-30'), n
    ))
})

orders = pd.DataFrame({
    'order_id': [f'O{i:06d}' for i in range(1, 5001)],
    'user_id': np.random.randint(1, n + 1, 5000),
    'product_id': np.random.randint(1, 51, 5000),
    'category': np.random.choice(['Electronics', 'Clothing', 'Home', 'Sports', 'Books'], 5000),
    'amount': np.round(np.random.exponential(100, 5000), 2),
    'quantity': np.random.randint(1, 5, 5000),
    'order_date': pd.to_datetime(np.random.choice(
        pd.date_range('2024-01-01', '2024-09-30'), 5000
    ))
})

# Inject data quality issues
orders.loc[np.random.choice(5000, 200, replace=False), 'amount'] = np.nan
orders.loc[np.random.choice(5000, 100, replace=False), 'category'] = np.nan
duplicates = orders.sample(50)
orders = pd.concat([orders, duplicates], ignore_index=True)

# EDA: shape, dtypes, missing, duplicates
print(f"Users: {users.shape}, Orders: {orders.shape}")
print(f"\nMissing per column:\n{orders.isnull().sum()}")
print(f"\nDuplicate orders: {orders.duplicated(subset='order_id').sum()}")
print(f"\nDate range: {orders['order_date'].min()} to {orders['order_date'].max()}")
print(f"\nAmount stats:\n{orders['amount'].describe()}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

4. 清洗流水线

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:pipe 清洗流水线(难度⭐⭐⭐)

PYTHON
# ============================================
# Step 3: Cleaning Pipeline (pipe)
# ============================================

def remove_duplicates(df):
    """Remove duplicate orders, keep latest"""
    before = len(df)
    df = df.drop_duplicates(subset='order_id', keep='last')
    print(f"  Duplicates removed: {before - len(df)}")
    return df

def fill_missing(df):
    """Fill missing values with appropriate strategies"""
    df = df.copy()
    # Fill amount with median per category
    df['amount'] = df.groupby('category')['amount'].transform(
        lambda x: x.fillna(x.median())
    )
    # Fill category with mode
    df['category'] = df.fillna({'category': df['category'].mode()[0]})
    return df

def fix_types(df):
    """Fix data types"""
    df = df.copy()
    df['order_date'] = pd.to_datetime(df['order_date'])
    df['category'] = df['category'].astype('category')
    return df

def add_derived(df):
    """Add derived columns"""
    df = df.copy()
    df['total_price'] = df['amount'] * df['quantity']
    df['month'] = df['order_date'].dt.to_period('M')
    return df

# Execute pipeline
clean_orders = (orders
    .pipe(remove_duplicates)
    .pipe(fill_missing)
    .pipe(fix_types)
    .pipe(add_derived)
)
print(f"\nClean orders: {clean_orders.shape}")
print(f"Remaining missing: {clean_orders.isnull().sum().sum()}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

5. 特征工程与聚合

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:RFM 特征 + 分组分析(难度⭐⭐⭐)

PYTHON
# ============================================
# Step 4-5: Feature Engineering + Aggregation
# ============================================

# RFM Analysis (Recency, Frequency, Monetary)
reference_date = clean_orders['order_date'].max() + pd.Timedelta(days=1)

rfm = clean_orders.groupby('user_id').agg(
    recency=('order_date', lambda x: (reference_date - x.max()).days),
    frequency=('order_id', 'count'),
    monetary=('total_price', 'sum')
).reset_index()

# RFM scoring (quartile-based)
rfm['R_score'] = pd.qcut(rfm['recency'], 4, labels=[4, 3, 2, 1]).astype(int)
rfm['F_score'] = pd.qcut(rfm['frequency'].rank(method='first'), 4, labels=[1, 2, 3, 4]).astype(int)
rfm['M_score'] = pd.qcut(rfm['monetary'].rank(method='first'), 4, labels=[1, 2, 3, 4]).astype(int)
rfm['RFM_score'] = rfm['R_score'] + rfm['F_score'] + rfm['M_score']

# Segment users
rfm['segment'] = pd.cut(rfm['RFM_score'], bins=[0, 5, 8, 10, 12],
                         labels=['At-Risk', 'Average', 'Good', 'Champions'])

print("=== User Segments ===")
print(rfm['segment'].value_counts())

# Monthly revenue trend
monthly_rev = clean_orders.groupby('month')['total_price'].sum()
print(f"\n=== Monthly Revenue ===\n{monthly_rev}")

# Category analysis
cat_stats = clean_orders.groupby('category').agg(
    orders=('order_id', 'count'),
    revenue=('total_price', 'sum'),
    avg_amount=('amount', 'mean')
).sort_values('revenue', ascending=False)
print(f"\n=== Category Stats ===\n{cat_stats}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

6. 可视化报告与结论

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:可视化报告(难度⭐⭐⭐)

PYTHON
# ============================================
# Step 6-7: Visualization & Conclusions
# ============================================

import matplotlib.pyplot as plt

# Summary report (no actual plots — code for local execution)
print("=" * 50)
print("  E-COMMERCE USER BEHAVIOR ANALYSIS REPORT")
print("=" * 50)

print(f"\n📊 Dataset Overview:")
print(f"  Users: {len(users):,}")
print(f"  Orders: {len(clean_orders):,}")
print(f"  Period: {clean_orders['order_date'].min().date()} to {clean_orders['order_date'].max().date()}")

print(f"\n🏆 Top Categories:")
for i, row in cat_stats.head(3).iterrows():
    print(f"  {i}: ${row['revenue']:,.0f} ({row['orders']} orders)")

print(f"\n👤 User Segments:")
for seg, count in rfm['segment'].value_counts().items():
    pct = count / len(rfm) * 100
    print(f"  {seg}: {count} ({pct:.1f}%)")

print(f"\n📈 Monthly Trend:")
monthly_growth = monthly_rev.pct_change().dropna() * 100
print(f"  Avg monthly growth: {monthly_growth.mean():.1f}%")
print(f"  Peak month: {monthly_rev.idxmax()}")
print(f"  Lowest month: {monthly_rev.idxmin()}")

print(f"\n💡 Recommendations:")
print(f"  1. Champions segment ({(rfm['segment']=='Champions').sum()} users) → VIP program")
print(f"  2. At-Risk segment ({(rfm['segment']=='At-Risk').sum()} users) → Win-back campaign")
print(f"  3. Electronics is top category → expand selection")
print(f"  4. Focus on high-F-score users for cross-selling")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

7. 数据验证与质量报告

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

:数据验证与质量报告(难度⭐⭐)

PYTHON
# ============================================
# Step: Data Validation & Quality Report
# Calculate missing rate / duplicate rate /
# outlier ratio, generate quality report
# ============================================

# 1. Missing rate analysis
missing_count = orders.isnull().sum()
missing_rate = (missing_count / len(orders) * 100).round(2)
print("=== Missing Rate Report ===")
for col in missing_count[missing_count > 0].index:
    print(f"  {col}: {missing_count[col]} rows missing ({missing_rate[col]}%)")

# 2. Duplicate rate analysis
dup_count = orders.duplicated(subset='order_id').sum()
dup_rate = dup_count / len(orders) * 100
print(f"\n=== Duplicate Rate Report ===")
print(f"  Duplicate orders: {dup_count} ({dup_rate:.2f}%)")

# 3. Outlier detection (amount too high or too low)
q1 = orders['amount'].quantile(0.25)
q3 = orders['amount'].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outliers = orders[(orders['amount'] < lower_bound) | (orders['amount'] > upper_bound)]
outlier_rate = len(outliers) / len(orders) * 100
print(f"\n=== Outlier Report ===")
print(f"  Amount outliers: {len(outliers)} rows ({outlier_rate:.2f}%)")
print(f"  Normal range: ${lower_bound:.2f} ~ ${upper_bound:.2f}")

# 4. Overall data quality score
quality_score = 100 - (missing_rate.sum() + dup_rate + outlier_rate) / 3
print(f"\n=== Data Quality Score: {quality_score:.1f}/100 ===")
print(f"  {'✅ Good quality' if quality_score > 90 else '⚠️ Needs attention'}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境(pandas 2.x)运行。Piston 服务器未预装 pandas,请在本机安装(`pip install pandas`)后实操对照。实际数值会因 pandas 版本略有差异。

❓ 常见问题

Q 分析从哪开始?
A 从 EDA(探索性数据分析)开始——加载后先看 shape/dtypes/missing/describe,对数据建立直觉。不要上来就建模/画图。80% 的分析问题在 EDA 阶段就能发现(缺失值、异常值、类型错误)。好的 EDA 决定分析质量。
Q 清洗到什么程度?
A 清洗到"不影响分析结论"的程度。关键字段(ID/金额/日期)必须干净,次要字段允许少量缺失。原则:关键列 0 缺失,非关键列 <5% 缺失。清洗不是追求完美,是消除噪声。
Q 特征工程做什么?
A 从原始数据提取分析有用的派生列。常见:时间特征(月/季度/星期)、聚合特征(RFM)、比值特征(利润率)、分类特征(价格档次)。特征工程是"用业务知识创造信息"——好的特征比复杂模型更有价值。
Q 如何验证分析结果?
A 三步验证:① 数值自洽(总销售额=各品类之和=各月之和);② 逻辑合理(月增长率不会>200%除非促销);③ 交叉验证(用不同方法算同一指标,结果一致)。任何一步不对就要回溯。
Q 报告怎么写?
A 结构:结论先行→数据支撑→建议。格式:摘要(1 段话)→关键发现(3-5 点带数据)→详细分析(图表+解读)→建议(可执行)。避免"数据堆砌"——每个图表必须有结论,每个结论必须有数据。
Q RFM 是什么?
A Recency(最近购买距今多少天)、Frequency(购买次数)、Monetary(消费金额)——3 维度衡量用户价值。R 近+F 高+M 高=Champions(最有价值),R 远+F 低+M 低=At-Risk(快流失)。RFM 是最经典也最实用的用户分群方法。
Q pipe 有什么好处?
A pipe 把多步清洗变成从上到下的可读流水线,每步函数职责单一、可测试、可复用。比嵌套函数调用 f3(f2(f1(df))) 和反复赋值 df=df... 更清晰。清洗步骤越多,pipe 优势越明显。

📖 小节


📝 作业

  1. 基础题(难度⭐):加载模拟电商数据,完成 EDA(shape/missing/describe),写出 3 条数据质量发现。
  2. 进阶题(难度⭐⭐):构建 3 步 pipe 清洗流水线(去重→填充→类型),清洗后验证 missing=0 和 duplicate=0。
  3. 挑战题(难度⭐⭐⭐):完成完整端到端分析:加载→pipe 清洗→RFM 特征→segment 分群→按月/品类聚合→输出结论和建议报告。

← 上一课:风格化输出 · 下一课:项目-时间序列 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏