📋 前言
各位伙伴们,大家好!经过了21天的学习,我们已经掌握了数据预处理、模型构建、调优和解释的全套流程。今天,Day 22,我们将迎来一次激动人心的"毕业演练"------开启我们的第一个独立项目!
我们将登陆全球最大的数据科学社区 Kaggle,它不仅是我们的"题库"和"数据集仓库",更是提供免费云端计算资源的"健身房"。
但这不仅仅是一堂工具使用课。我们将深入探讨一个看似简单却至关重要的技术细节------文件路径管理 ,并在此基础上,完成一次从提出问题 到解决问题的全流程独立项目实践。让我们一起,完成从"学生"到"数据科学家"的思维跃迁!
一、数据科学家的"健身房"------Kaggle 平台简介
对于数据科学家来说,Kaggle 就像是程序员的 GitHub,设计师的 Behance。它主要提供三大核心服务:
- 数据集 (Datasets):海量的、来自真实世界和学术界的数据集,涵盖金融、医疗、交通、图像等各个领域,是我们独立项目的"原材料"宝库。
- 竞赛 (Competitions):由公司或研究机构发布的数据科学挑战,提供了明确的目标和高质量的数据,是锻炼和检验技能的最佳场所。
- 笔记本 (Notebooks) :一个云端的、预装了所有主流数据科学库的 Jupyter 环境。最棒的是,它提供免费的 GPU 和 TPU 使用额度,是处理大规模数据和训练深度学习模型的"白嫖"利器!
二、万丈高楼平地起:代码稳健性的基石------路径管理
在开始独立项目前,我们必须掌握一个能让你避免无数 FileNotFoundError 的核心技巧:如何正确处理文件路径。
2.1 问题的根源:相对路径的"漂移"
当我们直接使用相对路径(如 pd.read_csv('./data/train.csv'))时,程序依赖于"当前工作目录 "。这个目录是你从哪里运行 Python 脚本决定的,而不是脚本文件本身所在的位置。
- 如果你在
G:\project\目录下运行python ./scripts/main.py,那么当前工作目录就是G:\project\。 - 如果你
cd到G:\project\scripts\再运行python main.py,工作目录就变成了G:\project\scripts\。
这种不确定性是导致代码在自己电脑上能跑,一到别人电脑或服务器上就报错的罪魁祸首。
2.2 黄金法则:拼接脚本所在目录的绝对路径
为了让代码无论在何处运行都能准确找到文件,我们必须使用绝对路径。最佳实践如下:
__file__: Python 的一个内置变量,它代表当前脚本文件的绝对路径。os.path.dirname(__file__): 获取该脚本文件所在的目录。os.path.join(dir, filename): 安全地拼接 目录和文件名,自动处理不同操作系统(Windows的\和 Linux的/)的分隔符问题。
这套组合拳,是保证你代码健壮性和可移植性的不二法门。
三、我的第一个独立项目:电信客户流失分析
根据作业要求,我将寻找一个全新的数据集,并实践从选题到完成的整个流程。
3.1 选题与思考:为什么是"客户流失"?
很多教程的重点是"如何实现一个算法",但正如笔记所言,"针对现有数据的思考才是真正拉开你与他人差距的最重要因素"。
我选择这个主题,是基于以下思考:
- 商业价值巨大:客户流失是所有订阅制业务(电信、SaaS、流媒体)的痛点。预测流失并进行干预,能直接为公司挽回损失。
- 研究问题明确:目标变量(是否流失)清晰,特征丰富,适合进行分类模型研究。
- 可提出独特视角 :除了预测"谁会流失",我更想探索"为什么会流失"。是合同问题?网络服务问题?还是客服问题?这能为业务部门提供更具体的行动建议。
3.2 数据来源
- 数据集名称: Telco Customer Churn (电信用户流失数据集)
- Kaggle 地址 : https://www.kaggle.com/datasets/blastchar/telco-customer-churn
- 数据简介: 包含了7043名客户的人口统计信息、账户信息、以及他们使用的各项电信服务(如电话、网络、在线安全、流媒体电视等)。
3.3 End-to-End 项目代码实现
python
# 【我的代码】
# 独立项目:电信客户流失预测与分析
import pandas as pd
import numpy as np
import os
import glob
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, accuracy_score
import matplotlib.pyplot as plt
import seaborn as sns
# --- 1. 路径管理:使用黄金法则加载数据 ---
# 获取当前脚本文件所在的目录
# 注意: 在交互式环境(如Jupyter)中, __file__可能未定义,可手动指定
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
except NameError:
current_dir = os.getcwd() # 在Jupyter等环境中备用
# 拼接成数据文件的绝对路径
# 假设数据文件 'WA_Fn-UseC_-Telco-Customer-Churn.csv' 存放在名为 'data' 的子目录中
data_path = os.path.join(current_dir, 'data', 'WA_Fn-UseC_-Telco-Customer-Churn.csv')
print(f"正在从绝对路径加载数据: {data_path}")
try:
df = pd.read_csv(data_path)
print("数据加载成功!")
except FileNotFoundError:
print(f"错误:文件未找到!请确保在 '{os.path.join(current_dir, 'data')}' 目录下有名为 'WA_Fn-UseC_-Telco-Customer-Churn.csv' 的文件。")
exit()
# --- 2. 数据探索与预处理 (EDA & Preprocessing) ---
# 查看数据基本信息
print("\n数据预览:\n", df.head())
print("\n数据信息:\n")
df.info()
# 'TotalCharges' 列有空白值,且应为数值类型
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
df.dropna(inplace=True)
# 将 'Churn' 列转换为 0 和 1
df['Churn'] = df['Churn'].apply(lambda x: 1 if x == 'Yes' else 0)
# 定义特征类型
# 删掉不用于建模的 customerID
df = df.drop('customerID', axis=1)
categorical_features = df.select_dtypes(include=['object']).columns
numerical_features = df.select_dtypes(include=np.number).columns.drop('Churn')
print(f"\n分类特征: {list(categorical_features)}")
print(f"数值特征: {list(numerical_features)}")
# --- 3. 构建预处理与建模管道 (Pipeline) ---
# 创建预处理器
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numerical_features),
('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
])
# 创建包含预处理和建模的管道
# 使用逻辑回归,因为它具有良好的可解释性,符合我们的分析目标
pipeline = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', LogisticRegression(random_state=42, max_iter=1000))])
# --- 4. 训练与评估 ---
# 划分数据集
X = df.drop('Churn', axis=1)
y = df['Churn']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 训练模型
pipeline.fit(X_train, y_train)
print("\n模型训练完成!")
# 进行预测
y_pred = pipeline.predict(X_test)
# 评估模型
print("\n模型评估报告:\n", classification_report(y_test, y_pred))
print(f"模型准确率: {accuracy_score(y_test, y_pred):.4f}")
# --- 5. (进阶) 特征重要性分析 ---
# 从管道中获取逻辑回归模型的系数
model = pipeline.named_steps['classifier']
preprocessor_fitted = pipeline.named_steps['preprocessor']
# 获取独热编码后的特征名
ohe_feature_names = preprocessor_fitted.named_transformers_['cat'].get_feature_names_out(categorical_features)
# 拼接所有特征名
all_feature_names = np.concatenate([numerical_features, ohe_feature_names])
# 创建一个包含特征名和其系数的DataFrame
coef_df = pd.DataFrame({'Feature': all_feature_names, 'Coefficient': model.coef_[0]})
coef_df['Abs_Coefficient'] = np.abs(coef_df['Coefficient'])
# 按重要性排序
sorted_coef_df = coef_df.sort_values(by='Abs_Coefficient', ascending=False)
print("\n影响客户流失的关键因素 (Top 10):\n", sorted_coef_df.head(10))
四、总结与心得:从代码到洞察的飞跃
完成 Day 22 的学习和实践,我最大的感触是,我们学习的重心正在发生根本性的转变。
-
工具是基础,思想是灵魂 :Kaggle 和各种库是强大的工具,但它们无法告诉你该研究什么。提出一个有价值、有新意的问题,其重要性甚至超过了模型调优本身。同一个数据集,有人只能得出"模型准确率80%"的结论,而有人却能发现"签订两年期合同的用户流失率比月度合同低90%"这样的商业洞察。差距就在于思考的深度。
-
好习惯是效率的倍增器:今天学习的"绝对路径拼接法",看似是一个小技巧,却是专业工程实践的基石。一个好的习惯,可以在未来为我们节省下数小时甚至数天的调试时间,让我们可以把精力聚焦在更有价值的分析和思考上。
-
数据质量是研究的天花板 :在寻找数据集的过程中,我深刻体会到了"进阶思考"中提到的问题。一个项目能否成功,很大程度上取决于数据的数量、质量、来源可靠性。在开始一个项目前,花时间去评估数据本身,远比匆忙开始建模要明智得多。
感谢 @浙大疏锦行 老师的课程设计,它不仅教会我们如何"跑通代码",更引导我们思考如何"创造价值"。这趟旅程,正变得越来越有挑战性,也越来越有意义!