Building an End-to-End Data Science Portfolio Project
TL;DR · AI 摘要
本文提供了一个完整的数据科学作品集项目构建指南,涵盖从业务问题定义到API部署的九个阶段。
核心要点
- 使用DoorDash配送时间预测项目作为完整案例贯穿全流程
- 通过SQL提取数据并用Python清洗,展示端到端数据处理
- 最终部署为API和仪表板,形成可展示的推荐系统
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- End-to-End Data Science Portfolio Project
- 业务问题定义
- DoorDash配送时间预测
- 技术实现流程
- SQL数据提取
- Python数据清洗
- 特征工程
- 模型训练与评估
- API部署
- 成果展示
- 交互式仪表板
金句 / Highlights
值得收藏与分享的关键句。
完整项目流程是简历无法证明的硬实力,notebook无法伪造的实战能力
使用DoorDash真实数据集,包含历史订单的市场ID、配送时间和商店信息等字段
最终输出包含API接口和可视化仪表板,形成完整的推荐决策系统
构建端到端数据科学作品集项目 - KDnuggets
publ: 2026年8月12日
- 博客热门文章
- 主题 人工智能 职业建议 计算机视觉 数据工程 数据科学 语言模型 机器学习 MLOps NLP 编程 Python SQL
- 数据集
- 活动
- 资源 快速参考指南 推荐 技术简报
- 广告
订阅电子报
#header end
/ad_wrapper
构建端到端数据科学作品集项目
大多数作品集只停留在笔记本阶段。让你的项目完整落地。
作者:Nate Rosidi,KDnuggets 市场趋势与SQL内容专家,2026年8月12日,数据科学
<div class="addthis_native_toolbox"></div>
真正能让人获得工作的项目会采取不同的做法。它们从商业问题出发,最终给出建议,并展示所有中间过程。从原始数据到部署应用的完整过程展示,是简历无法证明、笔记本无法伪造的关键价值。
为保持具体性,我们将全程使用一个真实项目:DoorDash配送时长预测数据项目。这是一个免费项目,你可以跟随实践并自行构建。
我们将通过StrataScratch内置的笔记本环境进行操作,这是一个集成的Marimo笔记本,你可以在项目页面点击"Start Solving"直接打开,无需任何安装准备。
接下来我们将这样操作:选取这个项目,完整经历真实数据科学项目的九个阶段:界定商业问题、使用SQL提取数据、用Python清洗数据、数据探索、特征工程、构建和评估模型,最终将结果部署为API和仪表板并给出建议。
每个阶段都是同一故事的不同章节,每个环节都是招聘经理可以直接看到的内容。最终你将获得一个模板,几乎可以套用任何项目。
# 从商业问题开始
在编写任何代码之前,先明确你要解决的实际问题。
DoorDash项目为我们提供了清晰的商业问题:给定一个订单,配送需要多长时间?这种界定方式很重要。它关注的是企业关心的内容,而非算法本身。
这是大多数作品集最容易出错的地方。
标题为"配送时间预测"的项目告诉招聘经理你在公司做了什么。标题为"XGBoost回归演示"的项目则说明你只是跟着教程操作。
围绕结果界定问题,并选择具有实际价值的领域:客户流失、预测、欺诈检测,或者像我们这样选择运营效率优化。
# 使用SQL提取数据
DoorDash项目为我们提供了CSV文件:historical_data.csv:
但这与现实世界的数据存储方式不同。在企业中,这个数据集会来自数据库,而你需要编写SQL来构建它。
我们通过前面提到的集成笔记本进行模拟,因为数据集已经导入(作为df)。我们直接使用SQL查询它。(如果是真正的数据库,查询语句会是FROM historical_data。)
SELECT
market_id,
created_at,
actual_delivery_time,
store_id,
store_primary_category,
order_protocol,
total_items,
subtotal,
total_onshift_dashers,
total_busy_dashers,
total_outstanding_orders
FROM df
WHERE actual_delivery_time IS NOT NULL
AND actual_delivery_time > created_at;输出结果:
market_id
created_at
actual_delivery_time
...
total_outstanding_orders
1
2015-02-06 22:24:17
/think
2015-02-06 23:27:16
21
2
2015-02-10 21:49:25
2015-02-10 22:56:29
3
2015-01-22 20:39:28
2015-01-22 21:09:09
0
2015-02-03 21:21:45
2015-02-03 22:13:00
2015-02-15 02:40:36
2015-02-15 03:20:26
9
2015-02-08 19:24:33
2015-02-08 20:01:41
23
这值得展示在你的作品集中。
与其默默地加载文件,不如描述生成数据集的查询:在订单、配送员和商店表之间进行的连接,用于过滤掉劣质行的 WHERE 条件,以及执行繁重过滤和连接操作的 GROUP BY 子句——然后将一个可供分析的表导入 Python,而不是原始数据转储。
# 在 Python 中清理数据
现在我们将数据导入 Python。这是数据科学工作中占 60% 到 80% 的不那么光鲜的阶段,跳过这个阶段是缺乏经验的最明显信号之一。
对于 DoorDash 数据而言,清理意味着计算我们的目标(实际配送时长等于配送时间戳减去订单创建时间戳),修复数据类型,并处理缺失值和不可能值。
我们使用 pandas 来完成这个任务,在作品集规模下这是正确的默认选择。
df["created_at"] = pd.to_datetime(df["created_at"])
df["actual_delivery_time"] = pd.to_datetime(df["actual_delivery_time"])
# 我们的目标:实际配送所花费的时间(以秒为单位)
df["delivery_duration_seconds"] = (
df["actual_delivery_time"] - df["created_at"]
).dt.total_seconds()
# 删除缺失值和不可能值
# 通常实际配送时间在 6 分钟到几小时之间
df2 = df[df["delivery_duration_seconds"].between(60, 3 * 3600)]
df3 = df2.dropna(subset=["delivery_duration_seconds"])
df3delivery_duration_seconds
3779.0
4024.0
1781.0
3075.0
…
2390.0
如果数据集大到足以导致内存压力,Polars 将是更快的多核替代方案,但对于此类项目,pandas 完全足够。
import polars as pl
df = pl.read_csv(
"historical_data.csv",
null_values=["NA"],
try_parse_dates=True
)
df = df.with_columns(
(pl.col("actual_delivery_time") - pl.col("created_at"))
.dt.total_seconds()
.alias("delivery_duration_seconds")
).filter(pl.col("delivery_duration_seconds") > 0)# 探索数据
探索性数据分析(EDA)是我们最终要讲述故事的起点。
工作流程简单且可重复:使用 df.info() 和 df.describe() 等方法总结数据,然后可视化分布和关系,最后记录令人惊讶的发现。
df3["delivery_minutes"] = df3["delivery_duration_seconds"] / 60
df3["delivery_minutes"].describe()请注意 df3 是上文 pandas 代码中清理后的数据集。
statistic
value
count
197283.0
mean
47.5
std
18.0
min
1.7
25%
35.1
50%
44.3
75%
56.3
max
179.8
对于配送时长,我们会查看其在不同市场、不同时间段以及配送员忙碌程度下的变化情况。我们使用 Matplotlib 和 Seaborn 绘制直方图、箱线图和散点图。
import matplotlib.pyplot as plt
import seaborn as sns
# 配送时间分布
sns.histplot(df3["delivery_minutes"].clip(upper=120), bins=50)
plt.xlabel("配送时长(分钟)")# 不同市场间的差异
df3.groupby("market_id")["delivery_minutes"].median().sort_values()46.9
43.3
5
43.4
6
43.6
44.1
4
44.4
目标是理解你正在预测的事物背后的驱动因素。
# 特征工程
原始列很少能成为最佳预测变量。特征工程是领域知识转化为模型输入的关键环节,它往往决定了一个项目是优秀还是平庸。
在DoorDash项目中,这是最有趣的阶段。我们构建了busy_dashers_ratio来衡量车队的紧张程度,以及结合驾驶时间和下单时间的estimated_non_prep_duration。
import numpy as np
df3["busy_dashers_ratio"] = (
df3["total_busy_dashers"]
/ df3["total_onshift_dashers"]
)
df3["estimated_non_prep_duration"] = (
df3["estimated_store_to_consumer_driving_duration"]
+ df3["estimated_order_place_duration"]
)
# 忙碌比例可能产生除零错误
df3 = df3.replace([np.inf, -np.inf], np.nan)
df3[
[
"busy_dashers_ratio",
"estimated_non_prep_duration",
]
].head()index | busy_dashers_ratio | estimated_non_prep_duration --- | --- | --- 0 | 0.424242 | 1307.0 1 | 2.000000 | 1136.0 2 | 0.000000 | 1.000000 3 | 735.0 | 1096.0
我们将市场和订单协议等分类列转换为虚拟变量。然后通过相关性热力图和方差膨胀因子(Variance Inflation Factor, VIF)处理信息重复的特征,剔除冗余特征。
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
numeric = [
"busy_dashers_ratio",
"estimated_non_prep_duration",
"total_items",
"subtotal",
"num_distinct_items",
"min_item_price",
"max_item_price",
"total_onshift_dashers",
"total_outstanding_orders",
]
categorical = ["market_id", "order_protocol"]
preprocess = ColumnTransformer([
("num", StandardScaler(), numeric),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical),
])
# 11个原始列经过编码后变成22个模型可用特征
preprocess.fit_transform(df3[numeric + categorical].dropna()).shape输出:
(175596, 22)将所有这些步骤封装到scikit-learn管道中,确保训练数据和新数据经过完全相同的处理流程,这能有效防止数据泄露。
# 构建模型
抵制直接使用复杂模型的冲动。
先从基线模型开始,即使是简单到预测平均配送时间的模型。如果真实模型无法超越这个基线,说明存在问题,需要尽早发现。
在此基础上,我们逐步尝试更强的模型:先使用岭回归等线性模型,然后是树模型,最后使用XGBoost进行梯度提升。
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyRegressor
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from xgboost import XGBRegressor
data = df3[numeric + categorical + ["delivery_duration_seconds"]].dropna()
X = data[numeric + categorical]
y = data["delivery_duration_seconds"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)
models = {
"Baseline (mean)": DummyRegressor(strategy="mean"),
"Ridge": Ridge(),
"XGBoost": XGBRegressor(
n_estimators=600,
learning_rate=0.05,
max_depth=7,
subsample=0.8,
colsample_bytree=0.8,
random_state=42
),
}
for name, model in models.items():
pipe = Pipeline([("pre", preprocess), ("model", model)])
pipe.fit(X_train, y_train)
rmse = mean_squared_error(y_test, pipe.predict(X_test)) ** 0.5
print(f"{name}: RMSE = {rmse:.0f} sec")模型
指标
基准(均值)
1074 秒
Ridge
927 秒
XGBoost
875 秒
基于树的模型通常在像这样的表格业务数据上表现最佳。在你的分析中,解释你为何选择特定方法。
这种推理是招聘经理用来判断你是否真正理解工具还是仅仅导入了它们的依据。
# 诚实评估
单一准确率数字无法证明任何问题。对于像配送时长这样的回归问题,我们报告误差指标(如均方根误差 RMSE),并让每个模型与基准模型和其他模型进行对比。
更重要的是诚实验证。应使用交叉验证而不是依赖一次幸运的训练-测试划分,且绝不要在测试集上调整模型,因为一旦这么做,你报告的分数就会变成乐观的虚构数据。
from sklearn.model_selection import cross_val_score
pipe = Pipeline([("pre", preprocess), ("model", models["XGBoost"])])
scores = cross_val_score(
pipe,
X,
y,
cv=5,
scoring="neg_root_mean_squared_error"
)
print("Fold RMSEs:", (-scores).round().astype(int))
print(f"CV RMSE: {-scores.mean():.0f} sec (+/- {scores.std():.0f})")指标
折叠 RMSEs
[900, 886, 867, 878, 882]
CV RMSE
883 秒 ±11 秒
对于分类问题,应同时报告精确率、召回率和F1值,而不仅仅是准确率。
# 部署模型
这就是大多数作品集通常停止的地方,这正是你继续深入的原因。将模型封装成 API 才能让其他人真正使用它。
我们使用 joblib 序列化训练好的模型,然后使用 FastAPI 创建小型服务,这几乎不需要任何努力就能获得请求验证和自动生成的文档。
import joblib
pipe.fit(X_train, y_train)
joblib.dump(pipe, "delivery_model.joblib")
# api.py
from fastapi import FastAPI
from pydantic import BaseModel
import joblib
import pandas as pd
app = FastAPI()
model = joblib.load("delivery_model.joblib")
class Order(BaseModel):
busy_dashers_ratio: float
estimated_non_prep_duration: float
total_items: int
subtotal: float
num_distinct_items: int
min_item_price: float
max_item_price: float
total_onshift_dashers: float
total_outstanding_orders: float
market_id: int
order_protocol: int
@app.post("/predict")
def predict(order: Order):
row = pd.DataFrame([order.model_dump()])
seconds = float(model.predict(row)[0])
return {"predicted_delivery_seconds": round(seconds)}向 /predict 发送 POST 请求现在会返回类似 {"predicted_delivery_seconds": 2472} 的结果。我们将所有内容打包到 Docker 容器中,并部署到某个公共位置,例如免费云服务层级。现在任何人都可以发送订单并获取预测的配送时间。
# 构建仪表盘
最终阶段将流程闭环回到第一阶段。并非所有查看你工作成果的人都会调用你的 API,因此给他们一个可点击的界面。我们使用 Streamlit 构建了一个小型仪表盘,这是将 Python 脚本转换为交互式应用的最快方式。
对于我们的项目,仪表盘允许用户输入订单详情并查看预测的配送时间,同时探索哪些因素会推高或压低预测值。
import streamlit as st
import joblib
import pandas as pd
model = joblib.load("delivery_model.joblib")
st.title("Delivery Duration Predictor")
order = {
"busy_dashers_ratio": st.slider(
"Busy dashers ratio",
0.0,
2.0,
0.5
),
"estimated_non_prep_duration": st.number_input(
"Non-prep duration (sec)",
value=900
),
"total_items": st.number_input(
"Total items",
value=4,
step=1
),
"subtotal": st.number_input(
"Subtotal (cents)",
value=3441
),
"num_distinct_items": st.number_input(
"Distinct items",
value=4,
step=1
),
"min_item_price": st.number_input(
"Min item price",
value=557
),
"max_item_price": st.number_input(
"Max item price",
value=1239
),
"total_onshift_dashers": st.number_input(
"On-shift dashers",
value=33
),
"total_outstanding_orders": st.number_input(
"Outstanding orders",
value=21
),
"market_id": st.selectbox(
"Market",
[1, 2, 3, 4, 5, 6]
),
"order_protocol": st.selectbox(
"Order protocol",
[1, 2, 3, 4, 5, 6, 7]
),
}
if st.button("Predict"):
seconds = float(
model.predict(
pd.DataFrame([order])
)[0]
)
st.metric(
"Predicted delivery time",
f"{seconds / 60:.1f} min"
)最后我们以所有优秀数据科学项目都应有的方式结束:提出建议。如果高 busy_dashers_ratio 是导致配送时间延长的主要因素,那么业务行动应该是调整高峰期的人力配置。以业务行动而非单纯的预测作为结尾。
生命周期是关键的区分点。任何人都可以训练一个模型,但很少有候选人能够将一个问题从SQL查询一直推进到部署的应用程序,并最终给出明确的建议。这种端到端的完整故事正是作品集所展示的,而简历本身永远无法单独体现这一点。
理解这个故事的两个部分各自证明了什么会有所帮助。早期阶段——定义问题、编写SQL、清洗和探索数据——展示了你能够将一个杂乱的商业问题转化为模型真正可以学习的内容。后期阶段——诚实评估、部署和构建仪表板——展示了你能够将模型从笔记本中取出,并展示给需要做决策的人。
大多数候选人只能完成其中一部分。能够同时完成两部分的人非常稀少,而你正在填补的正是这个缺口。贯穿始终的主线是第一阶段的商业问题定义:每个阶段都应该回指你最初提出的问题,并指向你最终给出的建议。
你不需要发明一个项目来练习这一点。我们在这里使用的DoorDash项目是StrataScratch数据项目中众多真实公司练习题之一,其他还包括来自Meta、Capital One、Google等公司的题目。
选择一个项目,将其完整地经历所有九个阶段,并诚实地记录下来,包括那些没有成功部分。这个单一完成的项目对你的求职帮助,将超过五个只停留在模型阶段的笔记本。
Nate Rosidi是一名数据科学家,同时负责产品战略。他还是教授分析学的兼职教授,也是StrataScratch的创始人,这是一个帮助数据科学家通过顶级公司真实面试题准备面试的平台。Nate撰写关于职业市场最新趋势的文章,提供面试建议,分享数据科学项目,并涵盖所有与SQL相关的内容。
更多相关内容
- 使用HuggingFace实现的端到端项目(易于实施)
- 构建端到端数据管道:从数据摄入到分析
- 使用Dask构建端到端数据管道
- 使用数据摄入开发端到端数据科学管道,…
- 使用Concrete ML实现模型训练和推理的端到端隐私保护
- 5个最佳端到端开源MLOps工具
<hr class="grey-line"><br> <div><h3>我们推荐的5个免费课程</h3><br> </div>
Mailchimp for WordPress v4.14.0 - https://wordpress.org/plugins/mailchimp-for-wp/
/ Mailchimp for WordPress插件
你可以从这里开始编辑。
如果评论已关闭。
<= 上一篇
下一篇 =>
#content end
<script type="text/javascript">kda_sid_write(kda_sid_n);</script>
最新文章
- 如何使用Python构建一个简单的AI网络爬虫 5篇有趣的智能代理AI论文阅读 构建一个流式本地AI代理 限制SLM输出空间以实现窄自动化优化 构建一个端到端的数据科学作品集项目 5种在Windows上安装Python的简便方法
热门文章
- 规格工程:提示工程之后的新技能
- 如何使用Python构建一个简单的AI网络爬虫
- 5篇有趣的智能代理AI论文阅读
- 构建一个端到端的数据科学作品集项目
- 5个免费课程学习现代AI和LLMs
- 构建一个流式本地AI代理
- 贡献开源项目的终极指南
- 3个视觉证明中央极限定理以建立你的直觉
- 5种在Windows上安装Python的简便方法
- 2026年最小的AI工程师工具包
#content_wrapper end
© 2026
Guiding Tech Media
|
关于
联系
广告
隐私
服务条款
发布于2026年8月12日,作者Nate Rosidi
空白
不,谢谢!
/.main_wrapper
<script defer type="text/javascript" src="https://s7.addthis.com/js/300/addthis_widget.js#pubid=gpsaddthis"></script>
noptimize
/noptimize