数据科学最初被称为统计分析,在它获得现在的名字之前,因为那是从数据中提取信息的主要方法。随着技术的最新进展,机器学习模型已被引入,扩展了我们分析和理解数据能力。市面上有许多机器学习模型,但您不必全部学会。最重要的是了解这些工具如何帮助您。
在这个为期7天的速成迷你课程中,您将通过示例学习如何使用最常见的机器学习模型来执行数据科学项目。本迷你课程侧重于数据科学的核心方面。它假设您已经收集并准备好数据以供使用。本课程专为熟悉Python编程并渴望了解常用数据科学工具(如pandas和scikit-learn)的从业者设计。虽然机器学习工程师的目标是开发模型,但数据科学家的目标是使用机器学习模型作为工具来解释数据。您将探索这些工具如何帮助获得有意义的见解并做出数据驱动的决策。
让我们开始吧!

下一级数据科学(7天迷你课程)
照片由 geraldo stanislas 拍摄。部分权利保留。
本迷你课程适合谁?
在开始之前,请确保您在正确的位置。下面的列表提供了本课程的目标人群的一般指南。如果您不完全符合这些要求,请不要担心——您可能只需要复习某些领域即可跟上。
- 有一定编程经验的开发者。 您应该熟悉编写Python代码并设置开发环境(这是前提条件)。您不需要成为一名熟练的程序员,但应该能够轻松安装包和编写脚本。
- 有基本机器学习知识的开发者。 您应该对机器学习模型有一般的了解,并对使用它们感到自在。您不必是每个模型的专家,但应该能够识别它们的优缺点。
- 熟悉数据科学工具的开发者。如果您使用过Jupyter Notebook或在Python中处理过数据,那会很有帮助。像pandas这样的库可以使数据处理更容易。您不必是任何特定库的专家,但应该熟悉使用不同的工具并编写代码来处理数据。
这个迷你课程不是一本数据科学的教科书。相反,它是一个基于项目的指南,将您一步一步地从经验最少的发展者转变为能够自信地演示如何完成数据科学项目的开发者。
迷你课程概述
本迷你课程分为 7 个部分。
每节课的设计时间约为普通开发人员 30 分钟。有些您可能会更快完成,有些您可能会选择深入研究并花费更多时间。
您可以根据自己的喜好快慢完成每个部分。一个舒适的时间表可能是七天内每天完成一节课。强烈推荐。
您将在接下来的 7 节课中涵盖的主题如下:
- 第1课:获取数据
- 第2课:查找用于线性回归的数字列
- 第3课:执行线性回归
- 第4课:解释因子
- 第5课:特征选择
- 第6课:决策树
- 第7课:随机森林与概率
这将非常有趣。
不过,您需要做一些工作:阅读、研究和编程。您想学会如何完成一个数据科学项目,对吧?
在评论中发布您的结果;我会为您加油!
坚持下去;不要放弃。
第01课:获取数据
本迷你课程我们将使用的数据集是 Kaggle 上提供的“所有国家数据集”。
该数据集描述了几乎所有国家的人口统计、经济、地理、健康和政治数据。这类数据中最著名的数据集是 CIA World Fact Book。从 World Fact Book 抓取数据应该能为您提供更全面、更及时的数据。但是,使用此 CSV 格式的数据集可以为您在构建网络爬虫时省去很多麻烦。
从 Kaggle 下载 此数据集(您可能需要注册一个帐户才能进行下载),您将找到 CSV 文件 All Countries.csv
。让我们用 pandas 来检查这个数据集。
1 2 3 4 |
import pandas as pd df = pd.read_csv("All Countries.csv") df.info() |
上面的代码将向屏幕打印一个表,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 194 entries, 0 to 193 Data columns (total 64 columns) # Column Non-Null Count Dtype --- ------ -------------- ----- 0 country 194 non-null object 1 country_long 194 non-null object 2 currency 194 non-null object 3 capital_city 194 non-null object 4 region 194 non-null object 5 continent 194 non-null object 6 demonym 194 non-null object 7 latitude 194 non-null float64 8 longitude 194 non-null float64 9 agricultural_land 193 non-null float64 ... 62 political_leader 187 non-null object 63 title 187 non-null object dtypes: float64(48), int64(6), object(10) memory usage: 97.1+ KB |
在上面,您可以看到数据集的基本信息。例如,顶部显示有 194 个条目(行)在此 CSV 文件中。表格显示有 64 列(索引为 0 到 63)。有些列是数字型的,例如 latitude,有些则不是,例如 capital_city。pandas 中的“object”数据类型通常表示它是字符串类型。您还可以知道存在一些缺失值,例如在 agricultural_land
列中,在 194 个条目中只有 193 个非空值,这意味着有一行在该列中缺失值。
让我们更详细地查看数据集,例如以前五个行为样本
1 |
df.head(5) |
这将以表格形式显示数据集的前五行。
您的任务
这是数据集的基本探索。但是,使用 head()
函数并不总是合适的(例如,当输入数据已排序时)。还有用于类似目的的 tail()
函数。然而,运行 df.sample(5)
通常会更有帮助,因为它会随机抽取 5 行。尝试使用此函数。此外,如您从上面的输出中看到的,列被裁剪到了屏幕宽度。如何修改上述代码以显示样本中所有列?
提示:pandas 中有一个 to_string()
函数,您也可以调整通用打印选项 display.max_columns
。
在下一课中,您将学习如何为线性回归准备数据。
第02课:查找用于线性回归的数字列
让我们开始一个最基本任务:根据其他因素预测一个国家的 GDP,使用线性回归。但在使用数据之前,确保没有不良数据是很重要的。例如,如果您使用线性回归,所有数字都必须有效,以便进行加法和乘法。这意味着不应存在 NaN
(“非数字”)或无穷大。通常,NaN
用于表示缺失值。
填充数据集中的缺失值很容易。例如,在 pandas 中,您可以将所有缺失值(NaN
)替换为零
1 |
df_filled = df.fillna(0) |
但为什么要用零呢?最佳填充值取决于列。有时,预定义的值是合适的,而有时,使用其他非缺失值的平均值是更好的方法。
另一种策略是忽略任何有缺失值的列。您可以通过计算 null 或 NaN
条目的数量来识别有缺失值的列
1 |
print(df.isnull().sum().sort_values(ascending=False).to_string()) |
您会看到上面的输出打印
1 2 3 4 5 6 7 8 9 |
internally_displaced_persons 121 central_government_debt_pct_gdp 74 hiv_incidence 61 energy_imports_pct 56 ... urban_population_under_5m 0 rural_land 0 urban_land 0 country 0 |
要列出所有没有缺失值的列,您可以过滤缺失值计数为零的行。
1 2 |
df_null_count = df.isnull().sum().sort_values(ascending=False) print(df_null_count[df_null_count == 0].index) |
对于线性回归,数据必须是数字型的。您可以使用 describe()
方法来识别数字列,该方法计算数字列的基本统计数据。
1 |
print(df.describe().columns) |
通过结合这些步骤,您可以使用 set
交集列出所有既是数字型又没有缺失值的列。
1 |
print(list(set(df.describe().columns) & set(df_null_count[df_null_count == 0].index))) |
您的任务
查看上面的集合列,GDP 缺失了。如果您查看 CSV 文件中的数据,这很合理——有一个国家没有 GDP 数据。您能使用 pandas 确定是哪个国家吗?
接下来,让我们查找缺失值少于三个的列,然后删除在这些列中具有缺失值的国家。您如何在 Python 中做到这一点?应该有一种简单的方法,只需几行代码即可筛选出 pandas DataFrame。
在下一课中,您将使用上面筛选出的数字列运行线性回归。
第03课:执行线性回归
让我们从 DataFrame 开始。我们将识别在整个数据集中缺失值少于三个的数字列。
1 2 3 4 5 6 7 |
df_null_count = df.isnull().sum().sort_values(ascending=False) good_cols = list(set(df_null_count[df_null_count <= 3].index) & set(df.describe().columns)) print(good_cols) df_cleaned = df.dropna(axis="index", how="any", subset=good_cols).copy() print(df_cleaned) |
现在,让我们关注 good_cols
中列出的列。您认为人口在预测 GDP 方面有多大作用?毕竟,人口较多的国家通常 GDP 较高。
为了找出答案,我们可以使用 scikit-learn 来构建一个线性模型。
1 2 3 4 5 6 7 8 9 |
来自 sklearn.linear_model 导入 LinearRegression model = LinearRegression(fit_intercept=True) X = df_cleaned[["population"]] Y = df_cleaned["gdp"] model.fit(X, Y) print(model.coef_) print(model.intercept_) print(model.score(X, Y)) |
最后一行打印的分数是线性回归模型的 $R^2$ 值。最佳可能分数是 1.0,而 0.0 的值表示预测变量 X
与 y
独立。在此情况下,我们获得的 $R^2$ 分数为 0.34,不算很高。
让我们尝试将更多列添加到 X
中,看看额外的预测变量是否能改进模型。
1 2 3 4 5 6 7 |
model = LinearRegression(fit_intercept=True) X = df_cleaned[["population", "rural_population", "median_age", "life_expectancy"]] Y = df_cleaned["gdp"] model.fit(X, Y) print(model.coef_) print(model.intercept_) print(model.score(X, Y)) |
$R^2$ 分数增加到 0.66,这是一个显著的改进。您还可以检查线性回归模型的系数。农村人口有一个负系数,这意味着较高的人口与较低的 GDP 相关。
您的任务
没有什么是阻止您将所有数字列用于线性回归的。您能试试吗?在这种情况下,$R^2$ 分数是多少?哪些因素与 GDP 正相关?您如何确定这一点?
在下一课中,您将学习如何解释线性回归模型的系数。
第04课:解释因子
让我们尝试运行一个线性回归模型,使用所有相关因素来预测预期寿命。为了确定哪些列可用,我们识别那些没有缺失值的列。
1 2 3 4 |
df_null_count = df.isnull().sum().sort_values(ascending=False) good_cols = list(set(df_null_count[df_null_count <= 3].index) & set(df.describe().columns)) print(good_cols) |
这表明将使用的列是以下这些:
1 2 3 4 5 6 7 |
['renewable_energy_consumption_pct', 'rural_land', 'urban_population_under_5m', 'women_parliament_seats_pct', 'electricity_access_pct', 'gdp', 'rural_population', 'birth_rate', 'population_female', 'fertility_rate', 'urban_land', 'nitrous_oxide_emissions', 'press', 'democracy_score', 'life_expectancy', 'urban_population', 'agricultural_land', 'longitude', 'methane_emissions', 'population', 'internet_pct', 'population_male', 'hospital_beds', 'land_area', 'median_age', 'net_migration', 'latitude', 'death_rate', 'forest_area', 'co2_emissions'] |
接下来,我们将预测变量定义为目标变量(预期寿命)之外的所有列。
1 2 3 4 5 6 |
X = df_cleaned[[x for x in good_cols if x != "life_expectancy"]] Y = df_cleaned["life_expectancy"] model.fit(X, Y) print(model.coef_) print(model.intercept_) print(model.score(X, Y)) |
为了方便地将每个系数与其对应的列匹配,我们将它们一起打印。
1 2 |
for col, coef in zip(X.columns, model.coef_): print("%s: %.3e" % (col, coef)) |
有些因素具有负系数,这意味着它们对预期寿命有负面影响。例如,较高的死亡率会降低预期寿命,这符合预期。有些系数非常小——例如,net_migration
的数量级为 $10^{-6}$,这意味着它实际上对目标变量没有影响。
您的任务
既然有些特征没有影响,为什么不将它们从回归中移除呢?如何自动执行此操作?提示:编写一个循环,迭代地添加“最佳特征”并比较 $R^2$ 分数的增加。
在下一课中,您将学习如何自动查找最佳特征子集。
第05课:特征选择
在上一课中,您使用所有可用因素预测了预期寿命。现在,让我们改进回归模型,使其更具“可解释性”。具体来说,让我们确定影响预期寿命的前五个因素。
特征选择的方法有很多,顺序特征选择是最容易理解的方法之一。它使用贪婪算法来评估所有可能的组合,直到选择出目标数量的特征。让我们试试吧。
1 2 3 4 5 6 7 8 9 10 11 12 |
from sklearn.feature_selection import SequentialFeatureSelector # 初始化线性回归模型 model = LinearRegression(fit_intercept=True) # 执行顺序特征选择器 sfs = SequentialFeatureSelector(model, n_features_to_select=5) X = df_cleaned[[x for x in good_cols if x != "life_expectancy"]] Y = df_cleaned["life_expectancy"] sfs.fit(X, Y) # 使用默认值 cv=5 selected_feature = list(X.columns[sfs.get_support()]) print("Feature selected for highest predictability:", selected_feature) |
这些是顺序特征选择器选出的五个最佳特征。现在让我们重新构建模型并检查系数。
1 2 3 4 5 6 7 8 |
model = LinearRegression(fit_intercept=True) X = df_cleaned[selected_feature] Y = df_cleaned["life_expectancy"] model.fit(X, Y) print(model.score(X, Y)) for col, coef in zip(X.columns, model.coef_): print("%s: %.3e" % (col, coef)) print("Intercept:", model.intercept_) |
这将产生以下结果:
1 2 3 4 5 6 7 |
0.9248375749867905 electricity_access_pct: 3.798e-02 birth_rate: 1.319e-01 press: 3.290e-01 median_age: 9.035e-01 death_rate: -1.118e+00 Intercept: 51.251243580962864 |
这表明预期寿命随着电力接入和平均年龄的增加而增加。直观地说,预期寿命高的国家会有更高的平均年龄。然而,这也凸显了回归模型的一个弱点:它们无法检测到“数据泄露”,即某些预测变量可能冗余或具有误导性,最终使模型的作用减小。
这就是数据科学的艺术——在运行算法之前仔细而智能地清理输入数据,以避免“垃圾进,垃圾出”的问题。
接下来,让我们将 GDP、土地面积和其他一些列转换为它们的“人均”版本,并重新运行特征选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
per_capita = ["gdp", "land_area", "forest_area", "rural_land", "agricultural_land", "urban_land", "population_male", "population_female", "urban_population", "rural_population"] for col in per_capita: df_cleaned[col] = df_cleaned[col] / df_cleaned["population"] col_to_use = per_capita + [ "nitrous_oxide_emissions", "methane_emissions", "fertility_rate", "hospital_beds", "internet_pct", "democracy_score", "co2_emissions", "women_parliament_seats_pct", "press", "electricity_access_pct", "renewable_energy_consumption_pct"] model = LinearRegression(fit_intercept=True) sfs = SequentialFeatureSelector(model, n_features_to_select=6) X = df_cleaned[col_to_use] Y = df_cleaned["life_expectancy"] sfs.fit(X, Y) # 使用默认值 cv=5 selected_feature = list(X.columns[sfs.get_support()]) print("Feature selected for highest predictability:", selected_feature) |
现在,让我们使用选定的特征检查回归系数。运行前面的代码将得到
1 2 3 4 5 6 7 8 |
0.7854421025889131 gdp: 1.076e-04 forest_area: -2.357e+01 fertility_rate: -2.155e+00 internet_pct: 3.464e-02 press: 3.032e-01 electricity_access_pct: 6.548e-02 Intercept: 66.44197315903226 |
这表明人均 GDP 是预期寿命的最强预测因子,这很有道理——富裕国家往往拥有更好的医疗保健。有趣的是,森林面积似乎与预期寿命呈负相关,可能表明与城市化有关。新闻自由、互联网接入和电力接入都与预期寿命呈正相关,因为它们反映了社会发展水平。
您的任务
这一课表明,数据科学并非纯粹是机械的——它需要直觉来有效地预处理数据以获得更好的模型性能。我们在这里没有解决的一个方面是回归前的标准化数据。例如,人均 GDP 以美元计价,而其他因素以百分比表示,这可能会夸大系数差异。
您能否尝试重新缩放这些因子并重新运行上述代码?这会改变选定的特征集吗?它会影响回归模型的 $R^2$ 分数吗?
在下一课中,您将了解决策树。
第06课:决策树
如果线性回归是数据科学家为任何任务尝试的第一个模型,那么决策树将是第二个。它是另一个简单易懂的模型。然而,它对于不同类型的问题更有效:分类。
让我们探索一下北半球和南半球的国家是否存在差异。首先,我们需要在数据集中创建一个标签。
1 |
df_cleaned["north"] = df_cleaned["latitude"] > 0 |
现在,让我们训练一个简单的决策树模型作为这个新列的分类器,使用上一课中选择的特征。在 scikit-learn 中,语法与线性回归几乎相同。
1 2 3 4 5 6 7 |
from sklearn.tree import DecisionTreeClassifier model = DecisionTreeClassifier(max_depth=3) X = df_cleaned[col_to_use] Y = df_cleaned["north"] model.fit(X, Y) print(model.score(X, Y)) |
决策树分类器的分数代表平均准确率。在分析此准确率之前,让我们检查一下此数据集中有多少国家。
1 |
print(Y.value_counts()) |
这将产生以下输出:
1 2 3 4 |
north True 147 False 40 Name: count, dtype: int64 |
如果北半球和南半球的国家数量相等,随机猜测的准确率将是 50%。然而,由于数据不平衡,如果模型总是预测北半球,准确率将是 78%。这意味着模型的性能略好于随机猜测。
这并不意味着模型毫无用处。在这种情况下,我们使用它来证明选定的特征并不是分类国家的强大预测因子。换句话说,仅基于这些特征,北半球和南半球的国家之间没有显著差异。
您的任务
您可以通过可视化决策树来查看它使用哪些因素。虽然 scikit-learn 有一个内置的绘图函数,但 Python 模块 dtreeviz
提供了更好的可视化效果。尝试运行下面的代码。模型使用了哪些因素?
在下一课中,您将把决策树扩展为随机森林。
第07课:随机森林与概率
如果您已经尝试过决策树,可以将其扩展为森林以提高准确性。有几种方法可以从单个树构建森林。例如,您可以使用重采样数据集(即,为每棵树选择随机行子集)来训练多棵树。另一种方法是使用随机特征子集(即,列)来训练树。
如果您不需要广泛的微调,那么构建随机森林很简单。
1 2 3 4 5 6 7 |
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(n_estimators=5, max_depth=3) X = df_cleaned[col_to_use] Y = df_cleaned["north"] model.fit(X, Y) print(model.score(X, Y)) |
使用 10 棵树而不是 1 棵树会稍微降低准确性。这是随机森林的固有特性,因为它们不使用所有数据进行训练,这意味着不能保证随机森林会优于单棵决策树。然而,这一结果与我们之前的观察一致:北半球和南半球国家之间可能没有太大差异。
可视化随机森林需要单独检查每棵决策树。您可以使用 `model.estimators_` 访问森林中的树。
上面创建的随机森林是决策树的集合,它们“投票”以确定最终结果。Scikit-learn 还提供了一个使用梯度提升算法的替代实现。理解详细差异不是必需的,因为函数语法保持不变。
1 2 3 4 5 6 7 |
from sklearn.ensemble import GradientBoostingClassifier model = GradientBoostingClassifier(n_estimators=5, max_depth=3) X = df_cleaned[col_to_use] Y = df_cleaned["north"] model.fit(X, Y) model.score(X, Y) |
尽管决策树和随机森林在本教程中被用作分类器,但它们并未提供确切的分类结果。特别是,`GradientBoostingClassifier` 假定输出是数值型的。因此,模型的原生输出是每个预测类的概率。您可以按如下方式检索这些概率:
1 |
print(model.predict_proba(X)) |
这会为每个输入行返回一行概率。通常,您对概率最高的类别感兴趣,您可以使用 `predict()` 来获取该类别。
1 |
print(model.predict(X)) |
您还可以通过计算其预测的平均概率来确定模型在预测国家属于北半球还是南半球方面的平均置信度。
1 2 3 |
import numpy as np print(np.mean(model.predict_proba(X)[range(len(X)), model.predict(X).astype(int)])) |
上面的代码从模型中提取预测的类别,将其与相应的概率匹配,然后计算平均值。这表明模型并未区分北半球和南半球,因为计算出的值并不比随机猜测更好。
您的任务
Scikit-learn 不是梯度提升分类器的主要库。更常见的选择是 XGBoost。您将如何使用 XGBoost 重写上述分类器?在 XGBoost 的情况下,应该如何设置 `n_estimators` 和 `max_depth` 超参数?
这是最后一课。
结束!(看看你已经走了多远)
您做到了。干得好!
花点时间回顾一下您已经走了多远。
- 您发现了 scikit-learn 如何帮助您完成数据科学项目。
- 您学习了如何使用机器学习模型来解释数据。
- 您尝试了线性回归和决策树模型,并看到了这些简单模型仍然很有用。
不要轻视这一点,您在短时间内取得了长足的进步。这仅仅是您数据科学之旅的开始。请继续练习和提升您的技能。
总结
您对这个迷你课程的学习情况如何?
您喜欢这个速成课程吗?
您有任何问题吗?有没有遇到什么难点?
告诉我。在下面留言。
你好 Adrian 和 Jason,
我正在学习这门课程,但是……
我正在运行 Python 3.12.3,并且似乎 sklearn 已弃用,并且指示应使用 scipy。
代码如何更改才能进行 LinearRegression?
此致,
你好 Phlip……请提供您遇到的任何错误的具体措辞。有些“警告”很常见,在某些特殊情况下可以忽略。总的来说,我们还希望更正代码,以确保在部署到生产环境的应用程序中没有任何警告。
如果您发现代码无法正常工作,请告知我们,我们可以提供建议!
你好 James,
当我尝试安装 sklearn 时,我实际上遇到了错误,并假设代码将无法正常工作。
我尝试了代码,然后才意识到 sklearn 实际上已安装并且代码工作正常。
Collecting sklearn
Using cached sklearn-0.0.post12.tar.gz (2.6 kB)
Preparing metadata (setup.py) … error
error: subprocess-exited-with-error
× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [15 lines of output]
“sklearn” PyPI 包已被弃用,请使用“scikit-learn”
而不是“sklearn”用于 pip 命令。
以下是在主要用例中修复此错误的方法
– 使用“pip install scikit-learn”而不是“pip install sklearn”
– 将“sklearn”替换为“scikit-learn”在您的 pip requirements 文件中
(requirements.txt, setup.py, setup.cfg, Pipfile 等…)
– 如果“sklearn”包被您的其中一个依赖项使用,
那就太好了,如果您花一些时间找出哪个包使用了
“sklearn”而不是“scikit-learn”并将其报告给他们的问题跟踪器
– 作为最后的手段,设置环境变量
SKLEARN_ALLOW_DEPRECATED_SKLEARN_PACKAGE_INSTALL=True 以避免此错误
更多信息可在
https://github.com/scikit-learn/sklearn-pypi-package
[end of output]
注意:此错误源自子进程,很可能不是 pip 的问题。
error: metadata-generation-failed
× 在生成包元数据时遇到错误。
╰─> 请参阅上面的输出。
注意:这是上面提到的包的问题,而不是 pip 的问题。
提示:请参阅上面的详细信息。
我很抱歉我没有在报告前尝试示例
此致,
Phlip
我认为您 `pip install` 了错误的包。请参阅 https://pypi.ac.cn/project/scikit-learn/,名称应该是 scikit-learn,而不是 sklearn。
此迷你课程是否有解决方案的 PDF?
你好 Donald……这是一个很好的建议!虽然我们目前没有这样的文件,但我们随时乐意帮助您处理您可能正在使用的任何代码示例。