
刚开始系统学习数据科学的时候最容易踩的坑不是某个库不会用而是“不知道一条完整的数据分析流程到底该怎么做”。今天 CSDN 上可以搜到无数 pandas 教程、SQL 语法、机器学习入门但它们大多是零散的学完之后依然很难把数据清洗、建模、评估串成一条线。UC Berkeley 的 Data 100 课程全称 Principles and Techniques of Data Science中文常译为“数据科学原理与技术”刚好补上了这个缺口它把从原始数据到预测模型的完整链路拆成了 27 讲核心技术栈就是 pandas、SQL、机器学习、Spark。本文基于这套课程的学习思路整理了一份本地可复现的数据科学全流程实战笔记适合想系统入门数据科学、或者正在自学的同学参考。1. Data 100 课程到底在讲什么1.1 数据科学与软件工程的边界在学习 Data 100 之前很多同学已经会写 Python也会用列表推导式处理小规模数据但一旦面对几万行甚至上亿行数据就会明显感觉吃力。数据科学和传统软件工程的核心区别在于软件工程关注的是“功能逻辑是否正确”数据科学关注的是“数据中包含的规律是否被正确提取”。Data 100 把这一区别讲得非常清楚。课程反复强调一个概念数据科学流程不是线性的而是循环迭代的。你拿到一份数据后先要观察它长什么样接着清洗然后可视化再建立模型最后回到业务问题本身验证结果。如果清洗环节出了问题后面的模型再漂亮也没有意义。这也是为什么课程的 27 讲里有相当大比例在讲数据探索、数据清洗和数据可视化而不是一上来就甩机器学习算法。1.2 Data 100 在数据科学生态中的定位UC Berkeley 的数据科学课程体系通常是先上 Data 8计算与数据科学入门再上 Data 100。Data 8 用的是相对简单的表结构操作Data 100 则切入真实数据处理场景采用 pandas、SQL、机器学习等工作流。课程的技术难度不算极高但知识点密度很大前段重点pandas 数据操作、SQL 查询、数据可视化。中段重点特征工程、线性回归、分类模型、交叉验证。后段重点随机森林、梯度提升、Spark 分布式数据操作、端到端项目。国内很多同学把这门课当作从“Python 小白”跨到“能做完整数据项目”的跳板。因为它没有堆砌数学公式而是从“怎么做成一件完整的事”出发把数据科学工程化的思维讲透了。1.3 课程核心能力模型学完 Data 100理想状态下你应该具备四种能力第一能用 pandas 完成 80% 以上的日常数据清洗任务。包括读取各种格式文件、类型转换、缺失值处理、分组聚合、连接合并。第二能使用 SQL 对数据库中的结构化数据进行查询、聚合和窗口计算。很多实际企业环境中数据存在数据库里不会 SQL 就等于没有数据分析的入口。第三能构建一个基础机器学习模型。课程要求你不仅能调用sklearn的模型还能理解训练集、验证集、测试集为什么必须分开以及过拟合是如何产生的。第四能理解 Spark 解决的是什么问题。数据量超过单机内存后原来的 pandas 方案就会崩此时需要分布式计算。Data 100 对 Spark 的介绍偏向入门但足够让你建立“大数据”的初步认知。2. 本地环境搭建复刻一套 Data 100 学习环境2.1 环境清单Data 100 课程的官方环境基于 Jupyter Notebook使用 Python 3。我们在本地搭建时不需要追求完全一致重要的是把核心依赖装好。下面是一个通用环境清单工具/库作用建议版本Python基础解释器3.9 或 3.10Jupyter Lab交互式编程环境最新即可pandas数据清洗与分析2.xnumpy数值计算1.26.xmatplotlib基础可视化3.xseaborn统计可视化0.13.xscikit-learn机器学习建模1.3.x 或 1.4.xpyspark分布式数据计算3.5.x需确认 JDK 兼容sqlite3 / SQLAlchemy本地 SQL 练习Python 自带 / 2.x需要注意的是pyspark依赖 Java 环境安装前要先确认机器上有 JDK 8 或 JDK 11。如果你只是先学 pandas 和机器学习可以暂时不装 Spark等学到后面再补齐避免环境问题干扰前期的学习节奏。2.2 使用 conda 创建虚拟环境我推荐使用 Anaconda 或 Miniconda 管理环境。虚拟环境最大的好处是隔离不会因为某一个项目的依赖版本冲突影响其他项目。创建命令如下conda create -n data100 python3.9 -y conda activate data100激活环境后安装基础数据科学库pip install pandas numpy matplotlib seaborn scikit-learn jupyterlab如果是在国内网络环境可以临时切换到清华镜像源加速pip install pandas numpy matplotlib seaborn scikit-learn jupyterlab -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后启动 Jupyter Labjupyter lab浏览器会自动打开一个 Notebook 工作台。到这里你的数据科学实验环境就基本可用了。2.3 练习项目目录结构Data 100 的作业和项目通常有一个相对固定的目录结构。我在本地学习时习惯这样组织data100-learning/ ├── data/ # 存放原始数据尽量只读 ├── notebooks/ # 每次实验的 notebook 文件 ├── scripts/ # 可复用的 Python 脚本 ├── models/ # 训练好的模型文件 └── requirements.txt # 依赖清单这个结构看起来简单但对长期学习非常重要。数据文件放进data目录后不要手动修改清洗后的结果输出到另一个文件可以避免反复下载原始数据。3. pandas 数据清洗数据分析的第一道工序3.1 数据加载与初步探查pandas 最常用的入口是read_csv和read_excel。实际业务中Excel 文件往往带有多级表头、合并单元格等脏数据读取时要额外注意参数。下面是一个读取 CSV 文件的示例import pandas as pd df pd.read_csv(data/sample_data.csv) print(df.head()) # 查看前 5 行 print(df.shape) # 查看行数、列数 print(df.info()) # 查看各列类型和非空数量 print(df.describe()) # 数值列统计摘要读取 Excel 时如果表不在第一个 sheet需要指定sheet_namedf_excel pd.read_excel(data/sample_data.xlsx, sheet_nameSheet1, header0)很多新手容易忽略info()的输出。仔细观察会发现某列明明应该是数值类型显示却是object这就提示需要做数据类型转换。3.2 数据类型转换与缺失值处理数据类型转换是 pandas 数据清洗中非常高频的操作也是最容易出错的环节。最典型的例子是Excel 中“金额”列可能是文本格式带有千分位逗号或货币符号直接建模会报错或者结果离谱。处理思路如下# 假设 salary 列是 $12,000 这样的字符串 df[salary] ( df[salary] .astype(str) .str.replace($, ) .str.replace(,, ) ) df[salary] pd.to_numeric(df[salary], errorscoerce)to_numeric的errorscoerce参数非常关键当某个值无法转换为数字时会变成NaN而不是让整个程序崩溃。之后再用填充或删除策略处理这些缺失值。缺失值的常见处理方式有三种直接删除适用缺失比例很低且分布随机的场景。填充均值/中位数适用数值型数据注意要按业务逻辑决定。填充众数/特殊占位符适用类别型数据例如“未知”。示例# 删除关键字段为空的记录 df_clean df.dropna(subset[salary]) # 用中位数填充缺失薪资 df[salary] df[salary].fillna(df[salary].median()) # 用字符串填充缺失类别 df[department] df[department].fillna(Unknown)3.3 分组聚合与数据连接分组聚合是 pandas 的精髓。Data 100 的作业里有大量类似这样的问题按地区统计平均销售额、按类别统计最大值、按时间统计数量等。# 按部门分组计算平均薪资和人数 result df.groupby(department)[salary].agg([mean, count]) print(result)agg函数可以同时传入多个聚合函数比逐个计算再合并高效得多。如果想对多列使用不同聚合方式可以使用字典result df.groupby(department).agg( avg_salary(salary, mean), max_age(age, max), emp_count(employee_id, count), )多张表连接时merge是最常用的操作。Data 100 中大量作业都要求把订单表和用户表、商品表连接起来order_df pd.read_csv(data/orders.csv) user_df pd.read_csv(data/users.csv) merged pd.merge(order_df, user_df, onuser_id, howleft)how参数控制连接方式inner是内连接只保留两边都有的匹配项left是左连接以左边的表为主表右边匹配不上就填NaN。3.4 一个完整清洗片段下面这段代码综合演示了一次标准清洗流程import pandas as pd df pd.read_csv(data/raw_sales.csv) # 1. 删除完全重复的行 df df.drop_duplicates() # 2. 日期列转 datetime df[order_date] pd.to_datetime(df[order_date], errorscoerce) # 3. 金额列转数值 df[amount] pd.to_numeric(df[amount], errorscoerce) # 4. 删除金额缺失的数据 df df.dropna(subset[amount, order_date]) # 5. 新增“月份”特征 df[month] df[order_date].dt.to_period(M) # 6. 按月统计销售额 monthly_sales ( df.groupby(month)[amount] .sum() .reset_index() ) print(monthly_sales.head())这段代码的核心思路是先把数据变成“可计算的类型”再做过滤和聚合。很多人拿到数据后直接groupby结果因为类型不对出现各种诡异报错根源就是跳过了步骤 2 到步骤 4。4. SQL 查询结构化数据的基本盘4.1 为什么要学 SQLpandas 能处理表数据但在真实企业环境中数据往往存放在数据库中无法直接把整个表下载下来用 pandas 打开。因此 SQL 是数据科学从业者的基本功。SQL 和 pandas 的思维有一个重要差异pandas 是在内存里通过对象方法操作数据SQL 是声明式查询你描述“想要什么”数据库优化器决定“怎么算”。初学 SQL 时不需要关心底层机制但要建立“表连接、聚合、过滤”三大思维。4.2 SQL 基础查询示例以商品销售表为例-- 查询销售额大于 10000 的订单 SELECT order_id, customer_id, amount FROM orders WHERE amount 10000 ORDER BY amount DESC LIMIT 100;分组统计是考试和面试中的高频点SELECT region, ROUND(AVG(amount), 2) AS avg_amount, COUNT(DISTINCT customer_id) AS customer_cnt FROM orders WHERE order_date 2024-01-01 GROUP BY region HAVING AVG(amount) 5000 ORDER BY avg_amount DESC;注意区分WHERE和HAVINGWHERE是在分组前过滤原始行HAVING是在分组后过滤聚合结果。如果要对聚合后的均值做条件筛选必须用HAVING。4.3 窗口函数分组 TopN 问题Data 100 中涉及多个数据分析题目窗口函数在作业中经常出现。窗口函数可以在不改变行数的情况下计算排名、累计值、移动平均非常实用。比如要求“每个区域销售额最高的前 3 个订单”SELECT * FROM ( SELECT order_id, region, amount, ROW_NUMBER() OVER ( PARTITION BY region ORDER BY amount DESC ) AS rn FROM orders ) t WHERE rn 3;ROW_NUMBER()按区域分组后给行编号外层再过滤掉编号大于 3 的记录。RANK()、DENSE_RANK()也可以排名区别在于遇到同分时排名是否跳跃。4.4 慢查询优化提示热词里经常出现“慢 SQL 优化”。对数据科学场景来说最常见的优化手段是不要在WHERE中对索引列使用函数例如WHERE YEAR(order_date) 2024会导致索引失效应写成WHERE order_date 2024-01-01 AND order_date 2025-01-01。只SELECT需要的列不要随手SELECT *。大数据量表尽量先过滤再关联减小中间结果集。如果 SQL 非常慢先用EXPLAIN查看执行计划。要注意的是SQL 本身面临注入风险写动态 SQL 时必须使用参数化查询绝对不要直接拼接用户输入。5. 机器学习建模从特征到评估5.1 数据科学中的建模流程Data 100 中机器学习部分并不追求复杂的数学证明而是强调掌握“套路”。建模流程可以拆成四步把业务数据整理成特征矩阵X和预测目标y。把数据划分成训练集、验证集、测试集。在训练集上训练模型在验证集上调参。最后仅在测试集上评估一次报告泛化能力。很多初学者拿到数据后就直接训练完全不划分数据集最后用训练集准确率当模型效果这是一种严重的错误。5.2 划分数据集import pandas as pd from sklearn.model_selection import train_test_split df pd.read_csv(data/house_data.csv) X df.drop(columns[price]) y df[price] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )test_size0.2表示把 20% 数据留作测试集。random_state固定随机种子保证结果可复现这是实验的基本素养。如果分类问题各类别比例不均衡建议使用分层抽样X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy )5.3 线性回归与交叉验证以房价预测为例线性回归是最直观的基线模型from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline model make_pipeline( StandardScaler(), LinearRegression() ) model.fit(X_train, y_train) train_score model.score(X_train, y_train) test_score model.score(X_test, y_test) print(fR² on train: {train_score:.4f}) print(fR² on test: {test_score:.4f})make_pipeline把标准化和线性回归串联起来保证训练和预测时执行相同的预处理逻辑避免数据泄漏。交叉验证适合在小训练集上评估模型稳定性from sklearn.model_selection import cross_val_score scores cross_val_score(model, X_train, y_train, cv5, scoringr2) print(scores) print(f平均 R²: {scores.mean():.4f} ± {scores.std():.4f})5.4 使用随机森林处理非线性特征线性回归对特征与目标之间的线性关系要求较高。如果数据中存在明显的非线性模式可以改用随机森林from sklearn.ensemble import RandomForestRegressor rf RandomForestRegressor( n_estimators200, max_depth10, random_state42 ) rf.fit(X_train, y_train) feature_importance pd.Series( rf.feature_importances_, indexX_train.columns ).sort_values(ascendingFalse) print(feature_importance.head(10))随机森林还能输出特征重要性帮助理解哪些变量对预测结果影响最大。这一点在实际业务汇报中非常有用。6. Spark 大规模数据处理进阶但不可绕过的部分6.1 为什么要学 SparkData 100 把 Spark 放在课程后段说明它不是数据科学入门的第一优先级但对于数据量超过单机内存的场景Spark 是绕不过去的工具。pandas 的最大限制是数据必须加载到一台机器的内存中。假设你的服务器内存是 32GB而原始数据集有 100GBpandas 会直接报内存错误。Spark 的核心思路是把数据分片到多台机器上并行计算对外仍然提供类似 DataFrame 的编程接口。6.2 Spark DataFrame 入门PySpark 的 API 与 pandas 有相似之处但底层执行机制不同。以下是一个标准流程from pyspark.sql import SparkSession spark SparkSession.builder \ .appName(Data100 Demo) \ .getOrCreate() df spark.read.csv(data/sales.csv, headerTrue, inferSchemaTrue) # 查看数据形状 print(f分区数: {df.rdd.getNumPartitions()}) df.show(5) df.printSchema()过滤和聚合语法与 SQL 风格接近from pyspark.sql.functions import avg, sum result df.groupBy(region).agg( avg(amount).alias(avg_amount), sum(amount).alias(total_amount) ) result.orderBy(total_amount, ascendingFalse).show()运行 Spark 时控制台会打印大量日志这是正常现象。如果觉得日志过多可以调整日志级别spark.sparkContext.setLogLevel(WARN)6.3 Spark on YARN 的常见配置坑热词里有一条“spark on yarn cpu 只能用 1 个”这是很多同学部署 Spark 时遇到过的典型问题。在 YARN 模式下Spark 任务使用的 CPU、内存由 ResourceManager 根据容器配置分配。如果你发现每个 Executor 只申请到 1 个 vcore通常是因为提交任务时没有设置spark.executor.coresspark-submit \ --master yarn \ --deploy-mode cluster \ --num-executors 4 \ --executor-cores 2 \ --executor-memory 8g \ your_job.py不同集群调度器的默认值不同需要根据实际情况调整。学习方法上初学者不要纠结于复杂调优先能把 Spark 任务在本地或单机伪分布式模式跑通即可。6.4 pandas 与 Spark 的适用边界场景建议数据量在几个 GB 以内pandas Jupyter Notebook 足够数据量在几十 GB 到上百 GBSpark DataFrame 有优势需要复杂机器学习建模pandas scikit-learn 方便Spark MLlib 适合超大规模原始数据在 HDFS/S3 上Spark 可以分布式读取pandas 很难直接处理7. 综合实战从一份模拟数据走到模型预测为了把前面的内容串起来我用模拟数据演示一个完整项目。任务目标是根据房屋特征预测房价输出一份预测结果并评估模型效果。7.1 准备模拟数据为了便于复现这里直接用代码生成一份小型数据集而不是依赖外部文件import pandas as pd import numpy as np np.random.seed(42) n 2000 df pd.DataFrame({ area: np.random.randint(50, 180, n).astype(float), bedrooms: np.random.randint(1, 6, n).astype(float), age: np.random.randint(0, 40, n).astype(float), region: np.random.choice([east, west, south, north], n), price: np.random.randint(100, 1000, n).astype(float) }) # 人为加入一些缺失值 df.loc[df.sample(frac0.05).index, area] np.nan df.loc[df.sample(frac0.05).index, price] np.nan # 把地区转为数值特征便于建模 df pd.get_dummies(df, columns[region], prefixregion) # 删除价格缺失的样本 df df.dropna(subset[price]) print(df.shape) print(df.head())7.2 pandas 清洗与特征工程# 面积用中位数填充 df[area] df[area].fillna(df[area].median()) # 构造一个“楼龄平方”特征模拟非线性的房龄折旧 df[age_sq] df[age] ** 2 X df.drop(columns[price]) y df[price]特征工程不一定要很复杂但要有业务依据。这里构造age_sq是因为房龄对房价的影响往往不是线性的新楼盘和老年份房子价格差异更明显。7.3 SQL 查询练习如果你的环境里没有独立数据库可以使用 Python 内置的sqlite3来体验 SQLimport sqlite3 conn sqlite3.connect(:memory:) df.head(100).to_sql(house, conn, indexFalse, if_existsreplace) query SELECT region_east, ROUND(AVG(price), 2) AS avg_price, COUNT(*) AS cnt FROM house WHERE price IS NOT NULL GROUP BY region_east result pd.read_sql(query, conn) print(result) conn.close()这展示了 pandas 与 SQL 的无缝衔接DataFrame 写入 SQLite 内存库再用 SQL 查询做聚合适合练习 SQL 语法。7.4 建模与验证from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model RandomForestRegressor( n_estimators200, max_depth12, random_state42 ) model.fit(X_train, y_train) y_pred model.predict(X_test) rmse mean_squared_error(y_test, y_pred, squaredFalse) r2 r2_score(y_test, y_pred) print(f测试集 RMSE: {rmse:.4f}) print(f测试集 R²: {r2:.4f})预期会得到一组 RMSE 和 R² 的具体数值。由于数据是模拟生成的数值大小本身没有业务意义重点是整个流程可以顺畅跑通。7.5 保存结果实际项目中模型结果需要落地。使用 pandas 保存预测结果submission pd.DataFrame({ id: X_test.index, predicted_price: y_pred }) submission.to_csv(output/predictions.csv, indexFalse)至此这个迷你项目完整覆盖了数据生成、清洗、SQL 查询、特征工程、模型训练、评估、导出结果的整套流程也是 Data 100 课程学习路径的一个缩影。8. 常见问题与排查思路学习过程中环境问题和代码报错是消耗时间最多的地方。下面整理了我在实践中常见的问题问题现象常见原因解决思路ModuleNotFoundError: No module named pandasconda 环境没激活或库没安装到当前环境执行conda activate data100后重新安装ValueError: could not convert string to float数据中存在非数字字符先用pd.to_numeric转类型并设置errorscoerceJupyter 能启动但无法新建 Python 内核内核环境混乱在激活环境中执行python -m ipykernel install --user --name data100PySpark 启动时报log4j相关错误Spark 版本与 Java 版本不匹配或缺少日志配置确认 JDK 版本升级或重装匹配的pysparkSpark on YARN 每个 Executor 只有 1 个 vcore提交任务未指定executor-cores提交任务时增加--executor-cores 2训练集 R² 很高但测试集很低过拟合限制模型深度、增加正则化、增加训练数据量SQL 查询响应很慢索引失效或扫描全表数据量过大检查执行计划优化过滤条件排错顺序其实是有规律的先确认环境再确认数据格式最后才怀疑模型逻辑。很多nan出现在模型预测结果里往往不是模型问题而是训练数据在清洗时就没有处理干净。9. 数据科学工程最佳实践9.1 尽快让代码可复现数据科学项目最大的敌人是“无法复现”。同一份 Notebook换一台机器结果不同换一个随机种子结果也不同。建议固定随机种子import numpy as np import pandas as pd from sklearn.model_selection import train_test_split np.random.seed(42)所有关键依赖版本写入requirements.txt并对原始数据做只读保护。9.2 训练集、验证集、测试集必须严格分离Data 100 课程反复强调的一个观点测试集只能使用一次。如果你反复在测试集上调整模型实际上你已经在不知不觉中把测试集变成了训练集最终得到的评估结果会有水分。正确做法是先切出测试集放在一边模型调参全部在训练集和验证集上完成最后阶段才用测试集做一次最终评估。9.3 用管道封装预处理逻辑清洗代码很容易出现“训练时用了某种填充策略预测时忘了用”的低级错误。使用sklearn.pipeline.Pipeline能把预处理和模型封装为一个整体from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.linear_model import Ridge pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()), (model, Ridge(alpha1.0)) ]) pipe.fit(X_train, y_train) y_pred pipe.predict(X_test)这样测试集预测时同样会执行缺失值填充和标准化避免数据泄漏。9.4 日志与实验记录实际做数据分析时不要只依赖 notebook 里的 Markdown 写注释。建议建立一个简单的实验记录表格至少包含实验日期数据集版本特征清单模型参数评估指标备注CSV、Excel、飞书表格都行关键是记录下来。三个月后回看你会感谢当时的记录。9.5 注意数据安全与合规如果使用真实业务数据必须遵守最小权限原则只能访问完成任务所必需的数据列不能随意导出敏感字段。写 SQL 时使用参数化查询避免动态拼接用户的输入。训练模型前还要检查数据是否包含可识别个人身份的信息必要时脱敏后再处理。10. 下一步学习建议完成 Data 100 的学习以后最直接的进阶方向有三个如果你对机器学习算法底层感兴趣可以继续深入统计学习理论、常见模型推导例如李航的《统计学习方法》和周志华的《机器学习》都可以作为补充阅读。不过 Data 100 的学习经历会帮你更好地理解这些书里在解决什么问题而不是被公式吓退。如果你想往大数据工程方向发展下一步可以学 Hadoop HDFS、Spark SQL、Spark Streaming并学习在真实集群上提交作业。热词里提到的“Spark 集群搭建”“Spark on YARN 配置”就是这个阶段会碰到的问题。如果你是往数据科学应用方向发展可以尝试参加 Kaggle 或天池的入门比赛。Data 100 已经教会你最核心的流程接下来就是反复实战。建议从房价预测、销售预测这类表格型任务开始比直接做图像识别或 NLP 要友好得多。最后动手永远是学习数据科学最好的方式。你可以尝试把任意一份网上公开的 CSV 数据下载下来按本文的流程走一遍用 pandas 清洗用 SQL 做聚合用 scikit-learn 训练一个模型最后提交预测结果。只要跑通一次完整流程数据科学的整个核心脉络就真正建立起来了。如果本文对你有帮助可以收藏备用也欢迎在实际操作中遇到问题时回来对照排查。