Python 数据科学(一)
到本章结束时,您将能够:使用各种 Python 机器学习库处理缺失数据和异常值执行数据集成,将来自不同来源的数据汇集在一起执行数据转换,将数据转换为机器可读的形式对数据进行缩放,以避免不同量级值所带来的问题将数据拆分为训练集和测试集描述不同类型的机器学习描述机器学习模型的不同性能评估指标本章介绍了数据科学,并涵盖了构建机器学习模型中涉及的各种过程,特别关注预处理部分。我们生活在一个充满数据的世界中
原文:
annas-archive.org/md5/b306e51c73948c57f772d5af5f61eb39
译者:飞龙
第一章:第一章
数据科学与数据预处理简介
学习目标
到本章结束时,您将能够:
-
使用各种 Python 机器学习库
-
处理缺失数据和异常值
-
执行数据集成,将来自不同来源的数据汇集在一起
-
执行数据转换,将数据转换为机器可读的形式
-
对数据进行缩放,以避免不同量级值所带来的问题
-
将数据拆分为训练集和测试集
-
描述不同类型的机器学习
-
描述机器学习模型的不同性能评估指标
本章介绍了数据科学,并涵盖了构建机器学习模型中涉及的各种过程,特别关注预处理部分。
介绍
我们生活在一个充满数据的世界中。因此,能够理解和处理数据是绝对必要的。
数据科学是一个处理数据描述、分析和预测的领域。举个我们日常生活中的例子:每天,我们都会在手机上使用多个社交媒体应用。这些应用收集并处理数据,以便为每个用户创造更个性化的体验——例如,向我们展示可能感兴趣的新闻文章,或根据我们的位置信息调整搜索结果。数据科学的这一分支被称为机器学习。
机器学习是计算机通过程序化学习和统计表示来完成任务的过程,无需人工干预。换句话说,它是通过模式和推断来教计算机自己执行任务,而不是依赖于明确的指令。一些常见的机器学习算法应用包括电子邮件过滤、计算机视觉和计算语言学。
本书将专注于使用 Python 进行机器学习及数据科学的其他方面。Python 是数据科学中一种流行的编程语言,因为它多功能且相对易用。它还拥有多个现成的库,非常适合用于数据处理。
Python 库
在本书中,我们将使用各种 Python 库,包括 pandas、Matplotlib、Seaborn 和 scikit-learn。
pandas
pandas 是一个开源包,具有许多用于加载和处理数据的功能,以便为机器学习任务做好准备。它还具有用于分析和操纵数据的工具。可以使用 pandas 从多种格式读取数据。本书主要使用 CSV 数据。要读取 CSV 数据,您可以使用 read_csv()
函数,并将 filename.csv
作为参数传入。以下是示例代码:
>>> import pandas as pd
>>> pd.read_csv("data.csv")
在前面的代码中,pd
是给 pandas 起的别名。为 pandas 起别名并非强制要求。要可视化一个 pandas DataFrame,您可以使用 head()
函数来列出前五行数据。接下来的练习中会展示这一点。
注意
请访问以下链接了解更多关于 pandas 的信息:pandas.pydata.org/pandas-docs/stable/
。
NumPy
NumPy 是 Python 提供的主要包之一。它主要用于科学计算相关的实践,特别是在进行数学运算时。它包含的工具使我们能够处理数组和数组对象。
Matplotlib
Matplotlib 是一个数据可视化包。它对于在二维空间中绘制数据点非常有用,可以借助 NumPy 实现。
Seaborn
Seaborn 也是一个基于 matplotlib 的数据可视化库。使用 Seaborn 创建的可视化在图形效果上比 matplotlib 更具吸引力。
scikit-learn
scikit-learn 是一个用于机器学习的 Python 包。它的设计方式使其可以与 Python 中的其他数值和科学库互操作,从而实现算法的实现。
这些现成可用的库吸引了开发者的兴趣,特别是在数据科学领域。现在我们已经介绍了 Python 中的各种库,接下来我们将探索构建机器学习模型的路线图。
构建机器学习模型的路线图
构建机器学习模型的路线图非常直接,包含五个主要步骤,下面进行解释:
-
数据预处理
这是构建机器学习模型的第一步。数据预处理是指在将数据输入模型之前对数据进行的转换。它处理的是将无法使用的原始数据转化为干净、可靠数据的技术。
由于数据收集通常不是以受控方式进行的,原始数据通常包含异常值(例如,年龄=120)、无意义的数据组合(例如,模型:自行车,类型:四轮车)、缺失值、尺度问题等。因此,原始数据不能直接输入机器学习模型,因为它可能会影响结果的质量。因此,这是数据科学过程中最重要的一步。
-
模型学习
在对数据进行预处理并将其拆分为训练集/测试集(稍后会详细介绍)之后,我们进入建模阶段。模型只是一些被称为算法的明确定义的方法,这些算法使用预处理过的数据来学习模式,之后可以用来做预测。学习算法有不同的类型,包括监督学习、半监督学习、无监督学习和强化学习。这些将在后面讨论。
-
模型评估
在此阶段,模型通过特定的性能指标进行评估。通过这些指标,我们可以进一步调整模型的超参数以改进模型。这个过程称为超参数优化。我们将重复这一步骤,直到对模型的性能满意为止。
-
预测
一旦我们对评估步骤的结果满意,我们将进入预测阶段。当训练好的模型接触到新的数据集时,它会进行预测。在商业环境中,这些预测可以与决策者共享,以做出有效的商业决策。
-
模型部署
机器学习的整个过程不仅仅止于模型构建和预测。它还涉及利用模型构建带有新数据的应用程序。根据商业需求,部署可能是一个报告,或者是一些需要执行的重复性数据科学步骤。部署后,模型需要定期进行适当的管理和维护,以确保其正常运行。
本章主要关注预处理部分。我们将讨论数据预处理中的不同任务,如数据表示、数据清理等。
数据表示
机器学习的主要目标是构建能够理解数据并发现潜在模式的模型。为了实现这一目标,将数据以计算机能够理解的方式进行输入是非常重要的。为了将数据输入到模型中,它必须以表格或矩阵的形式表示,且具有所需的维度。在预处理能够正确开始之前,将数据转换为正确的表格形式是第一步。
以表格形式表示的数据
数据应该安排在由行和列组成的二维空间中。这种数据结构使得理解数据和发现问题变得容易。以下是一些存储为 CSV(逗号分隔值)文件的原始数据示例:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_01.jpg
图 1.1:CSV 格式的原始数据
相同数据在表格中的表示如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_02.jpg
图 1.2:表格格式的 CSV 数据
如果你对比 CSV 格式和表格格式中的数据,你会发现两者都有缺失值。我们将在本章后续部分讨论如何处理这些缺失值。要加载 CSV 文件并将其作为表格处理,我们使用 pandas 库。这里的数据被加载到称为 DataFrame 的表格中。
注意
要了解更多关于 pandas 的信息,请访问以下链接:pandas.pydata.org/pandas-docs/version/0.15/tutorials.html
。
自变量和目标变量
我们使用的数据框(DataFrame)包含可以分为两类的变量或特征。这些是自变量(也称为预测变量)和因变量(也称为目标变量)。自变量用于预测目标变量。顾名思义,自变量应该彼此独立。如果它们不独立,需要在预处理(清理)阶段加以处理。
自变量
这些是 DataFrame 中所有的特征,除了 目标变量。它们的尺寸是 (m, n),其中 m 是观测值的数量,n 是特征的数量。这些变量必须服从正态分布,并且不应包含以下内容:
-
缺失值或 NULL 值
-
高度分类的数据特征或高基数特征(这些术语将在后面详细讨论)
-
异常值
-
不同尺度的数据
-
人为错误
-
多重共线性(相互关联的自变量)
-
非常大的独立特征集(自变量过多,难以管理)
-
稀疏数据
-
特殊字符
特征矩阵和目标向量
一条数据称为标量。多个标量组成一个向量,多个向量组成一个矩阵。矩阵是通过行和列来表示的。特征矩阵数据由独立的列组成,而目标向量则依赖于特征矩阵的列。为了更好地理解这一点,我们来看一下以下表格:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_03.jpg
图 1.3:包含汽车详细信息的表格
正如你在表格中看到的,有多个列:汽车型号、汽车容量、汽车品牌和汽车价格。除了汽车价格之外,所有列都是自变量,代表特征矩阵。汽车价格是因变量,它依赖于其他列(汽车型号、汽车容量和汽车品牌)。它是一个目标向量,因为它依赖于特征矩阵的数据。在下一部分,我们将通过一个基于特征和目标矩阵的练习来全面理解。
注意
所有练习和活动将主要在 Jupyter Notebook 中进行开发。建议为不同的作业保持一个单独的 notebook,除非有特别的要求。另外,为了加载样本数据集,将使用 pandas 库,因为它能以表格形式显示数据。其他加载数据的方法将在后续章节中解释。
练习 1:加载样本数据集并创建特征矩阵和目标矩阵
在本次练习中,我们将把 House_price_prediction
数据集加载到 pandas DataFrame 中,并创建特征矩阵和目标矩阵。House_price_prediction
数据集来自 UCI 机器学习库。该数据收集自美国的多个郊区,包含 5,000 条记录和与房屋相关的 6 个特征。按照以下步骤完成此练习:
注意
House_price_prediction
数据集可以在以下位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/USA_Housing.csv。
-
打开一个 Jupyter notebook,并添加以下代码来导入 pandas:
import pandas as pd
-
现在我们需要将数据集加载到 pandas DataFrame 中。由于数据集是一个 CSV 文件,我们将使用
read_csv()
函数来读取数据。添加以下代码来实现:dataset = "https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/USA_Housing.csv" df = pd.read_csv(dataset, header = 0)
正如你在上面的代码中看到的,数据存储在名为
df
的变量中。 -
要打印数据框的所有列名,我们将使用
df.columns
命令。在笔记本中编写以下代码:df.columns
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_04.jpg
图 1.4:数据框中存在的列列表
-
数据集包含 n 个数据点。我们可以使用以下命令找到行数的总数:
df.index
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_05.jpg
图 1.5:数据框中的总索引
如前图所示,我们的数据集包含 5000 行,索引从 0 到 5000。
注意
你可以在 pandas 中使用
set_index()
函数将某一列转换为数据框中行的索引。这有点像使用该列中的值作为行标签。Dataframe.set_index('column name', inplace = True')'
-
让我们将
Address
列设置为索引,然后将其重置回原始数据框。pandas 库提供了set_index()
方法,将列转换为数据框中的行索引。添加以下代码来实现:df.set_index('Address', inplace=True) df
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_06.jpg
图 1.6:带有索引的地址列的数据框
set_index()
函数中的inplace
参数默认为False
。如果将其值更改为True
,那么我们执行的任何操作都会直接修改数据框的内容,而不会创建副本。 -
要重置给定对象的索引,我们使用
reset_index()
函数。编写以下代码来实现:df.reset_index(inplace=True) df
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_07.jpg
图 1.7:重置索引的数据框
注意
索引就像是给行和列起的名字。行和列都有索引。你可以通过行/列号或者行/列名来索引。
-
我们可以使用行号和列号来检索前四行和前三列数据。这可以通过 pandas 中的
iloc
索引器来完成,该索引器通过索引位置检索数据。添加以下代码来实现:df.iloc[0:4 , 0:3]
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_08.jpg
图 1.8:包含四行和三列的数据集
-
要使用标签检索数据,我们使用
loc
索引器。添加以下代码来检索收入和年龄列的前五行:df.loc[0:4 , ["Avg. Area Income", "Avg. Area House Age"]]
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_09.jpg
图 1.9:包含五行和两列的数据集
-
现在创建一个名为
X
的变量来存储独立特征。在我们的数据集中,我们将把除了价格以外的所有特征视为独立变量,并使用drop()
函数将它们包括进来。完成后,打印出X
变量的前五个实例。可以添加以下代码来实现:X = df.drop('Price', axis=1) X.head()
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_10.jpg
图 1.10:显示特征矩阵前五行的数据集
注意
默认情况下,获取头部的实例数量为五,因此如果你没有指定数量,它将默认输出五个观测值。前面的截图中的 axis 参数表示你是否想从行(axis = 0)或列(axis = 1)中删除标签。
-
使用
X.shape
命令打印你新创建的特征矩阵的形状。添加以下代码来实现这一功能:X.shape
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_11.jpg
图 1.11:特征矩阵的形状
在前面的图中,第一个值表示数据集中观测值的数量(5000),第二个值表示特征的数量(6)。
-
同样,我们将创建一个名为
y
的变量,用来存储目标值。我们将使用索引来获取目标列。索引允许你访问一个更大元素的部分。在这种情况下,我们想从df
数据框中获取名为 Price 的列。然后,我们希望打印出该变量的前 10 个值。添加以下代码来实现这一功能:y = df['Price'] y.head(10)
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_12.jpg
图 1.12:显示目标矩阵前 10 行的数据集
-
使用
y.shape
命令打印你新变量的形状。形状应该是一维的,长度仅等于观测值的数量(5000)。添加以下代码来实现这一功能:y.shape
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_13.jpg
图 1.13:目标矩阵的形状
你已成功创建了数据集的特征矩阵和目标矩阵。你已完成建立预测模型过程中的第一步。这个模型将从特征矩阵(X
中的列)中学习模式,并了解它们如何与目标向量(y
)中的值相匹配。这些模式可以用于基于新房屋的特征,从新数据中预测房价。
在下一部分,我们将探讨更多的数据预处理步骤。
数据清理
数据清理包括填补缺失值和处理不一致的过程。它可以检测到损坏的数据,并进行替换或修改。
缺失值
如果你想掌握成功管理和理解数据的技能,理解缺失值的概念是非常重要的。让我们看看下图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_14.jpg
图 1.14:银行客户信用数据
正如你所看到的,这些数据来自一家银行;每一行代表一个独立的客户,每一列包含他们的详细信息,如年龄和信用额度。有些单元格中要么是NA,要么是空白。这些都是缺失数据。银行需要知道每个客户的每一项信息。如果任何信息缺失,银行就难以预测是否能够向该客户提供贷款。
处理缺失数据
智能地处理缺失数据将帮助构建一个强健的模型,能够应对复杂的任务。处理缺失数据有多种方法。接下来我们将看看其中的一些方法。
删除数据
检查缺失值是数据预处理中的第一步,也是最重要的一步。模型无法接受包含缺失值的数据。这是一个非常简单且常用的方法来处理缺失值:如果缺失值出现在某行中的位置,就删除该行;或者如果某一列的缺失数据超过 70%-75%,就删除该列。同样,阈值不是固定的,取决于你希望修复多少数据。
这种方法的好处在于它快速且简单,并且在很多情况下,缺少数据比错误的数据更好。缺点是,你可能会丢失重要的信息,因为你是基于几个缺失值删除整个特征的。
练习 2:删除缺失数据
在这个练习中,我们将把Banking_Marketing.csv
数据集加载到 pandas DataFrame 中,并处理缺失数据。这个数据集与葡萄牙一家银行的直接营销活动有关。营销活动包括打电话给客户,尝试让他们订阅某一特定产品。数据集包含了每个被联系客户的详细信息,以及他们是否订阅了该产品。请按照以下步骤完成这个练习:
注意
Banking_Marketing.csv
数据集可以在此位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv。
-
打开一个 Jupyter 笔记本。插入一个新单元格,并添加以下代码以导入 pandas 并获取
Banking_Marketing.csv
数据集:import pandas as pd dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv' #reading the data into the dataframe into the object data df = pd.read_csv(dataset, header=0)
-
一旦获取了数据集,打印出每一列的数据类型。为此,可以使用 pandas DataFrame 的
dtypes
属性:df.dtypes
上述代码会生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_15.jpg
图 1.15:每个特征的数据类型
-
现在,我们需要找到每一列的缺失值。为此,我们使用 pandas 提供的
isna()
函数:df.isna().sum()
上述代码会生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_16.jpg
图 1.16:数据集中每列的缺失值
在上面的图中,我们可以看到有三列数据缺失,分别是
age
、contact
和duration
。age列有两个缺失值,contact列有六个缺失值,duration列有七个缺失值。 -
一旦你弄清楚所有缺失的细节,我们就从 DataFrame 中删除所有缺失的行。为此,我们使用
dropna()
函数:#removing Null values data = data.dropna()
-
为了检查缺失值是否仍然存在,可以使用
isna()
函数:df.isna().sum()
上面的代码会生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_17.jpg
图 1.17:数据集中每列没有缺失值
你已经成功地从 DataFrame 中删除了所有缺失数据。在接下来的章节中,我们将介绍第二种处理缺失数据的方法,即使用填补法。
均值/中位数/众数填补
对于数值数据,我们可以计算其均值或中位数,并用结果替换缺失值。对于类别(非数值)数据,我们可以计算其众数来替代缺失值。这就是所谓的填补法(imputation)。
使用填补法(imputation)而不是直接删除数据的好处在于,它可以防止数据丢失。缺点是你无法知道在特定情况下,使用均值、中位数或众数的准确性如何。
让我们来看一个练习,使用填补法来解决缺失数据问题。
练习 3:填补缺失数据
在这个练习中,我们将把Banking_Marketing.csv
数据集加载到 pandas 的 DataFrame 中,并处理缺失数据。我们将使用填补法。请按照以下步骤完成这个练习:
注意
Banking_Marketing.csv
数据集可以在以下位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv。
-
打开一个 Jupyter 笔记本并添加一个新单元格,将数据集加载到 pandas 的 DataFrame 中。添加以下代码来实现这一点:
import pandas as pd dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv' df = pd.read_csv(dataset, header=0)
-
使用
age
列的均值填补缺失的数值数据。为此,首先使用 pandas 的mean()
函数找到age
列的均值,然后打印出来:mean_age = df.age.mean() print(mean_age)
上面的代码会生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_18.jpg
图 1.18:年龄列的均值
-
一旦完成,使用
fillna()
函数用均值填补缺失数据。可以通过以下代码来完成:df.age.fillna(mean_age, inplace=True)
-
现在我们用持续时间列的中位数填补缺失的数值数据。为此,首先使用 pandas 的
median()
函数找到持续时间列的中位数。添加以下代码来实现这一点:median_duration = df.duration.median() print(median_duration)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_19.jpg
图 1.19:持续时间的中位数
-
使用
fillna()
函数,用持续时间的中位数填补缺失数据。df. duration.fillna(median_duration,inplace=True)
-
使用
mode()
函数,将contact
列的类别数据填补为其众数。为此,首先使用 pandas 的mode()
函数找到contact
列的众数。添加以下代码来实现这一点:mode_contact = df.contact.mode()[0] print(mode_contact)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_20.jpg
图 1.20:联系方式的众数
-
使用
fillna()
函数用众数填充联系方式列的缺失数据。添加以下代码来实现:df.contact.fillna(mode_contact,inplace=True)
与均值和中位数不同,列中可能有多个众数。因此,我们只取第一个众数,其索引为 0。
你已经成功地以不同的方式填充了缺失数据,并使数据完整且清洁。
数据清理的另一个部分是处理异常值,我们将在下一节讨论。
异常值
异常值是指与其他数据的分布相比,非常大或非常小的值。我们只能在数值数据中找到异常值。箱型图是一种有效的识别数据集异常值的方法,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01.21.jpg
图 1.21:箱型图中的异常值示例
注意
异常值不一定是坏数据!借助商业理解和客户互动,你可以判断是否要移除或保留异常值。
让我们通过一个简单的例子来学习如何查找异常值。考虑一个地方在不同时间的温度样本数据集:
71, 70, 90, 70, 70, 60, 70, 72, 72, 320, 71, 69
现在我们可以执行以下操作:
-
首先,我们对数据进行排序:
60,69, 70, 70, 70, 70, 71, 71, 72, 72, 90, 320
-
接下来,我们计算中位数(Q2)。中位数是排序后的中间数据。
在这里,排序后中间的项是 70 和 71。
中位数是*(70 + 71) / 2 = 70.5*
-
然后我们将计算下四分位数(Q1)。Q1 是数据集前半部分的中位数。
数据的前半部分 =
60, 69, 70, 70, 70, 70
下 6 个数据点的第 3 和第 4 个都等于 70。
平均值是*(70 + 70) / 2 = 70*
Q1 = 70
-
然后计算上四分位数(Q3)。
Q3 是数据集后半部分的中位数。
数据的后半部分 =
71, 71, 72, 72, 90, 320
上 6 个数据点的第 3 和第 4 个是 72 和 72。
平均值是*(72 + 72) / 2 = 72*
Q3 = 72
-
然后我们找出四分位距(IQR)。
IQR = Q3 – Q1 = 72 – 70
IQR = 2
-
接下来,我们找出上限和下限。
下限 = Q1 – 1.5 (IQR) = 70 – 1.5(2) = 67
上限 = Q3 + 1.5 (IQR) = 71.5 + 1.5(2) = 74.5
我们的边界值 = 67 和 74.5
任何低于下限并大于上限的数据点都是异常值。因此,我们例子中的异常值是 60、90 和 320。
练习 4:查找和移除数据中的异常值
在本练习中,我们将加载german_credit_data.csv
数据集到 pandas DataFrame 中,并去除异常值。该数据集包含 1,000 条记录和 20 个分类/符号属性,由 Hofmann 教授准备。每条记录代表一个从银行借贷的人。根据这些属性,每个人被分类为良好或不良信用风险。按照以下步骤完成本练习:
注意
german_credit_data.csv
数据集的链接可以在这里找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/german_credit_data.csv。
-
打开一个 Jupyter 笔记本并添加一个新单元格。编写以下代码来导入必要的库:pandas、NumPy、matplotlib 和 seaborn。获取数据集并将其加载到 pandas DataFrame 中。添加以下代码来实现:
import pandas as pd import numpy as np %matplotlib inline import seaborn as sbn dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/german_credit_data.csv' #reading the data into the dataframe into the object data df = pd.read_csv(dataset, header=0)
在前面的代码中,
%matplotlib inline
是一个魔法函数,如果我们希望图表在笔记本中可见,它是必不可少的。 -
这个数据集包含一个
Age
列。让我们绘制Age
列的箱型图。为此,使用 seaborn 库中的boxplot()
函数:sbn.boxplot(df['Age'])
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_22.jpg
图 1.22:年龄列的箱型图
我们可以在箱型图中看到一些数据点是异常值。
-
箱型图使用 IQR 方法来显示数据和异常值(数据的形状)。但是,为了打印出异常值,我们使用数学公式来检索它。添加以下代码来使用 IQR 方法查找
Age
列的异常值:Q1 = df["Age"].quantile(0.25) Q3 = df["Age"].quantile(0.75) IQR = Q3 - Q1 print(IQR) >>> 15.0
在前面的代码中,Q1 是第一四分位数,Q3 是第三四分位数。
-
现在通过添加以下代码,我们可以找到上边界和下边界,并打印所有高于上边界和低于下边界的数据。添加以下代码来实现:
Lower_Fence = Q1 - (1.5 * IQR) Upper_Fence = Q3 + (1.5 * IQR) print(Lower_Fence) print(Upper_Fence) >>> 4.5 >>> 64.5
-
为了打印所有高于上边界和低于下边界的数据,添加以下代码:
df[((df["Age"] < Lower_Fence) |(df["Age"] > Upper_Fence))]
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_23.jpg
图 1.23:基于年龄列的异常数据
-
筛选掉异常数据并只打印潜在数据。为此,只需使用
~
运算符否定前面的结果:df = df[~((df ["Age"] < Lower_Fence) |(df["Age"] > Upper_Fence))] df
前面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_24.jpg
图 1.24:基于年龄列的潜在数据
你已经成功地使用 IQR 找到了异常值。在接下来的部分,我们将探讨另一种预处理方法——数据集成。
数据集成
到目前为止,我们已经确保移除数据中的杂质,使其变得干净。现在,下一步是将来自不同来源的数据合并,以获得一个统一的结构,包含更有意义和有价值的信息。如果数据被拆分到不同的来源,这通常是必需的。为了简单起见,假设我们有一些 CSV 格式的数据,存放在不同的地方,所有这些数据都描述的是相同的场景。比如,我们有一些关于某个员工的数据存储在一个数据库中。我们不能指望所有关于员工的数据都存在同一个表格中。可能员工的个人信息在一个表格中,员工的项目历史在第二个表格中,员工的考勤数据在另一个表格中,等等。因此,如果我们想对员工进行分析,我们需要将所有员工数据整合到一个共同的地方。这一过程被称为数据整合。为了进行数据整合,我们可以使用 merge
函数合并多个 pandas DataFrame。
让我们通过一个数据整合的练习来清晰地理解它。
练习 5:数据整合
在本练习中,我们将合并来自两个数据集(student.csv
和 marks.csv
)的学生信息。student
数据集包含诸如 Age
(年龄)、Gender
(性别)、Grade
(年级)和 Employed
(是否就业)等列。marks.csv
数据集包含 Mark
(分数)和 City
(城市)等列。Student_id
列在两个数据集之间是共同的。按照以下步骤完成此练习:
注意
student.csv
数据集可以在此位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/student.csv。
marks.csv
数据集可以在此位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/mark.csv。
-
打开一个 Jupyter notebook 并添加一个新单元格。编写以下代码以导入 pandas 并将
student.csv
和marks.csv
数据集加载到df1
和df2
pandas DataFrame 中:import pandas as pd dataset1 = "https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/student.csv" dataset2 = "https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/mark.csv" df1 = pd.read_csv(dataset1, header = 0) df2 = pd.read_csv(dataset2, header = 0)
-
要打印第一个 DataFrame 的前五行,请添加以下代码:
df1.head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_25.jpg
图 1.25:第一个 DataFrame 的前五行
-
要打印第二个 DataFrame 的前五行,请添加以下代码:
df2.head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_26.jpg
图 1.26:第二个 DataFrame 的前五行
-
Student_id
在两个数据集中都是共同的。使用pd.merge()
函数对两个 DataFrame 进行数据整合,基于Student_id
列,然后打印新 DataFrame 的前 10 个值:df = pd.merge(df1, df2, on = 'Student_id') df.head(10)
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_27.jpg
图 1.27:合并后的 DataFrame 的前 10 行
这里,df1
数据框的数据与 df2
数据框的数据进行了合并。合并后的数据存储在一个名为 df
的新数据框中。
我们现在已经学习了如何进行数据集成。在下一部分中,我们将探索另一个预处理任务——数据转换。
数据转换
之前,我们已经看到如何将来自不同来源的数据合并成一个统一的数据框。现在,我们有很多列,包含不同类型的数据。我们的目标是将数据转换为机器学习可以处理的格式。所有机器学习算法都是基于数学的。因此,我们需要将所有列转换为数值格式。在此之前,让我们看一下我们所拥有的不同类型的数据。
从更广泛的角度来看,数据可以分为数值型数据和分类数据:
-
数值型:顾名思义,这些是可以量化的数字数据。
-
分类数据:数据是字符串或非数值型数据,具有定性特征。
数值型数据进一步分为以下几类:
-
离散型:简单来说,任何可以计数的数值数据称为离散型数据,例如家庭中的人数或班级中的学生人数。离散型数据只能取某些特定值(如 1、2、3、4 等)。
-
连续型:任何可测量的数值数据都称为连续型数据,例如一个人的身高或到达某地所花费的时间。连续型数据可以取几乎任何值(例如,1.25、3.8888 和 77.1276)。
分类数据进一步分为以下几类:
-
有序型:任何有顺序的分类数据称为有序型分类数据,例如电影评分(优秀、好、差、最差)和反馈(满意、不差、差)。你可以将有序数据看作是可以在一个量表上标记的内容。
-
名义型:任何没有顺序的分类数据称为名义型分类数据。例如,性别和国家。
在这些不同类型的数据中,我们将重点讨论分类数据。在下一部分中,我们将讨论如何处理分类数据。
处理分类数据
有些算法可以很好地处理分类数据,例如决策树。但大多数机器学习算法不能直接处理分类数据。这些算法要求输入和输出都必须是数值型的。如果需要预测的输出是分类数据,那么在预测之后,我们将其从数值数据转换回分类数据。让我们讨论一下处理分类数据时面临的一些关键挑战:
-
高基数:基数指的是数据的唯一性。在这种情况下,数据列将包含大量不同的值。一个很好的例子是用户 ID——在一个包含 500 个不同用户的表格中,用户 ID 列将有 500 个独特的值。
-
稀有出现:这些数据列可能包含非常少见的变量,因此它们对模型的影响不足。
-
频繁出现:数据列中可能存在一个类别,它出现的次数很多,但方差很小,这样的类别对模型没有影响。
-
无法适应:这些未处理的类别数据无法适应我们的模型。
编码
为了解决与类别数据相关的问题,我们可以使用编码。这是将类别变量转换为数字形式的过程。在这里,我们将介绍三种简单的编码类别数据的方法。
替换
这是一种将类别数据替换为数字的技术。这是一个简单的替换过程,不涉及太多逻辑处理。让我们通过一个练习更好地理解这一点。
练习 6:将类别数据简单替换为数字
在本练习中,我们将使用前面看到的student
数据集。我们将数据加载到 pandas 数据框中,并将所有类别数据简单地替换为数字。按照以下步骤完成此练习:
注意
student
数据集可以在以下位置找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/student.csv。
-
打开一个 Jupyter notebook 并添加一个新单元格。写入以下代码以导入 pandas,并将数据集加载到 pandas 数据框中:
import pandas as pd import numpy as np dataset = "https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/student.csv" df = pd.read_csv(dataset, header = 0)
-
找到类别列,并将其与其他数据分开。为此,使用 pandas 中的
select_dtypes()
函数:df_categorical = df.select_dtypes(exclude=[np.number]) df_categorical
上述代码生成的输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_28.jpg
图 1.28:数据框的类别列
-
找到
Grade
列中的不同唯一值。为此,使用 pandas 中的unique()
函数,指定列名:df_categorical['Grade'].unique()
上述代码生成的输出如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_29.jpg
图 1.29:Grade 列中的唯一值
-
找到每个类别列的频率分布。为此,使用
value_counts()
函数对每一列进行处理。此函数返回对象中唯一值的计数:df_categorical.Grade.value_counts()
这一步的输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_30.jpg
](https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_30.jpg)
图 1.30:Grade 列中每个唯一值的总计数
-
对于
Gender
列,写入以下代码:df_categorical.Gender.value_counts()
这段代码的输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_31.jpg
](https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_31.jpg)
图 1.31:Gender 列中每个唯一值的总计数
-
类似地,对于
Employed
列,写入以下代码:df_categorical.Employed.value_counts()
这段代码的输出结果如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_32.jpg
](https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_32.jpg)
图 1.32:Employed 列中每个唯一值的总计数
-
替换
Grade
列中的条目。将1st class
替换为1
,2nd class
替换为2
,3rd class
替换为3
。为此,使用replace()
函数:df_categorical.Grade.replace({"1st Class":1, "2nd Class":2, "3rd Class":3}, inplace= True)
-
替换
Gender
列中的条目。将Male
替换为0
,将Female
替换为1
。为此,使用replace()
函数:df_categorical.Gender.replace({"Male":0,"Female":1}, inplace= True)
-
替换
Employed
列中的条目。将no
替换为0
,将yes
替换为1
。为此,使用replace()
函数:df_categorical.Employed.replace({"yes":1,"no":0}, inplace = True)
-
一旦完成三列的所有替换操作,我们需要打印数据框。添加以下代码:
df_categorical.head()
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_33.jpg
图 1.33:替换后的数值数据
你已经成功地使用简单的手动替换方法将分类数据转换为数值数据。接下来我们将看看另一种编码分类数据的方法。
标签编码
这是一种技术,其中我们将分类列中的每个值替换为从 0 到 N-1 的数字。例如,假设我们在一列中有一份员工名字列表。进行标签编码后,每个员工名字将分配一个数值标签。但这对于所有情况可能不适用,因为模型可能会将数值视为分配给数据的权重。标签编码是处理有序数据的最佳方法。scikit-learn 库提供了LabelEncoder()
,可以帮助进行标签编码。接下来我们将在下一节中查看一个练习。
练习 7:使用标签编码将分类数据转换为数值数据
在本次练习中,我们将把Banking_Marketing.csv
数据集加载到 pandas 数据框中,并使用标签编码将分类数据转换为数值数据。请按照以下步骤完成此练习:
注意
Banking_Marketing.csv
数据集可以在以下位置找到:https://github.com/TrainingByPackt/Master-Data-Science-with-Python/blob/master/Chapter%201/Data/Banking_Marketing.csv。
-
打开一个 Jupyter 笔记本并添加一个新单元格。编写代码导入 pandas 并将数据集加载到 pandas 数据框中:
import pandas as pd import numpy as np dataset = 'https://github.com/TrainingByPackt/Master-Data-Science-with-Python/blob/master/Chapter%201/Data/Banking_Marketing.csv' df = pd.read_csv(dataset, header=0)
-
在进行编码之前,删除所有缺失的数据。为此,使用
dropna()
函数:df = df.dropna()
-
使用以下代码选择所有非数值类型的列:
data_column_category = df.select_dtypes(exclude=[np.number]).columns data_column_category
要了解选择的内容,请参见以下截图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_34.jpg
图 1.34:数据框中的非数值列
-
打印新数据框的前五行。添加以下代码来执行此操作:
df[data_column_category].head()
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_35.jpg
图 1.35:列中的非数值类型
-
遍历
category
列,并使用LabelEncoder()
将其转换为数值数据。为此,导入sklearn.preprocessing
包并使用LabelEncoder()
类来转换数据:#import the LabelEncoder class from sklearn.preprocessing import LabelEncoder #Creating the object instance label_encoder = LabelEncoder() for i in data_column_category: df[i] = label_encoder.fit_transform(df[i]) print("Label Encoded Data: ") df.head()
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_36.jpg
图 1.36:非数值列转换为数值形式
在上面的截图中,我们可以看到所有的值已经从类别数据转换为数值数据。这里,原始值已被转换并替换为新编码的数据。
你已经成功使用 LabelEncoder
方法将类别数据转换为数值数据。在接下来的章节中,我们将探讨另一种编码方法:独热编码。
独热编码
在标签编码中,类别数据被转换为数值数据,并为每个值分配标签(如 1、2 和 3)。使用这些数值数据进行分析的预测模型有时可能会误认为这些标签具有某种顺序(例如,模型可能认为标签 3 比标签 1 “更好”,这是错误的)。为了避免这种混淆,我们可以使用独热编码。在这里,标签编码的数据进一步被分成 n 列,其中 n 表示标签编码时生成的唯一标签的总数。例如,假设通过标签编码生成了三个新标签。那么,在执行独热编码时,列将分为三部分。所以,n 的值为 3。让我们通过一个练习来进一步澄清。
练习 8:使用独热编码将类别数据转换为数值数据
在本练习中,我们将把 Banking_Marketing.csv
数据集加载到 pandas 数据框中,并使用独热编码将类别数据转换为数值数据。按照以下步骤完成此练习:
注意
Banking_Marketing
数据集可以在这里找到:github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv
。
-
打开 Jupyter 笔记本并添加一个新单元格。编写代码导入 pandas 并将数据集加载到 pandas 数据框中:
import pandas as pd import numpy as np from sklearn.preprocessing import OneHotEncoder dataset = 'https://github.com/TrainingByPackt/Master-Data-Science-with-Python/blob/master/Chapter%201/Data/Banking_Marketing.csv' #reading the data into the dataframe into the object data df = pd.read_csv(dataset, header=0)
-
在进行编码之前,移除所有缺失数据。为此,使用
dropna()
函数:df = df.dropna()
-
使用以下代码选择所有非数值型的列:
data_column_category = df.select_dtypes(exclude=[np.number]).columns data_column_category
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_37.jpg
图 1.37:数据框的非数值列
-
打印新数据框的前五行。添加以下代码来实现:
df[data_column_category].head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_38.jpg
图 1.38:列的非数值值
-
迭代这些类别列,并使用
OneHotEncoder
将其转换为数值数据。为此,导入sklearn.preprocessing
包,并使用OneHotEncoder()
类进行转换。在执行独热编码之前,我们需要先进行标签编码:#performing label encoding from sklearn.preprocessing import LabelEncoder label_encoder = LabelEncoder() for i in data_column_category: df[i] = label_encoder.fit_transform(df[i]) print("Label Encoded Data: ") df.head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_39.jpg
图 1.39:非数值列转换为数值数据
-
一旦我们完成了标签编码,就可以执行一热编码。添加以下代码来实现这一操作:
#Performing Onehot Encoding onehot_encoder = OneHotEncoder(sparse=False) onehot_encoded = onehot_encoder.fit_transform(df[data_column_category])
-
现在我们创建一个包含编码数据的新数据框,并打印前五行。添加以下代码来实现这一操作:
#Creating a dataframe with encoded data with new column name onehot_encoded_frame = pd.DataFrame(onehot_encoded, columns = onehot_encoder.get_feature_names(data_column_category)) onehot_encoded_frame.head()
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_40.jpg
图 1.40:具有一热编码值的列
-
由于一热编码,新的数据框架中的列数增加了。为了查看和打印所有创建的列,请使用
columns
属性:onehot_encoded_frame.columns
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_41.jpg
图 1.41:经过一热编码生成的新列列表
-
对于每个级别或类别,会创建一个新的列。为了在列名前加上类别名,你可以使用另一种方法来创建一热编码。为了在列名前加上类别名,可以写下以下代码:
df_onehot_getdummies = pd.get_dummies(df[data_column_category], prefix=data_column_category) data_onehot_encoded_data = pd.concat([df_onehot_getdummies,df[data_column_number]],axis = 1) data_onehot_encoded_data.columns
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_42.jpg
图 1.42:包含类别的新列列表
你已经成功地使用OneHotEncoder
方法将分类数据转换为数值数据。
现在我们将进入另一个数据预处理步骤——如何处理数据中的不同量级范围。
不同尺度的数据
在现实生活中,数据集中的值可能具有不同的量级、范围或尺度。使用距离作为参数的算法可能不会以相同的方式加权这些特征。存在多种数据转换技术,用于将数据特征转换到相同的尺度、量级或范围。这确保了每个特征对模型预测的影响是合适的。
我们数据中的某些特征可能具有较大的数值(例如,年薪),而其他特征的数值可能相对较小(例如,在公司工作的年数)。仅仅因为某些数据的值较小并不意味着它不重要。因此,为了确保我们的预测不会因为数据中特征的不同量级而有所不同,我们可以进行特征缩放、标准化或归一化(这三种方式都用于处理数据中的量级问题)。
练习 9:使用标准缩放器方法实现缩放
在本练习中,我们将把Wholesale customer's data.csv
数据集加载到 pandas 数据框中,并使用标准缩放器方法进行缩放。该数据集指的是某批发分销商的客户信息,包含了在不同产品类别上每年花费的货币数额。按照以下步骤完成本练习:
注意
Wholesale customer
数据集可以在此找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Wholesale%20customers%20data.csv。
-
打开一个 Jupyter 笔记本并添加一个新单元格。编写代码以导入 pandas 并将数据集加载到 pandas 数据框中:
import pandas as pd dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Wholesale%20customers%20data.csv' df = pd.read_csv(dataset, header=0)
-
检查是否有任何缺失数据。如果有,删除缺失数据:
null_ = df.isna().any() dtypes = df.dtypes info = pd.concat([null_,dtypes],axis = 1,keys = ['Null','type']) print(info)
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_43.jpg
图 1.43:数据框的不同列
如我们所见,数据框中有八列,都是
int64
类型。由于空值为False
,这意味着在任何列中都没有空值。因此,无需使用dropna()
函数。 -
现在执行标准缩放,并打印新数据集的前五行。为此,使用
sklearn.preprocessing
中的StandardScaler()
类,并实现fit_transform()
方法:from sklearn import preprocessing std_scale = preprocessing.StandardScaler().fit_transform(df) scaled_frame = pd.DataFrame(std_scale, columns=df.columns) scaled_frame.head()
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_44.jpg
图 1.44:特征数据缩放为统一单位
使用StandardScaler
方法,我们已将数据缩放为所有列的统一单位。正如前面的表格所示,所有特征的值已转换为相同范围和尺度的统一值。因此,模型更容易进行预测。
你已经成功使用StandardScaler
方法对数据进行了缩放。在接下来的部分,我们将尝试一个练习,使用MinMax
缩放器方法来实现数据缩放。
练习 10:使用 MinMax 缩放器方法实现缩放
在本练习中,我们将把Wholesale customers data.csv
数据集加载到 pandas 数据框中,并使用MinMax
缩放器方法进行缩放。按照以下步骤完成此练习:
注意
Whole customers data.csv
数据集可以在此找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Wholesale%20customers%20data.csv。
-
打开一个 Jupyter 笔记本并添加一个新单元格。编写以下代码以导入 pandas 库并将数据集加载到 pandas 数据框中:
import pandas as pd dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Wholesale%20customers%20data.csv' df = pd.read_csv(dataset, header=0)
-
检查是否有任何缺失数据。如果有,删除缺失数据:
null_ = df.isna().any() dtypes = df.dtypes info = pd.concat([null_,dtypes],axis = 1,keys = ['Null','type']) print(info)
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_45.jpg
图 1.45:数据框的不同列
如我们所见,数据框中有八列,都是
int64
类型。由于空值为False
,这意味着在任何列中都没有空值。因此,无需使用dropna()
函数。 -
执行
MinMax
缩放,并打印新数据集的前五个值。为此,使用sklearn.preprocessing
中的MinMaxScaler()
类,并实现fit_transform()
方法。添加以下代码以实现此功能:from sklearn import preprocessing minmax_scale = preprocessing.MinMaxScaler().fit_transform(df) scaled_frame = pd.DataFrame(minmax_scale,columns=df.columns) scaled_frame.head()
上述代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_46.jpg
图 1.46:特征数据被缩放到统一的单位
使用 MinMaxScaler
方法,我们再次将所有列的数据缩放到统一的单位。正如在上述表格中所看到的,所有特征的值已经被转换为相同范围的统一尺度。你已经成功地使用 MinMaxScaler
方法缩放了数据。
在下一部分,我们将探讨另一个预处理任务:数据离散化。
数据离散化
到目前为止,我们已经通过编码处理了类别数据,并通过缩放处理了数值数据。
数据离散化是将连续数据通过分组转换为离散桶的过程。离散化还因数据的易维护性而闻名。与使用连续数据训练模型相比,使用离散数据训练模型更加快速和有效。尽管连续值数据包含更多信息,但大量数据会拖慢模型的运行速度。在这里,离散化可以帮助我们在两者之间找到平衡。数据离散化的一些著名方法包括分箱和使用直方图。尽管数据离散化有用,但我们需要有效地选择每个桶的范围,这是一项挑战。
离散化的主要挑战是选择区间或桶的数量,以及如何决定它们的宽度。
这里我们使用一个叫做 pandas.cut()
的函数。这个函数对于实现数据的分桶和排序非常有用。
练习 11:连续数据的离散化
在这个练习中,我们将加载 Student_bucketing.csv
数据集并执行分桶操作。该数据集包含学生的详细信息,如 Student_id
、Age
、Grade
、Employed
和 marks
。按照以下步骤完成此练习:
注意
Student_bucketing.csv
数据集可以在这里找到:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Student_bucketing.csv。
-
打开一个 Jupyter Notebook,添加一个新单元格。编写以下代码以导入所需的库并将数据集加载到 pandas 数据框中:
import pandas as pd dataset = "https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Student_bucketing.csv" df = pd.read_csv(dataset, header = 0)
-
一旦加载数据框,显示数据框的前五行。添加以下代码以完成此操作:
df.head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_47.jpg
图 1.47:数据框的前五行
-
使用
pd.cut()
函数对marks
列进行分桶,并显示前 10 列。cut()
函数接受如x
、bins
和labels
等参数。在这里,我们只使用了三个参数。添加以下代码以实现此功能:df['bucket']=pd.cut(df['marks'],5,labels=['Poor','Below_average','Average','Above_Average','Excellent']) df.head(10)
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_48.jpg
图 1.48:具有五个离散桶的 marks
列
在上述代码中,第一个参数表示一个数组。在这里,我们已从数据框中选择 marks
列作为数组。5
表示要使用的箱数。由于我们设置了箱数为 5
,因此需要相应地填充标签,使用五个值:Poor
, Below_average
, Average
, Above_average
和 Excellent
。在上述图中,我们可以看到整个连续的 marks 列被放入了五个离散的桶中。我们已经学会了如何进行分桶。
我们现在已经涵盖了所有预处理中涉及的主要任务。在下一节中,我们将详细讨论如何训练和测试您的数据。
训练和测试数据
一旦您将数据预处理为可以被模型使用的格式,您需要将数据分割为训练集和测试集。这是因为您的机器学习算法将使用训练集中的数据来学习所需的内容。然后,它将使用所学内容对测试集中的数据进行预测。您随后可以将此预测与测试集中的实际目标变量进行比较,以查看模型的准确性。下一节的练习将更加详细地解释这一点。
我们将按比例进行训练/测试分割。数据分割的较大部分将是训练集,较小部分将是测试集。这将有助于确保您使用足够的数据来准确训练您的模型。
通常情况下,我们按照 80:20 的比例进行训练-测试分割,遵循帕累托原则。帕累托原则指出:“对于许多事件,大约 80% 的效果来自于 20% 的原因。” 但是,如果您有一个大型数据集,无论是 80:20 还是 90:10 或者 60:40 都无关紧要。(如果我们的过程计算密集型,使用较小的训练集可能会更好,但这可能会导致过拟合问题 – 这将在本书的后续部分进行讨论。)
练习 12: 将数据分割为训练集和测试集
在此练习中,我们将加载 USA_Housing.csv
数据集(您之前看到的)到 pandas 数据框中,并执行训练/测试分割。按照以下步骤完成此练习:
注意
USA_Housing.csv
数据集可在此处获取:https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/USA_Housing.csv.
-
打开一个 Jupyter 笔记本,并添加一个新的单元格来导入 pandas 并将数据集加载到 pandas 中:
import pandas as pd dataset = 'https://github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/USA_Housing.csv' df = pd.read_csv(dataset, header=0)
-
创建一个名为
X
的变量来存储独立特征。使用drop()
函数来包括所有特征,留出依赖或目标变量,本例中名为Price
。然后,打印出该变量的前五个实例。添加以下代码来完成这一步骤:X = df.drop('Price', axis=1) X.head()
上述代码生成以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_49.jpg
图 1.49: 包含独立变量的数据框
-
使用
X.shape
命令打印出你新创建的特征矩阵的形状:X.shape
上面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_50.jpg
图 1.50:X 变量的形状
在前面的图中,第一个值表示数据集中的观察数(5000),第二个值表示特征数(6)。
-
同样地,我们将创建一个名为
y
的变量来存储目标值。我们将使用索引来获取目标列。索引允许我们访问更大元素的一部分。在本例中,我们希望从df
数据框中提取名为 Price 的列,并打印出前 10 个值。添加以下代码来实现:y = df['Price'] y.head(10)
上面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_51.jpg
图 1.51:y 变量的前 10 个值
-
使用
y.shape
命令打印出你新创建的变量的形状:y.shape
上面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_52.jpg
图 1.52:y 变量的形状
数据集的形状应为一维,长度等于观察数量(5000)。
-
将数据集按 80:20 的比例分成训练集和测试集。为此,请使用来自
sklearn.model_selection
包的train_test_split()
函数。添加以下代码来实现:from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
在上面的代码中,
test_size
是一个浮动值,定义了测试数据的大小。如果值为 0.2,则为 80:20 的分割比例。test_train_split
会将数组或矩阵随机分割成训练集和测试集。每次我们运行代码而不指定random_state
时,都会得到不同的结果。 -
打印出
X_train
、X_test
、y_train
和y_test
的形状。添加以下代码来实现:print("X_train : ",X_train.shape) print("X_test : ",X_test.shape) print("y_train : ",y_train.shape) print("y_test : ",y_test.shape)
上面的代码生成了以下输出:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_53.jpg
图 1.53:训练集和测试集的数据形状
你已经成功地将数据分割成训练集和测试集。
在接下来的章节中,你将完成一项活动,其中你将对数据集进行预处理。
活动 1:使用银行营销订阅数据集进行预处理
在这个活动中,我们将对 Bank Marketing Subscription
数据集进行各种预处理任务。该数据集与葡萄牙银行机构的直接营销活动相关。通过电话营销新产品,并记录每个客户是否订阅了该产品。
按照以下步骤完成此活动:
注意
Bank Marketing Subscription
数据集可以在此处找到: github.com/TrainingByPackt/Data-Science-with-Python/blob/master/Chapter01/Data/Banking_Marketing.csv
。
-
从给定的链接加载数据集到 pandas 数据框中。
-
通过找到行数和列数、列出所有列、找到所有列的基本统计信息(可以使用
describe().transpose()
函数)和列出列的基本信息(可以使用info()
函数),探索数据的特征。 -
检查是否有任何缺失(或 NULL)值,并找出每列有多少个缺失值。
-
删除任何缺失值。
-
打印
education
列的频率分布。 -
数据集的
education
列有许多类别。减少类别以进行更好的建模。 -
选择并执行适合数据的编码方法。
-
将数据分为训练集和测试集。目标数据在
y
列中,独立数据在其余列中。将数据以 80%的训练集和 20%的测试集分割。注意
此活动的解决方案可以在第 324 页找到。
现在我们已经涵盖了各种数据预处理步骤,让我们更详细地看看数据科学家可用的不同类型的机器学习。
监督学习
监督学习是一种使用标记数据(目标变量已知的数据)进行训练的学习系统。模型学习如何将特征矩阵中的模式映射到目标变量。当训练后的机器用新数据集进行输入时,它可以利用所学内容预测目标变量。这也可以称为预测建模。
监督学习广泛分为两类。这些类别如下:
分类 主要处理分类目标变量。分类算法有助于预测数据点属于哪个组或类。
当预测处于两个类之间时,称为二元分类。例如,预测客户是否会购买产品(在这种情况下,类别为是和否)。
如果预测涉及超过两个目标类,则称为多分类;例如,预测客户将购买的所有物品。
回归 处理数值目标变量。回归算法基于训练数据集预测目标变量的数值。
线性回归 测量一个或多个 预测变量 与一个 结果变量 之间的关联。例如,线性回归可以帮助列举年龄、性别和饮食(预测变量)对身高(结果变量)的相对影响。
时间序列分析,顾名思义,处理的是随时间分布的数据,即按时间顺序排列的数据。股市预测和客户流失预测是时间序列数据的两个例子。根据需求或必要性,时间序列分析可以是回归或分类任务。
无监督学习
与有监督学习不同,无监督学习过程涉及的数据既没有分类也没有标记。算法将对数据进行分析,而不需要指导。机器的任务是根据数据中的相似性将未分组的信息进行分组。其目标是让模型在数据中发现模式,从而为我们提供数据的洞察力并进行预测。
一个例子是,使用一整批未标记的客户数据来寻找模式,将客户分成不同的群体。然后可以针对不同的群体推销不同的产品,以实现最大的利润。
无监督学习大致分为两类:
-
聚类:聚类过程有助于发现数据中的内在模式。
-
关联:关联规则是一种独特的方式,用来发现与大量数据相关的模式,例如当某人购买产品 1 时,他们也倾向于购买产品 2。
强化学习
强化学习是机器学习的一个广泛领域,在这个领域中,机器通过观察已执行操作的结果来学习如何在环境中执行下一步操作。强化学习没有固定的答案,学习代理根据任务的要求决定应该做什么。它从以前的经验中学习。这种学习方式涉及奖励和惩罚。
无论你使用何种机器学习方法,你都希望能够衡量你的模型效果。你可以通过各种性能指标来做到这一点。你将在本书的后续章节中看到这些如何使用,但这里简要概述了其中一些最常见的指标。
性能指标
机器学习中有不同的评估指标,这些指标依赖于数据的类型和需求。以下是一些常见的指标:
-
混淆矩阵
-
精确度
-
召回
-
准确率
-
F1 分数
混淆矩阵
混淆矩阵是一个表格,用来定义分类模型在测试数据上的表现,其中实际值是已知的。为了更好地理解这一点,请看下图,展示了预测值与实际值:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_54.jpg
图 1.54:预测值与实际值
让我们详细了解混淆矩阵及其指标,TP、TN、FP 和 FN。假设你正在构建一个预测怀孕的模型:
-
True
。 -
True
,这是不可能发生的。它是一种错误,称为类型 1 错误。 -
False
,这也是一种错误,称为类型 2 错误。 -
False
;即真正负类。
类型 1 错误比类型 2 错误更危险。根据问题的不同,我们必须弄清楚是需要减少类型 1 错误还是类型 2 错误。
精确度
精确度是 TP 结果与模型预测的所有正向结果的比例。精确度衡量我们模型的准确性,如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_55.jpg
图 1.55:精度公式
召回率
召回率计算我们模型预测出的 TP(真正例)结果所占的比例:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_56.jpg
图 1.56:召回率公式
准确率
准确率计算模型在所有预测中正确预测正例的比例:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_57.jpg
图 1.57:准确率公式
F1 得分
F1 得分是另一种准确度衡量方法,但它允许我们在精度和召回率之间寻求平衡:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_58.jpg
图 1.58:F1 得分
在考虑模型表现时,我们必须理解预测误差的另外两个重要概念:偏差和方差。
什么是偏差?
偏差是指预测值与实际值之间的差距。高偏差意味着模型非常简单,无法捕捉到数据的复杂性,导致所谓的欠拟合。
什么是方差?
高方差是指模型在训练数据集上表现得过好。这会导致过拟合,使得模型对训练数据过于特定,也就是说,模型在测试数据上表现不佳。
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/C13322_01_59.jpg
图 1.59:高方差
假设你正在构建一个线性回归模型,目的是预测一个国家中汽车的市场价格。假设你有关于汽车及其价格的大量数据集,但仍有一些汽车的价格需要预测。
当我们用数据集训练模型时,我们希望模型仅仅在数据集中找到那个模式,仅此而已,因为如果超出这个范围,它就会开始记住训练集中的数据。
我们可以通过调整模型的超参数来改进模型——书中的后续章节会详细介绍这一点。我们通过使用另一个数据集,即验证集,来努力最小化误差并最大化准确率。第一个图表显示模型还没有学到足够的知识来在测试集上进行良好的预测。第三个图表显示模型已经记住了训练数据集,这意味着准确率将是 100,误差为 0。但如果我们在测试数据上进行预测,中间的模型表现会优于第三个。
总结
在本章中,我们介绍了数据科学的基础,并探讨了使用科学方法、过程和算法从数据中提取潜在信息的过程。接着我们进入了数据预处理,包括数据清洗、数据集成、数据转换和数据离散化。
我们看到在使用机器学习算法构建模型时,如何将预处理过的数据分为训练集和测试集。我们还讨论了监督学习、无监督学习和强化学习算法。
最后,我们介绍了不同的度量标准,包括混淆矩阵、精度、召回率和准确率。
在下一章中,我们将介绍数据可视化。
序言
“开发者是当今商业中最重要、最有价值的群体,无论在哪个行业。” | ||
---|---|---|
–斯蒂芬·奥格雷迪,《新王 makers》一书的作者 |
首先,我要感谢并祝贺你,亲爱的读者,做出决定,投资宝贵的时间来阅读这本书。在接下来的章节中,我将带领你从开发者的角度来发现或重新发现数据科学,并阐述本书的主题,即数据科学是一项团队运动,如果它要成功,开发者将在不久的将来扮演更重要的角色,并且与数据科学家更好地合作。然而,为了使数据科学更加包容各行各业的人,我们首先需要通过让数据变得简单和可访问来让它民主化——这实际上就是本书的核心内容。
我为什么要写这本书?
正如我将在第一章中详细解释的那样,编程与数据科学 – 一套新工具,我首先是一名开发者,拥有超过 20 年的多样化软件组件构建经验;包括前端、后端、中间件等等。回顾这段时光,我意识到,每当我思考时,正确的算法总是放在首位;数据总是别人的问题。我很少需要分析数据或从中提取洞察。充其量,我只是设计合适的数据结构,以便以一种更高效、更优雅且可复用的方式加载数据,进而使我的算法运行得更加顺畅。
然而,随着人工智能和数据科学革命的展开,我很明显地意识到像我这样的开发者需要参与其中,因此,7 年前的 2011 年,我抓住机会成为 IBM Watson 核心平台 UI 与工具的首席架构师。当然,我并不自称已成为机器学习或自然语言处理领域的专家,远非如此。通过实践学习并不能替代获得正式的学术背景。
然而,我在本书中想要展示的一个重要观点是,凭借正确的工具和方法,拥有合适数学基础的人(我这里只谈高中的微积分概念)可以迅速成为这一领域的优秀从业者。成功的关键之一是尽可能简化构建数据管道的不同步骤;从获取、加载、清洗数据,到可视化和探索,再到构建和部署机器学习模型。
正是出于让数据更简单、让数据科学家之外的社区也能接触到数据的想法,三年前,我在 IBM Watson 数据平台团队中担任领导角色,致力于扩大开发者社区,专注于教育和为开发者争取权益。在那个时期,作为首席开发者倡导者,我开始公开讨论开发者和数据科学家在解决复杂数据问题时需要更好地协作。
注意
注意:在会议和聚会上讨论时,我有时会与数据科学家产生矛盾,因为他们误解了我的叙述,以为我在说数据科学家不是优秀的软件开发人员。我想澄清一下,包括对你——数据科学家的读者——说,这绝对不是我的意思。
大多数数据科学家都是优秀的软件开发人员,具备全面的计算机科学知识。然而,他们的主要目标是解决复杂的数据问题,这需要快速、反复试验新事物,而不是编写优雅、可重用的组件。
但我不想只是空谈,我还希望有所行动,便启动了 PixieDust 开源项目,作为我为解决这个重要问题所做的微薄贡献。随着 PixieDust 的工作进展顺利,叙述变得更加简洁易懂,开发者和数据科学家们都能为之兴奋。
当我被提供机会写一本关于这个故事的书时,由于两个主要原因,我犹豫了很久才开始这次冒险:
-
我在博客、文章和教程中广泛写过我作为数据科学从业者使用 Jupyter Notebooks 的经验。我还在各种会议中担任过演讲者和工作坊主持人。一个很好的例子是我在 2017 年的 ODSC 伦敦大会上发表的主题演讲,《数据科学的未来:少一些权力的游戏,多一些联盟》(
odsc.com/training/portfolio/future-data-science-less-game-thrones-alliances
)。然而,我从未写过书,完全不知道这会是多么大的承诺,尽管许多曾经写过书的朋友多次提醒过我。 -
我希望这本书能够包容不同的读者,平等地面向开发者、数据科学家以及业务线用户,但我在寻找合适的内容和语气时感到困惑。
最终,决定踏上这段冒险之旅变得相当容易。经过两年的 PixieDust 项目工作,我觉得我们在这个项目中取得了非常好的进展,做出了许多有趣的创新,引起了开源社区的广泛关注,写一本书将会很好地补充我们在帮助开发者参与数据科学方面的倡导工作。
作为旁注,对于那些有写书打算并有类似顾虑的读者,我只能大声地建议:“是的,去做吧。”当然,这是一项需要付出巨大承诺的工作,需要大量的牺牲,但只要你有一个好的故事要讲,且内容扎实,这绝对值得付出努力。
本书适合谁阅读
本书将帮助有志于提升技能的初学数据科学家和开发者,或任何希望成为专业数据科学家的读者。由于书中引入了 PixieDust 的创造者,这本书还将成为已经成就非凡的数据科学家的优秀桌面伴侣。
无论个人的兴趣程度如何,简洁易读的文本和现实生活中的案例都适合那些对该领域有一般兴趣的人,因为他们可以在 Jupyter Notebooks 中运行 Python 代码进行实践。
要制作一个功能完备的 PixieDust 仪表盘,只需要掌握少量的 HTML 和 CSS。数据解读和可视化的流利性也是必不可少的,因为本书面向的数据专业人士包括商业和一般数据分析师。后面的章节也有很多内容值得学习。
本书内容概述
本书分为两个逻辑部分,长度大致相等。在前半部分,我阐述了本书的主题,即需要弥合数据科学与工程之间的鸿沟,详细介绍了我提出的 Jupyter + PixieDust 解决方案。后半部分则专注于将我们在前半部分学到的内容应用于四个行业案例。
第一章, 编程与数据科学——一套新工具,在这一章中,我试图通过自己的经验来定义数据科学,建立一个对 Twitter 帖子进行情感分析的数据管道。我为这个观点辩护——数据科学是一项团队运动,通常,数据科学团队和工程团队之间存在隔阂,这会导致不必要的摩擦、低效,最终无法充分发挥其潜力。我还提出数据科学将会长期存在,并且最终它将成为今天所称计算机科学的一个组成部分(我喜欢认为,总有一天会出现一些新术语,比如计算机数据科学,来更好地体现这一双重性)。
第二章,用 Python 和 Jupyter Notebooks 推动数据分析,我开始深入探讨流行的数据科学工具,如 Python 及其专注于数据科学的开源库生态系统,当然还有 Jupyter Notebooks。我解释了为什么我认为 Jupyter Notebooks 将在未来几年成为大赢家。我还介绍了 PixieDust 开源库的功能,从简单的display()
方法开始,用户可以通过构建引人入胜的图表,在交互式用户界面中直观地探索数据。通过此 API,用户可以选择多个渲染引擎,如 Matplotlib、Bokeh、Seaborn 和 Mapbox。display()
功能是 PixieDust MVP(最小可行产品)中唯一的特性,但随着时间的推移,随着我与许多数据科学从业者的互动,我添加了新功能,迅速形成了 PixieDust 工具箱:
-
sampleData():一个简单的 API,用于轻松地将数据加载到 pandas 和 Apache Spark 数据框中。
-
wrangle_data():一个简单的 API,用于清理和处理数据集。此功能包括使用正则表达式将列拆解为新列,以提取非结构化文本中的内容。
wrangle_data()
API 还可以根据预定义的模式提供建议。 -
包管理器:允许用户在 Python Notebook 中安装第三方 Apache Spark 包。
-
Scala 桥接:使用户能够在 Python Notebook 中运行 Scala 代码。在 Python 端定义的变量可以在 Scala 中访问,反之亦然。
-
Spark 作业进度监控:让你通过实时进度条跟踪 Spark 作业的状态,进度条直接显示在执行代码的输出单元中。
-
PixieApp:提供以 HTML/CSS 为中心的编程模型,允许开发者构建复杂的仪表板,以将 Notebook 中构建的分析操作化。PixieApps 可以直接在 Jupyter Notebook 中运行,也可以使用 PixieGateway 微服务作为分析 Web 应用程序进行部署。PixieGateway 是 PixieDust 的开源配套项目。
以下图表总结了 PixieDust 的发展历程,包括最近添加的 PixieGateway 和 PixieDebugger,后者是 Jupyter Notebooks 中的第一个可视化 Python 调试器:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_preface_01.jpg
PixieDust 历程
从本章中要记住的一个关键点是,PixieDust 首先是一个开源项目,它通过开发者社区的贡献而生生不息。与无数开源项目一样,我们可以预期 PixieDust 在未来会加入更多突破性的功能。
第三章,使用 Python 库加速数据分析,我将带领读者深入了解 PixieApp 编程模型,沿途通过一个分析 GitHub 数据的示例应用程序来阐明每个概念。我从 PixieApp 的基本结构入手,描述其生命周期和执行流程,并结合路由概念进行讲解。接着,我介绍开发者如何使用常规的 HTML 和 CSS 代码片段构建仪表盘的 UI,实现与分析结果的无缝交互,并利用 PixieDust 的display()
API 添加复杂的图表。
PixieApp 编程模型是连接数据科学与工程之间的工具策略的基石,因为它简化了将分析成果转化为可操作应用的过程,从而促进了数据科学家与开发者之间的协作,并减少了应用的上市时间。
第四章,将数据分析发布到 Web - PixieApp 工具,我讨论了 PixieGateway 微服务,它使开发者能够将 PixieApps 发布为分析型 Web 应用程序。我首先展示了如何在本地和云端快速部署 PixieGateway 微服务实例,作为 Kubernetes 容器。然后,我介绍了 PixieGateway 管理员控制台的功能,包括各种配置文件以及如何实时监控已部署的 PixieApps 实例和相关的 Python 后端内核。我还介绍了 PixieGateway 的图表共享功能,允许用户将使用 PixieDust 的display()
API 创建的图表转换为网页,供团队中的任何人访问。
PixieGateway 是一项突破性的创新,具有显著加速分析操作化的潜力——这是当今非常需要的——以充分实现数据科学的潜力。它代表了一个开源替代方案,可以与市场上已有的类似产品竞争,如 R-Studio 的 Shiny Server(shiny.rstudio.com/deploy
)和 Plotly 的 Dash(dash.plot.ly
)。
第五章,Python 和 PixieDust 最佳实践与高级概念,我通过深入探讨 PixieApp 编程模型的高级概念,完成了对 PixieDust 工具箱的深度剖析:
-
@captureOutput 装饰器:默认情况下,PixieApp 路由要求开发者提供一个将被注入到应用 UI 中的 HTML 片段。当我们想调用一个第三方 Python 库,而该库不了解 PixieApp 架构并直接生成输出到 Notebook 时,就会出现问题。
@captureOutput
通过自动重定向第三方 Python 库生成的内容并将其封装成一个合适的 HTML 片段来解决这个问题。 -
利用 Python 类继承提高模块化和代码重用性:将 PixieApp 代码拆解成逻辑类,并利用 Python 类继承功能将其组合在一起。我还展示了如何通过
pd_app
自定义属性调用外部 PixieApp。 -
PixieDust 对流式数据的支持:展示了 PixieDust 的
display()
和 PixieApp 如何处理流式数据。 -
通过 PixieApp 事件实现仪表盘深入分析:提供了一种机制,让 PixieApp 组件能够发布和订阅当用户与 UI 交互时生成的事件(例如,图表和按钮)。
-
为 PixieDust display() API 构建自定义显示渲染器:演示了一个简单渲染器的代码,扩展了 PixieDust 菜单。该渲染器展示了一个自定义的 HTML 表格,显示所选数据。
-
调试技巧:介绍 PixieDust 提供的各种调试技巧,包括名为 PixieDebugger 的可视化 Python 调试器,以及用于显示 Python 日志消息的
%%PixiedustLog
魔法命令。 -
运行 Node.js 代码的能力:我们讨论了
pixiedust_node
扩展,它管理一个 Node.js 进程的生命周期,该进程负责直接从 Python Notebook 中执行任意的 Node.js 脚本。
多亏了开放源代码模型,凭借其透明的开发过程和日益增长的用户社区提供的一些宝贵反馈,我们能够随着时间的推移优先实施许多先进的功能。我想表达的关键点是,遵循一个合适的开源模型并采用适当的许可证(PixieDust 使用的是 Apache 2.0 许可证,详情请见 www.apache.org/licenses/LICENSE-2.0
)是非常有效的。它帮助我们壮大了用户社区,而社区又为我们提供了必要的反馈,帮助我们优先开发那些我们知道具有高价值的新特性,在某些情况下,甚至贡献了代码,形式为 GitHub 拉取请求。
第六章,分析研究:AI 与图像识别与 TensorFlow,我深入讲解了四个行业案例中的第一个。我从机器学习的高级介绍开始,接着介绍了深度学习——机器学习的一个子领域——以及使得构建神经网络模型更简单的 TensorFlow 框架。然后,我继续构建一个图像识别示例应用程序,并在四个部分中包括相关的 PixieApp:
-
第一部分:使用预训练的 ImageNet 模型构建一个图像识别的 TensorFlow 模型。通过《TensorFlow for poets》教程,我展示了如何构建分析功能来加载和评分神经网络模型。
-
第二部分:创建一个 PixieApp,将第一部分中创建的分析功能投入实际应用。这个 PixieApp 从用户提供的网页 URL 中抓取图像,将它们与 TensorFlow 模型进行评分,并以图形方式展示结果。
-
第三部分:我展示了如何将 TensorBoard 图形可视化组件直接集成到 Notebook 中,以提供调试神经网络模型的能力。
-
第四部分:我展示了如何使用自定义训练数据重新训练模型,并更新 PixieApp 以展示来自两个模型的结果。
我决定以深度学习图像识别为主题开始这一系列示例应用程序,因为这是一个日益流行的重要用例,展示了我们如何构建模型并将其部署到同一个 Notebook 中的应用程序,这一做法为弥合数据科学与工程之间的鸿沟提供了有力的支持。
第七章,分析研究:NLP 与大数据——Twitter 情感分析,我谈到了在 Twitter 规模上进行自然语言处理。在这一章中,我展示了如何使用 IBM Watson 自然语言理解的云服务对推文进行情感分析。这一点非常重要,因为它提醒读者,有时候复用托管服务而不是自己构建能力,可能是一个更具吸引力的选择。
我首先介绍了 Apache Spark 并行计算框架,然后分四部分开始构建应用程序:
-
第一部分:使用 Spark 结构化流处理获取 Twitter 数据。
-
第二部分:通过提取文本中的情感和最相关的实体来丰富数据。
-
第三部分:通过创建一个实时仪表盘 PixieApp 来实现分析功能。
-
第四部分:这是一个可选部分,重新实现该应用程序,使用 Apache Kafka 和 IBM Streaming Designer 托管服务,展示如何提高可扩展性。
我认为读者,尤其是那些不熟悉 Apache Spark 的读者,会喜欢这一章,因为它比前一章稍微容易跟随。关键的收获是如何构建可扩展的分析,利用连接到 Spark 集群的 Jupyter Notebooks。
第八章,分析研究:预测 - 财务时间序列分析与预测,我谈到了时间序列分析,这是数据科学中一个非常重要的领域,在工业界有许多实际应用。我从深入探讨 NumPy 库开始,NumPy 是许多其他库(如 pandas 和 SciPy)的基础。接着,我开始构建示例应用程序,分析一个由历史股票数据组成的时间序列,分为两个部分:
-
第一部分:提供了时间序列的统计探索,包括各种图表,如自相关函数(ACF)和偏自相关函数(PACF)。
-
第二部分:基于 ARIMA 算法使用
statsmodels
Python 库构建预测模型。
时间序列分析是数据科学中一个非常重要的领域,我认为它被低估了。在写这章时,我个人学到了很多东西。我真心希望读者也能喜欢它,并且阅读它能够激发对这个伟大主题的兴趣。如果真是这样,我也希望你会被说服,尝试在下次学习时间序列分析时使用 Jupyter 和 PixieDust。
第九章,分析研究:图算法 - 美国国内航班数据分析,我通过研究图形算法来完成这一系列的行业应用案例。我选择了一个分析航班延误的示例应用程序,因为数据是现成的,而且非常适合使用图算法(老实说,我也可能是因为我已经写过一个类似的应用程序,基于天气数据预测航班延误,那个应用程序使用了 Apache Spark MLlib:developer.ibm.com/clouddataservices/2016/08/04/predict-flight-delays-with-apache-spark-mllib-flightstats-and-weather-data
)。
我首先介绍了图形及其相关的图算法,包括一些最流行的图算法,如广度优先搜索(Breadth First Search)和深度优先搜索(Depth First Search)。接着,我介绍了用于构建示例应用程序的 networkx
Python 库。
该应用程序由四个部分组成:
-
第一部分:展示了如何将美国国内航班数据加载到图中。
-
第二部分:创建
USFlightsAnalysis
PixieApp,允许用户选择起点和目的地机场,然后根据选定的中心性展示两机场之间的最短路径的 Mapbox 地图。 -
第三部分:为 PixieApp 添加数据探索功能,包括针对每个航司的各种统计数据,统计的是从所选起点机场起飞的航班。
-
第四部分:使用在第八章,分析学习:预测 – 财务时间序列分析与预测 中学到的技术,构建一个 ARIMA 模型来预测航班延误。
图论也是数据科学中一个重要且不断发展的领域,本章很好地总结了这一系列内容,我希望它提供了一个多样且具有代表性的行业案例集。对于那些特别感兴趣于使用图算法处理大数据的读者,我建议查看 Apache Spark GraphX (spark.apache.org/graphx
),它通过一个非常灵活的 API 实现了许多图算法。
第十章,数据分析的未来及如何发展你的技能,我通过简要总结并解释 Drew Conway 的 Venn 图来结束本书。接着我会谈到人工智能和数据科学的未来,以及公司如何为人工智能和数据科学的革命做好准备。此外,我还列出了一些有助于进一步学习的优秀参考资料。
附录,PixieApp 快速参考,是为开发者提供的快速参考指南,概述了所有 PixieApp 属性。这本指南通过适当的示例解释了各种注释、自定义 HTML 属性和方法。
但介绍部分说得够多了:让我们从第一章开始,标题为 编程与数据科学 – 新工具集。
为了最大程度地从本书中受益
-
大部分跟随本书示例所需的软件是开源的,因此可以免费下载安装。书中提供了详细的安装说明,首先从安装 Anaconda 开始,Anaconda 包含了 Jupyter Notebook 服务器。
-
在第七章,分析学习:NLP 和大数据与 Twitter 情感分析,示例应用程序需要使用 IBM Watson 云服务,包括 NLU 和 Streams Designer。这些服务提供免费的计划层,足以让你跟随本书的示例。
下载示例代码文件
你可以从 www.packtpub.com
账户下载本书的示例代码文件。如果你是从其他地方购买的这本书,可以访问 www.packtpub.com/support
,注册后文件会直接通过电子邮件发送给你。
你可以按照以下步骤下载代码文件:
-
登录或注册
www.packtpub.com
-
选择支持标签。
-
点击代码下载与勘误。
-
在搜索框中输入书名,并按照屏幕上的指示操作。
一旦文件下载完成,请确保使用以下最新版本解压或提取文件夹:
-
Windows 版 WinRAR / 7-Zip
-
Mac 版 Zipeg / iZip / UnRarX
-
Linux 版 7-Zip / PeaZip
本书的代码包也托管在 GitHub 上,地址是github.com/PacktPublishing/Thoughtful-Data-Science
。我们还有其他来自丰富书籍和视频目录的代码包,地址是github.com/PacktPublishing/
。快来看看吧!
下载彩色图片
我们还提供了一个 PDF 文件,里面有本书中使用的截图/图表的彩色图片。你可以在这里下载:www.packtpub.com/sites/default/files/downloads/ThoughtfulDataScience_ColorImages.pdf
。
使用的约定
本书中使用了多种文本约定。
CodeInText
:表示文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。例如:“你可以使用{%if ...%}...{%elif ...%}...{%else%}…{%endif%}
符号来有条件地输出文本。”
代码块的格式如下:
import pandas
data_url = "https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD"
building_df = pandas.read_csv(data_url)
building_df
当我们希望特别提醒你注意代码块中的某一部分时,相关行或项会以粗体显示:
import pandas
data_url = "https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD"
building_df = pandas.read_csv(data_url)
building_df
任何命令行输入或输出都如下所示:
jupyter notebook --generate-config
粗体:表示一个新术语、重要词汇或你在屏幕上看到的词汇,例如在菜单或对话框中,也会在文本中像这样出现。例如:“下一步是创建一个新的路由,该路由接受用户值并返回结果。这个路由将通过提交查询按钮触发。”
注意
警告或重要提示如下所示。
提示
提示和技巧如下所示。
联系我们
我们非常欢迎读者的反馈。
一般反馈:电子邮件<feedback@packtpub.com>
,并在邮件主题中提到书籍的标题。如果你有关于本书的任何问题,请通过电子邮件联系我们<questions@packtpub.com>
。
勘误:尽管我们已尽一切努力确保内容的准确性,但错误仍然可能发生。如果你在本书中发现错误,我们将非常感激你能报告给我们。请访问www.packtpub.com/submit-errata
,选择你的书籍,点击勘误提交表单链接,输入相关细节。
盗版:如果您在互联网上发现任何形式的非法复制我们作品的情况,我们将非常感激您能提供该材料的地址或网站名称。请通过<copyright@packtpub.com>
与我们联系,并附上相关链接。
如果您有兴趣成为作者:如果您在某个领域有专长,并且有兴趣写书或为书籍贡献内容,请访问 authors.packtpub.com
。
评论
请留下评论。在阅读和使用本书后,为什么不在您购买它的网站上留下评论呢?潜在的读者可以看到并参考您的客观意见来做出购买决策,我们在 Packt 也能了解您对我们产品的看法,作者们也能看到您对他们书籍的反馈。谢谢!
欲了解更多关于 Packt 的信息,请访问 packtpub.com。
第一章 编程与数据科学——一种新工具集
“数据是宝贵的,它将比系统本身存在得更久。”
– 蒂姆·伯纳斯-李,万维网的发明人
(en.wikipedia.org/wiki/Tim_Berners-Lee
)
在这一章节的开始,我将尝试回答一些基础问题,希望这些问题能为本书的后续内容提供背景和清晰度:
-
什么是数据科学以及为什么它正在兴起
-
为什么数据科学将长久存在
-
为什么开发人员需要参与数据科学
结合我作为开发人员和最近的数据科学实践者的经验,我将讨论一个具体的数据管道项目,以及从这个项目中衍生出的一种数据科学策略,这个策略由三大支柱组成:数据、服务和工具。最后,我将介绍 Jupyter Notebooks,它是我在本书中提出的解决方案的核心。
什么是数据科学
如果你在网上搜索“数据科学”的定义,你一定会找到很多不同的解释。这反映了数据科学对不同的人意味着不同的东西。对于数据科学家究竟做什么以及他们必须接受什么样的训练,实际上并没有达成一致意见;这完全取决于他们要完成的任务,例如数据收集与清洗、数据可视化等。
现在,我会尝试使用一个普遍且希望得到共识的定义:数据科学指的是分析大量数据,以提取知识和见解,从而做出可操作的决策。不过这仍然有些模糊;人们可以问,我们到底在谈论什么样的知识、见解和可操作的决策?
为了引导话题,让我们将数据科学的领域范围缩小到三个领域:
-
描述性分析:数据科学与信息检索和数据收集技术相关,目的是重建过去的事件,以识别模式并发现有助于理解发生了什么以及是什么导致其发生的见解。一个例子是查看销售数据和按地区划分的人口统计信息,以便对客户偏好进行分类。这部分需要熟悉统计学和数据可视化技术。
-
预测性分析:数据科学是一种预测当前正在发生或将来会发生某些事件的可能性的方法。在这种情况下,数据科学家会查看过去的数据,找出解释变量,并构建可以应用于其他数据点的统计模型,以预测其结果。例如,实时预测信用卡交易是否存在欺诈行为。这部分通常与机器学习领域相关。
-
规范性分析:在这种情况下,数据科学被看作是一种做出更好决策的方法,或者我应该说是基于数据的决策。这个想法是,查看多个选项,并使用模拟技术量化并最大化结果,例如,通过优化供应链来最小化运营成本。
从本质上讲,描述性数据科学回答了什么(数据告诉我什么),预测性数据科学回答了为什么(数据为什么以某种方式表现),而规范性数据科学回答了如何(我们如何将数据优化以实现特定目标)。
数据科学会一直存在吗?
让我们从一开始就直截了当地说:我坚信答案是肯定的。
然而,情况并非总是如此。几年前,当我第一次听说数据科学作为一个概念时,我最初以为这只是另一个营销术语,用来描述行业中已经存在的活动:商业智能(BI)。作为一名开发者和架构师,主要解决复杂的系统集成问题,我很容易说服自己不需要直接参与数据科学项目,尽管显然它们的数量在不断增加,原因是开发者传统上将数据管道视为可以通过明确定义的 API 访问的黑箱。然而,在过去的十年里,我们看到数据科学在学术界和工业界的兴趣呈指数级增长,直到它变得非常清楚,这种模型将无法持续下去。
随着数据分析在公司运营过程中扮演着越来越重要的角色,开发者的角色也扩展到了更接近算法的部分,并构建能够在生产环境中运行它们的基础设施。数据科学已成为新的淘金热的另一个证据就是数据科学家职位的非凡增长,这些职位连续两年在 Glassdoor 上排名第一(www.prnewswire.com/news-releases/glassdoor-reveals-the-50-best-jobs-in-america-for-2017-300395188.html
),并且在 Indeed 上经常是雇主发布最多的职位。猎头也在 LinkedIn 和其他社交媒体平台上活跃,向任何拥有数据科学技能资料的人发送大量招聘信息。
所有这些新技术背后投资的主要原因之一,是希望它们能为企业带来显著的改进和更高的效率。然而,尽管这是一个正在发展的领域,今天企业中的数据科学仍然局限于实验,而没有像大家预期的那样成为核心活动。这使得许多人开始怀疑,数据科学是否只是一个短暂的潮流,最终会消退,成为又一个技术泡沫,最终破灭,留下许多人被抛在后头。
这些都是很好的观点,但我很快意识到这不仅仅是一个昙花一现的潮流;我所领导的越来越多的项目开始将数据分析融入到核心产品功能中。最终,当 IBM Watson 问答系统在危险边缘游戏中战胜了两位经验丰富的冠军时,我深信数据科学、云计算、大数据和人工智能(AI)将会长久存在,并最终改变我们对计算机科学的理解。
为什么数据科学在崛起?
数据科学的迅速崛起涉及多个因素。
首先,收集的数据量正在以指数级的速度增长。根据 IBM 营销云的最新市场研究(www-01.ibm.com/common/ssi/cgi-bin/ssialias?htmlfid=WRL12345GBEN
),每天大约会产生 2.5 万亿字节的数据(为了让你更清楚它有多大,这相当于 25 亿亿字节),但这些数据中只有极小的一部分会被分析,导致错失了大量的机会。
其次,我们正处于一场几年前开始的认知革命中;几乎所有行业都在纷纷加入 AI 的浪潮,这其中包括自然语言处理(NLP)和机器学习。尽管这些领域已经存在了很长时间,但最近它们获得了重新的关注,以至于现在它们已经成为大学中最受欢迎的课程之一,并且占据了开源活动的大部分份额。显然,如果企业想要生存下去,它们需要变得更加敏捷,更快地行动,并转型为数字化企业。而随着决策时间的缩短,几乎接近实时,它们必须完全依赖数据。再加上 AI 算法需要高质量(并且大量的)数据才能正常工作,我们可以开始理解数据科学家所扮演的关键角色。
第三,随着云技术的进步和平台即服务(PaaS)的发展,访问海量计算引擎和存储变得前所未有的简单且廉价。曾经只有大型企业才能承担的大数据工作负载,现在对小型组织或任何拥有信用卡的个人都可用;这反过来又推动了各领域创新的增长。
正因如此,我毫不怀疑,类似于人工智能革命,数据科学将长期存在,而且它的增长将持续很长时间。但我们也不能忽视数据科学尚未充分发挥其潜力,并且未能产生预期的结果,特别是在帮助公司转型为数据驱动型组织方面。最常见的挑战是实现下一步,即将数据科学和分析转化为核心业务活动,最终推动清晰、智能的“赌命”决策。
这与开发者有什么关系?
这是一个非常重要的问题,我们将在接下来的章节中花费大量时间探讨。让我先回顾一下我的职业生涯;我大部分时间作为开发者,回溯到 20 多年前,参与了计算机科学的多个方面。
我开始通过构建各种工具来帮助软件国际化,自动化将用户界面翻译成多种语言的过程。随后,我在 Eclipse 中开发了一个 LotusScript(Lotus Notes 的脚本语言)编辑器,该编辑器可以直接与底层编译器进行交互。这个编辑器提供了第一流的开发功能,例如内容提示(提供建议)、实时语法错误报告等等。接着,我花了几年时间为 Lotus Domino 服务器构建基于 Java EE 和 OSGI 的中间件组件(www.osgi.org
)。在此期间,我带领团队通过将其引入当时可用的最新技术来现代化 Lotus Domino 编程模型。我对软件开发的各个方面都很熟悉,包括前端、中间件、后端数据层、工具等;我可以说是一个全栈开发者。
直到我看到 IBM Watson 问答系统的演示,2011 年它在《危险边缘!》节目中击败了长期冠军 Brad Rutter 和 Ken Jennings。哇!这真是一个突破性进展,一个能够回答自然语言问题的计算机程序。我非常感兴趣,在做了一些研究,拜访了几个参与该项目的研究人员,并了解了构建此系统所用的技术,如 NLP、机器学习和通用数据科学后,我意识到,如果将这项技术应用到商业的其他领域,它将有巨大的潜力。
几个月后,我有机会加入 IBM 新成立的 Watson 部门,领导一个工具团队,任务是为 Watson 系统构建数据摄取和准确性分析功能。我们的一个重要需求是确保工具对客户易于使用,这也是为什么回头看,将这个责任交给一个开发团队是正确的决定。从我的角度来看,接手这个职位既具挑战性又充满收获。我离开了一个熟悉的世界,在那里我擅长设计基于常见模式的架构,实施前端、中间件或后端软件组件,进入了一个主要聚焦于大数据工作的世界:获取数据、清洗数据、分析数据、可视化数据并构建模型。我花了前六个月像从火管中喝水一样,阅读和学习自然语言处理(NLP)、机器学习、信息检索和统计数据科学,至少足以能够参与我所构建的功能。
正是在那个时候,我与研究团队合作将这些算法推向市场,我意识到开发人员和数据科学家需要更好地协作。传统的方法是让数据科学家单独解决复杂的数据问题,然后将结果“抛给”开发人员,由他们来实现这些结果。但考虑到数据处理量不断呈指数级增长,以及市场所需时间日益缩短,这种做法是不可持续的,且无法扩展。
相反,他们的角色需要转变为作为一个团队共同工作,这意味着数据科学家必须像软件开发人员一样思考和工作,反之亦然。事实上,这在理论上看起来非常好:一方面,数据科学家将从成熟的软件开发方法论中受益,例如敏捷开发——其快速迭代和频繁反馈的方式——同时也能从严谨的软件开发生命周期中获益,带来符合企业需求的合规性,例如安全性、代码审查、源代码控制等。另一方面,开发人员将开始以全新的方式思考数据:将其视为用于发现洞察的分析,而不仅仅是具有查询和CRUD(即创建、读取、更新、删除)API 的持久层。
将这些概念付诸实践
在担任 Watson 核心工具首席架构师并为 Watson 问答系统构建自助工具四年后,我加入了 IBM Watson 数据平台组织的开发者倡导团队,后者的使命是创建一个平台,将数据和认知服务的产品组合带到 IBM 公共云。我们的任务相当简单:赢得开发者的心,并帮助他们在数据和 AI 项目中取得成功。
这项工作有多个维度:教育、宣传和激进主义。前两者比较直接,但激进主义的概念与这次讨论相关,并且值得详细解释。顾名思义,激进主义是关于在需要改变的地方带来变革。对于我们 15 位开发者倡导者的团队来说,这意味着要站在开发者的角度思考他们在处理数据时的体验——无论他们是刚刚开始,还是已经在应用高级算法——感受他们的痛点并识别应该解决的空白。为此,我们构建并开源了多个带有真实场景案例的数据管道示例。
至少,这些项目需要满足三个基本要求:
-
用作输入的原始数据必须是公开可用的
-
提供清晰的指令,确保数据管道可以在合理时间内部署到云端
-
开发者应该能够将该项目作为类似场景的起点,即代码必须具备高度的可定制性和可重用性
从这些练习中获得的经验和洞察是无价的:
-
理解哪些数据科学工具最适合每项任务
-
最佳实践框架和语言
-
部署和操作分析的最佳实践架构
指导我们选择的指标有很多:准确性、可扩展性、代码重用性,但最重要的是,改善数据科学家与开发者之间的协作。
深入探讨一个具体的例子
最初,我们希望构建一个数据管道,通过对包含特定标签的推文进行情感分析,提取 Twitter 中的洞察,并将结果部署到实时仪表板上。这个应用程序对我们来说是一个完美的起点,因为数据科学分析并不复杂,且该应用程序涵盖了许多现实场景的方面:
-
高流量、高吞吐量的流式数据
-
通过情感分析 NLP 进行数据增强
-
基本数据聚合
-
数据可视化
-
部署到实时仪表板
为了尝试这个方案,第一次实现是一个简单的 Python 应用程序,使用了 tweepy 库(Python 的官方 Twitter 库:pypi.python.org/pypi/tweepy
)来连接 Twitter,获取一系列的推文流,以及 textblob 库(用于基本 NLP 的简单 Python 库:pypi.python.org/pypi/textblob
)进行情感分析的丰富。
然后,结果被保存到 JSON 文件中进行分析。这个原型是启动项目并快速实验的一个绝佳方式,但经过几轮迭代后,我们迅速意识到,需要认真构建一个满足企业需求的架构。
数据管道蓝图
从高层次来看,数据管道可以通过以下通用蓝图来描述:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_01.jpg
数据管道工作流
数据管道的主要目标是将数据科学分析结果转化为可操作的(即提供直接的业务价值)输出,并且在可扩展、可重复的过程中实现高度自动化。分析的例子可以是一个推荐引擎,用于激励消费者购买更多产品,例如,亚马逊的推荐列表,或者一个展示关键绩效指标(KPI)的仪表板,帮助 CEO 为公司的未来决策提供依据。
构建数据管道的过程中涉及多个角色:
-
数据工程师:他们负责设计和运营信息系统。换句话说,数据工程师负责与数据源对接,获取原始数据,然后进行处理(有些人称之为数据清洗),直到数据准备好进行分析。在亚马逊推荐系统的例子中,他们会实现一个流处理管道,捕捉并聚合来自电商系统的特定消费者交易事件,并将这些数据存储到数据仓库中。
-
数据科学家:他们分析数据并构建提取洞察的分析模型。在亚马逊推荐系统的例子中,他们可能会使用 Jupyter Notebook 连接到数据仓库,加载数据集,并使用例如协同过滤算法来构建推荐引擎(
en.wikipedia.org/wiki/Collaborative_filtering
)。 -
开发人员:他们负责将分析功能转化为面向业务用户(如业务分析师、高层管理人员、最终用户等)的应用程序。在亚马逊推荐系统中,开发人员将在用户完成购买后或通过定期电子邮件呈现推荐的产品列表。
-
业务用户:指的是所有使用数据科学分析输出的用户,例如,业务分析师分析仪表板以监控业务健康状况,或最终用户使用一个提供推荐的应用程序来决定下一步购买什么。
注意
在现实生活中,通常同一个人可能会扮演多个角色;这意味着一个人在与数据管道互动时可能有多个不同的需求。
正如前面的图示所示,构建数据科学管道是一个迭代过程,并遵循一个明确定义的流程:
-
获取数据:此步骤包括从各种来源获取原始数据:结构化(关系数据库、记录系统等)或非结构化(网页、报告等):
-
数据清洗:检查数据完整性,填补缺失数据,修复不正确的数据,以及数据预处理
-
数据准备:丰富数据,检测/移除异常值,并应用业务规则
-
-
分析:此步骤结合了描述性(理解数据)和规范性(构建模型)活动:
-
探索:发现统计特性,例如集中趋势、标准差、分布,以及变量识别,如单变量和双变量分析、变量之间的相关性等。
-
可视化:这个步骤对于正确分析数据和形成假设至关重要。可视化工具应该提供合理的互动性,以便帮助理解数据。
-
构建模型:应用推断统计学来形成假设,例如为模型选择特征。这个步骤通常需要专家领域知识,并且有很大的解释空间。
-
-
部署:将分析阶段的结果转化为可操作的实际应用:
-
沟通:生成报告和仪表板,清晰地传达分析结果,以供业务用户(如高层管理人员、业务分析师等)使用
-
发现:设定一个聚焦于发现新见解和商业机会的业务目标,这些见解和机会可能会带来新的收入来源
-
实施:为最终用户创建应用程序
-
-
测试:这个活动应该贯穿于每一步,但在这里我们讨论的是创建一个来自实际应用的反馈循环:
-
创建衡量模型准确性的指标
-
优化模型,例如获取更多数据、寻找新特征等
-
成为数据科学家需要哪些技能?
在行业中,现实情况是,数据科学如此新兴,以至于公司还没有为其制定出明确的职业发展路径。那么,如何才能获得数据科学家的职位呢?需要多少年的经验?需要具备哪些技能?数学、统计学、机器学习、信息技术、计算机科学,还有什么?
其实,答案可能是“稍微懂一点儿各个领域”再加上一项关键技能:领域特定的专业知识。
目前有一种争论,关于是否在没有深入了解数据意义的情况下,将通用的数据科学技术应用于任何数据集,能否实现期望的商业结果。许多公司倾向于确保数据科学家具有足够的领域专业知识,其理由是,没有这一点,你可能会在任何步骤中无意间引入偏差,比如在数据清洗阶段填补空缺时,或在特征选择过程中,最终构建的模型可能非常适合某个数据集,但依然没有实际价值。想象一下,一个没有化学背景的数据科学家,正在为一家制药公司研究不需要的分子相互作用,帮助其开发新药。或许这也是为什么我们看到越来越多专门针对某一领域的统计学课程,例如生物学的生物统计学,或是供应链分析,专注于分析与供应链相关的运营管理等。
总结来说,一名数据科学家在理论上应该对以下领域有所熟练掌握:
-
数据工程 / 信息检索
-
计算机科学
-
数学与统计学
-
机器学习
-
数据可视化
-
商业智能
-
领域专业知识
注意事项
如果你正在考虑获取这些技能,但没有时间参加传统的课堂学习,我强烈推荐使用在线课程。
我特别推荐这门课程:www.coursera.org/
: www.coursera.org/learn/data-science-course
。
Drew 的 Conway 维恩图提供了一个关于什么是数据科学以及为什么数据科学家有点像独角兽的优秀可视化图示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_02.jpg
Drew 的 Conway 数据科学维恩图
到目前为止,我希望你已经明白,符合上述描述的完美数据科学家更像是例外而非常态,而且通常这个角色涉及多个身份。没错,我想表达的观点是,数据科学是一项团队运动,这个观点将在本书中反复出现。
IBM Watson DeepQA
有一个项目很好地说明了数据科学是一项团队运动的观点,那就是 IBM DeepQA 研究项目。这个项目最初是 IBM 发起的一个重大挑战,目的是构建一个能够回答自然语言问题的人工智能系统,依据预定的领域知识。**问答(QA)**系统应该足够好,能够与《危险边缘!》这档受欢迎的电视游戏节目中的人类选手竞争。
众所周知,这个名为 IBM Watson 的系统在 2011 年赢得了比赛,战胜了两位最有经验的*危险边缘!*冠军:Ken Jennings 和 Brad Rutter。以下照片来自 2011 年 2 月播出的实际比赛:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_03.jpg
IBM Watson 与 Ken Jennings 和 Brad Rutter 在《危险边缘!》上的对决!
来源:https://upload.wikimedia.org/wikipedia/e
正是在与构建 IBM Watson QA 计算机系统的研究团队互动时,我才得以更深入地了解 DeepQA 项目架构,并亲身体验到许多数据科学领域实际上是如何被应用的。
以下图示描述了 DeepQA 数据管道的高层次架构:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_04.jpg
Watson DeepQA 架构图
来源:https://researcher.watson.ibm.com/researcher/files/us-mi
如上图所示,回答问题的数据管道由以下高层次步骤组成:
-
问题与主题分析(自然语言处理):此步骤使用一个深度解析组件,检测构成问题的单词之间的依赖关系和层次结构。目标是更深入地理解问题,并提取出基本属性,例如:
-
重点:这个问题到底是在问什么?
-
词汇答案类型(LAT):期望答案的类型,例如一个人,一个地方等等。在候选答案的评分过程中,这些信息非常重要,因为它为与 LAT 不匹配的答案提供了早期过滤。
-
命名实体解析:将一个实体解析为标准化的名称,例如,将“Big Apple”解析为“New York”。
-
指代解析:将代词与问题中的先前术语连接起来,例如,在句子“1715 年 9 月 1 日,路易十四在这座城市去世,他建造了这座宏伟的宫殿”中,代词“他”指代的是路易十四。
-
关系检测:检测问题中的关系,例如,“她在 1954 年与乔·迪马吉奥离婚”,其中的关系是“乔·迪马吉奥娶了 X”。这类关系(主语->谓语->宾语)可以用于查询三元组存储,并得到高质量的候选答案。
-
问题分类:将问题映射到*Jeopardy!*中预定义的类型之一,例如,事实类问题、多项选择题、谜题等。
-
-
初步搜索与假设生成(信息检索):这一步骤主要依赖于问题分析步骤的结果,构建一组适应不同答案来源的查询。答案来源的一些例子包括各种全文搜索引擎,如 Indri(
www.lemurproject.org/indri.php
)和 Apache Lucene/Solr(lucene.apache.org/solr
),面向文档和标题的搜索(Wikipedia),三元组存储等等。搜索结果随后用于生成候选答案。例如,面向标题的结果将直接作为候选答案,而文档搜索则需要对段落进行更详细的分析(再次使用 NLP 技术)以提取可能的候选答案。 -
假设与证据评分(NLP 和信息检索):对于每个候选答案,进行另一次搜索以寻找更多支持证据,并使用不同的评分技术。此步骤还充当筛选测试,某些不符合步骤 1 中计算的 LAT 的候选答案将被淘汰。此步骤的输出是一组与找到的支持证据对应的机器学习特征。这些特征将作为输入,传递给一组机器学习模型,用于对候选答案进行评分。
-
最终合并与评分(机器学习):在最后一步中,系统识别相同答案的变体并将它们合并。它还使用机器学习模型,根据步骤 3 中生成的特征,选择由各自得分排名的最佳答案。这些机器学习模型已在一组代表性问题上进行训练,使用正确答案和预先摄取的文档语料库进行对比。
在继续讨论数据科学和人工智能如何改变计算机科学领域时,我认为有必要看看最先进的技术。IBM Watson 是这些旗舰项目之一,它为我们自它击败 Ken Jennings 和 Brad Rutter 参加Jeopardy!(危险游戏)以来的进步铺平了道路。
回到我们的 Twitter 话题情感分析项目
我们构建的快速数据管道原型使我们对数据有了更好的理解,但接下来我们需要设计一个更强大的架构,并使我们的应用程序准备好进入企业级。我们的主要目标仍然是积累构建数据分析的经验,而不是在数据工程部分花费太多时间。这就是为什么我们尽可能利用开源工具和框架的原因:
-
Apache Kafka (
kafka.apache.org
): 这是一个可扩展的流处理平台,用于以可靠和容错的方式处理大量的推文。 -
Apache Spark (
spark.apache.org
): 这是一个内存计算集群框架。Spark 提供了一个编程接口,抽象了并行计算的复杂性。 -
Jupyter Notebooks (
jupyter.org
): 这些基于 Web 的交互式文档(Notebooks)允许用户远程连接到计算环境(内核),以创建先进的数据分析。Jupyter 内核支持多种编程语言(Python、R、Java/Scala 等)以及多个计算框架(Apache Spark、Hadoop 等)。
对于情感分析部分,我们决定用 Watson Tone Analyzer 服务 (www.ibm.com/watson/services/tone-analyzer
) 替换我们使用 textblob Python 库编写的代码。Watson Tone Analyzer 是一个基于云的 REST 服务,提供包括情感、语言和社交语气检测在内的高级情感分析。尽管 Tone Analyzer 不是开源的,但 IBM 云上提供了一个可供开发和试用的免费版本 (www.ibm.com/cloud
)。
我们的架构现在如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_05.jpg
Twitter 情感分析数据管道架构
在上述图中,我们可以将工作流程分解为以下几个步骤:
-
产生一系列推文并将其发布到 Kafka 主题中,Kafka 主题可以看作是将事件聚集在一起的通道。接收组件可以订阅该主题/通道以消费这些事件。
-
通过情感、语言和社交语气得分来丰富推文:使用 Spark Streaming 订阅来自组件1的 Kafka 主题,并将文本发送到 Watson Tone Analyzer 服务。生成的语气得分将被添加到数据中,供下游进一步处理。此组件是使用 Scala 实现的,为了方便起见,它是通过 Jupyter Scala Notebook 运行的。
-
数据分析和探索:对于这一部分,我们决定使用 Python Notebook,因为 Python 提供了更具吸引力的库生态系统,尤其是在数据可视化方面。
-
将结果发布回 Kafka。
-
实现一个实时仪表板作为 Node.js 应用。
在三人团队的协作下,我们花了大约 8 周的时间,成功将仪表板与实时 Twitter 情感数据对接。这个看似较长的时间有多个原因:
-
一些框架和服务,比如 Kafka 和 Spark Streaming,对我们来说是全新的,我们必须学习如何使用它们的 API。
-
仪表板前端是作为一个独立的 Node.js 应用构建的,使用了 Mozaïk 框架(
github.com/plouc/mozaik
),它使得构建强大的实时仪表板变得简单。然而,我们发现代码中有一些局限性,这迫使我们深入实现,编写补丁,因此对整体进度造成了一定延迟。
结果展示在以下截图中:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_06.jpg
Twitter 情感分析实时仪表板
从构建我们第一个企业级数据管道中学到的经验教训
利用开源框架、库和工具,的确帮助我们在实现数据管道时提高了效率。例如,Kafka 和 Spark 部署相对简单,使用也非常方便,而且当我们遇到瓶颈时,我们可以随时依赖开发者社区的帮助,例如通过使用像stackoverflow.com
这样的问题解答网站。
使用基于云的托管服务进行情感分析步骤,比如 IBM Watson Tone Analyzer(www.ibm.com/watson/services/tone-analyzer
),也是一个积极的做法。它让我们可以抽象出训练和部署模型的复杂性,使得整个步骤更可靠,显然比我们自己实现要准确得多。
它也非常容易集成,因为我们只需要发送一个 REST 请求(也称为 HTTP 请求,关于 REST 架构的更多信息,请参考en.wikipedia.org/wiki/Representational_state_transfer
)就能获得我们的答案。现在,大多数现代 Web 服务都遵循 REST 架构,然而,我们仍然需要了解每个 API 的规范,这通常需要很长时间才能正确实现。通过使用 SDK 库,这一步通常会变得更简单,SDK 库通常是免费的,并且支持大多数流行的编程语言,如 Python、R、Java 和 Node.js。SDK 库通过抽象出生成 REST 请求的代码,提供了更高级的程序化访问服务。SDK 通常会提供一个类来表示服务,每个方法封装一个 REST API,并处理用户认证和其他请求头。
在工具方面,我们对 Jupyter Notebooks 印象深刻,它提供了许多出色的功能,例如协作和完全交互性(我们稍后会更详细地介绍 Notebooks)。
当然,并非一切都很顺利,我们在一些关键领域遇到了困难:
-
在选择编程语言方面,对于一些关键任务,如数据丰富和数据分析,我们最终选择了 Scala 和 Python,尽管团队经验不多,主要是因为它们在数据科学家中非常受欢迎,而且我们也希望学习它们。
-
创建数据探索的可视化图表耗费了太多时间。使用可视化库(如 Matplotlib 或 Bokeh)编写一个简单的图表需要写大量的代码。这反过来又拖慢了我们对快速实验的需求。
-
将分析功能操作化为实时仪表板的过程非常困难,难以扩展。如前所述,我们需要编写一个完整的独立 Node.js 应用程序,该应用程序从 Kafka 消费数据,并且需要作为云平台应用程序(
www.cloudfoundry.org
)部署在 IBM Cloud 上。可以理解的是,这个任务第一次完成时需要相当长的时间,但我们还发现,更新它也很困难。写入 Kafka 的分析功能需要与仪表板应用程序上的更改同步。
数据科学战略
如果数据科学要继续增长并成为核心业务活动,公司必须找到一种方法,在整个组织的所有层面上扩展它,并克服我们之前讨论的所有困难挑战。为了实现这一目标,我们确定了三大重要支柱,架构师在规划数据科学战略时应重点关注这三点:数据、服务和工具:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_07.jpg
大规模数据科学的三大支柱
-
数据是你最宝贵的资源:你需要一个合适的数据策略,确保数据科学家可以轻松访问所需的精心策划的内容。通过对数据进行适当的分类、设定合适的治理政策,并使元数据可搜索,将减少数据科学家获取数据并请求使用权限的时间。这不仅能提高他们的生产力,还能提升他们的工作满意度,因为他们会有更多的时间从事实际的数据科学工作。
制定一个数据策略,使数据科学家能够轻松访问与其相关的高质量数据,能够提高生产力和士气,并最终带来更高的成功率。
-
服务:每个规划数据科学的架构师都应该考虑采用面向服务架构(SOA)。与传统的单体应用程序不同,后者将所有功能捆绑在一起进行单一部署,面向服务的系统将功能拆解为若干服务,每个服务只负责做少数几件事,但做得非常出色,且具备高性能和可扩展性。这些系统可以独立部署和维护,相互之间没有依赖,从而为整个应用基础设施提供可扩展性和可靠性。例如,你可以有一个运行算法创建深度学习模型的服务,另一个则负责持久化模型并允许应用程序运行它来对客户数据进行预测,等等。
优势显而易见:高度的可重用性、更容易的维护、更短的上市时间、可扩展性等等。此外,这种方法还非常适合融入云策略,当你的工作负载超出现有容量时,它能够提供增长路径。你还需要优先考虑开源技术,并尽可能标准化开放协议。
将流程拆分为更小的功能模块,能够为系统注入可扩展性、可靠性和重复性。
-
工具确实很重要! 没有合适的工具,一些任务将变得极其难以完成(至少这是我用来解释自己为什么修不好家里东西的理由)。然而,你也需要保持工具的简单性、标准化,并合理地集成它们,以便即便是技术水平较低的用户也能使用(即使我得到了正确的工具,我也不确定自己能否完成家务修理任务,除非它足够简单)。一旦你降低了这些工具的学习曲线,非数据科学家的用户将会感到更加舒适使用它们。
简化工具的使用有助于打破信息孤岛,并增强数据科学、工程和业务团队之间的协作。
Jupyter Notebooks 是我们策略的核心
本质上,Notebooks 是由可编辑单元格组成的网页文档,允许你与后端引擎进行交互式命令操作。正如其名称所示,我们可以将其视为纸质便签的数字版,用于记录实验的笔记和结果。这个概念既强大又简单:用户可以在自己选择的编程语言中输入代码(大多数 Notebooks 实现支持多种语言,如 Python、Scala、R 等),运行单元并在单元下方的输出区域互动式地查看结果,这些结果会成为文档的一部分。结果可以是任何类型:文本、HTML 和图像,非常适合数据图表化。这就像是在传统的 REPL(读取-评估-打印-循环)程序上使用增强版,因为 Notebook 可以连接到强大的计算引擎(例如 Apache Spark(spark.apache.org
)或 Python Dask(dask.pydata.org
)集群),如果需要,可以进行大数据实验。
在 Notebooks 中,任何在单元格中创建的类、函数或变量都可以在下方的单元格中看到,允许你逐步编写复杂的分析,迭代地测试假设并在进入下一个阶段之前解决问题。此外,用户还可以使用流行的 Markdown 语言编写富文本,或者使用 LaTeX(www.latex-project.org/
)编写数学表达式,来描述他们的实验供其他人阅读。
下图展示了一个示例 Jupyter Notebook 的部分内容,其中包含一个 Markdown 单元,解释实验的内容,一个用 Python 编写的代码单元用于创建 3D 图表,以及实际的 3D 图表结果:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_01_08.jpg
样本 Jupyter Notebook
为什么 Notebooks 如此受欢迎?
在过去的几年里,Notebooks 作为数据科学相关活动的首选工具,迎来了爆炸式的增长。解释这一现象的原因有很多,但我认为主要原因是其多功能性,使得它不仅是数据科学家不可或缺的工具,也是参与构建数据管道的大多数角色,包括业务分析师和开发人员的必备工具。
对于数据科学家来说,Notebooks 非常适合迭代实验,因为它使得他们能够快速加载、探索和可视化数据。Notebooks 也是一个出色的协作工具;它们可以导出为 JSON 文件,并轻松地在团队之间共享,允许实验在需要时能够精确重复和调试。此外,由于 Notebooks 还是网页应用,它们可以轻松集成到基于云的多用户环境中,从而提供更好的协作体验。
这些环境还可以通过使用像 Apache Spark 这样的框架,将笔记本与机器集群连接,从而按需提供大规模计算资源。这些基于云的笔记本服务器的需求正在迅速增长,因此,我们看到越来越多的SaaS(即软件即服务)解决方案,不仅包括商业产品,如 IBM 数据科学体验(datascience.ibm.com
)或 DataBricks(databricks.com/try-databricks
),还包括开源解决方案,如 JupyterHub(jupyterhub.readthedocs.io/en/latest
)。
对于商业分析师,笔记本可以作为演示工具,在大多数情况下,凭借其对 Markdown 的支持,提供足够的功能来取代传统的 PowerPoint。生成的图表和表格可以直接用于有效地传达复杂分析的结果;不再需要复制粘贴,并且算法的更改会自动反映在最终演示中。例如,某些笔记本实现(如 Jupyter)提供了将单元格布局自动转换为幻灯片的功能,使得整个体验更加无缝。
注意
作为参考,以下是在 Jupyter 笔记本中制作这些幻灯片的步骤:
-
使用视图 | 单元工具栏 | 幻灯片放映,首先通过选择幻灯片、子幻灯片、片段、跳过或笔记来标注每个单元格。
-
使用
nbconvert jupyter
命令将笔记本转换为 Reveal.js 驱动的 HTML 幻灯片: -
可选地,你可以启动一个 web 应用服务器,以便在线访问这些幻灯片:
jupyter nbconvert <pathtonotebook.ipynb> --to slides
jupyter nbconvert <pathtonotebook.ipynb> --to slides –post serve
对于开发人员来说,情况要复杂得多。一方面,开发人员喜欢 REPL 编程,而笔记本提供了交互式 REPL 的所有优势,此外,它还可以连接到远程后端。由于它是在浏览器中运行的,结果可以包含图形,而且由于它们可以被保存,整个笔记本或其部分内容可以在不同的场景中重复使用。因此,对于开发人员来说,只要你选择的语言可用,笔记本提供了一种很好的方式来尝试和测试不同的内容,比如微调算法或集成新的 API。另一方面,尽管开发人员最终负责将分析结果转化为满足客户需求的应用程序,但他们对笔记本的采纳率很低,尤其是在数据科学活动中,这些活动本可以补充数据科学家的工作。
为了改善软件开发生命周期并缩短价值实现的时间,开发者需要开始使用与数据科学家相同的工具、编程语言和框架,包括 Python 及其丰富的库生态系统,以及已经成为数据科学重要工具的 Notebooks。尽管开发者必须在数据科学的理论和概念上与数据科学家站在一起并迅速掌握,但根据我的经验,我强烈推荐使用MOOCs(大规模在线开放课程),如 Coursera(www.coursera.org
)或 EdX(www.edx.org
),这些平台提供各种各样的课程,适合各个层次的学习者。
然而,尽管 Notebook 功能强大,但它们主要是为数据科学家设计的,开发者需要面临陡峭的学习曲线。它们还缺乏开发者所需的应用开发功能,这对开发者至关重要。正如我们在Twitter 话题标签情感分析项目中所看到的,基于 Notebook 中创建的分析构建应用程序或仪表板可能非常困难,需要一种架构,而这种架构不仅难以实现,而且对基础设施的负担较重。
正是为了解决这些问题,我决定创建 PixieDust(github.com/ibm-watson-data-lab/pixiedust
)库并将其开源。如我们在接下来的章节中将看到的,PixieDust 的主要目标是通过提供简单的 API 来加载和可视化数据,从而降低新用户(无论是数据科学家还是开发者)的入门成本。PixieDust 还提供了一个开发者框架,带有 API,方便开发者构建可以直接在 Notebook 中运行并部署为 Web 应用程序的应用、工具和仪表板。
总结
在本章中,我从开发者的角度分享了我对数据科学的看法,讨论了为什么我认为数据科学与人工智能(AI)和云计算(Cloud)一起,具有定义下一代计算时代的潜力。我还讨论了在数据科学充分实现其潜力之前,必须解决的许多问题。尽管本书并不打算提供一个解决所有这些问题的“魔法食谱”,但它确实尝试回答一个既困难又关键的问题,那就是如何实现数据科学的民主化,特别是如何弥合数据科学家和开发者之间的鸿沟。
在接下来的几章中,我们将深入探讨 PixieDust 开源库,了解它如何帮助 Jupyter Notebook 用户在处理数据时提高效率。我们还将深入研究 PixieApp 应用开发框架,该框架使开发者能够利用 Notebook 中实现的分析来构建应用程序和仪表板。
在接下来的章节中,我们将深入探讨许多示例,展示数据科学家和开发者如何有效地协作,构建端到端的数据管道,迭代分析,并将其在更短的时间内部署到最终用户。这些示例应用将涵盖多个行业用例,如图像识别、社交媒体和金融数据分析,其中包括数据科学用例,如描述性分析、机器学习、自然语言处理和流数据。
我们不会深入讨论示例应用中涵盖的所有算法背后的理论(这超出了本书的范围,且需要一本书来讲解),而是将重点强调如何利用开源生态系统快速完成手头的任务(如模型构建、可视化等),并将结果转化为应用程序和仪表板。
注意
提供的示例应用主要使用 Python 编写,并附带完整的源代码。代码经过广泛测试,已经准备好可以在您的项目中重复使用和定制。
第二章 Python 和 Jupyter Notebooks 助力你的数据分析
“最好的代码是你不必写的那一行!”
– 未知
在上一章中,我从开发者的角度,基于真实经验探讨了数据科学,并讨论了成功部署企业所需的三个战略支柱:数据、服务和工具。我还讨论了一个观点,即数据科学不仅仅是数据科学家的专属领域,它实际上是一个团队合作的过程,其中开发者扮演着特殊的角色。
在本章中,我将介绍一个解决方案——基于 Jupyter Notebooks、Python 和 PixieDust 开源库——它专注于三个简单的目标:
-
通过降低非数据科学家的准入门槛,实现数据科学的普及
-
增加开发者与数据科学家之间的合作
-
让数据科学分析更易于操作化
注意
该解决方案仅聚焦于工具层面,而不涉及数据和服务,尽管我们将在第六章 数据分析研究:使用 TensorFlow 的 AI 与图像识别中讨论一些内容,数据和服务应独立实现。
为什么选择 Python?
像许多开发者一样,在构建数据密集型项目时,使用 Python 并不是我的首选。说实话,经过多年的 Java 工作经验后,最初 Scala 对我来说更具吸引力,尽管学习曲线相当陡峭。Scala 是一种非常强大的语言,它优雅地结合了面向对象编程和函数式编程,而这些在 Java 中是极为缺乏的(至少直到 Java 8 开始引入 Lambda 表达式)。
Scala 还提供了非常简洁的语法,这转化为更少的代码行、更高的生产力,最终也减少了错误。这在你大部分工作都涉及数据操作时非常有用。喜欢 Scala 的另一个原因是,当使用像 Apache Spark 这样的“大数据”框架时,它的 API 支持更好,而这些框架本身就是用 Scala 编写的。还有很多其他理由使得 Scala 更受青睐,比如它是一个强类型系统,能够与 Java 互操作,拥有在线文档,并且性能高。
因此,对于像我这样的开发者,刚开始涉足数据科学时,Scala 似乎是一个更自然的选择,但实际上,剧透一下,我们最终选择了 Python。这一选择有多个原因:
-
Python 作为一种语言也有很多优点。它是一种动态编程语言,具有与 Scala 类似的优势,比如函数式编程和简洁的语法等。
-
在过去几年里,Python 在数据科学家中迅速崛起,超过了长期竞争对手 R,成为数据科学领域的首选语言,这一点通过在 Google Trends 上搜索
Python Data Science
、Python Machine Learning
、R Data Science
和R Machine Learning
可以看到:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_01.jpg2017 年的兴趣趋势
在良性循环中,Python 日益增长的流行度推动了一个庞大而不断扩展的生态系统,涵盖了各种各样的库,这些库可以通过 pip Python 包安装器轻松导入到项目中。数据科学家现在可以使用许多强大的开源 Python 库进行数据处理、数据可视化、统计学、数学、机器学习和自然语言处理。
即使是初学者,也可以通过流行的 scikit-learn 包 (scikit-learn.org
) 快速构建机器学习分类器,而不需要成为机器学习专家,或者使用 Matplotlib (matplotlib.org
) 或 Bokeh (bokeh.pydata.org
) 快速绘制丰富的图表。
此外,Python 也成为了开发者的顶级语言之一,正如 IEEE Spectrum 2017 年调查所示 (spectrum.ieee.org/computing/software/the-2017-top-programming-languages
):
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_02.jpg
按编程语言划分的使用统计
这一趋势在 GitHub 上也得到了确认,Python 现在在所有仓库的总数中排名第三,仅次于 Java 和 JavaScript:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_03.jpg
按编程语言划分的 GitHub 仓库统计
上面的图表展示了一些有趣的统计数据,说明了 Python 开发者社区的活跃程度。在 GitHub 上,Python 相关的活跃仓库是第三大,拥有健康的总代码推送和每个仓库的开启问题数量。
Python 还在网页开发中变得无处不在,许多知名网站都采用了如 Django (www.djangoproject.com
)、Tornado (www.tornadoweb.org
)和 TurboGears (turbogears.org
)等网页开发框架。最近,Python 也开始渗透到云服务领域,所有主要云服务提供商都将其作为某种程度的服务纳入其产品中。
显然,Python 在数据科学领域有着光明的前景,特别是当与强大的工具如 Jupyter Notebooks 一起使用时,Jupyter Notebooks 在数据科学家社区中已经变得非常流行。Notebook 的价值主张在于,它们非常容易创建,非常适合快速运行实验。此外,Notebook 支持多种高保真度的序列化格式,可以捕捉指令、代码和结果,然后很容易与团队中的其他数据科学家分享,或作为开源项目供所有人使用。例如,我们看到越来越多的 Jupyter Notebooks 在 GitHub 上被分享,数量已超过 250 万,并且还在不断增加。
下图显示了在 GitHub 上搜索所有扩展名为 .ipynb
的文件的结果,这是最流行的 Jupyter Notebooks 序列化格式(JSON 格式):
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_04.jpg
在 GitHub 上搜索 Jupyter Notebooks 的结果
这很好,但 Jupyter Notebooks 常常被认为仅仅是数据科学家的工具。我们将在接下来的章节中看到,它们可以做得更多,而且可以帮助所有类型的团队解决数据问题。例如,它们可以帮助业务分析师快速加载和可视化数据集,使开发者能够直接在 Notebook 中与数据科学家合作,利用他们的分析并构建强大的仪表盘,或允许 DevOps 将这些仪表盘轻松地部署到可扩展的企业级微服务中,这些微服务可以作为独立的 Web 应用程序运行,或者作为可嵌入的组件。正是基于将数据科学工具带给非数据科学家的愿景,才创建了 PixieDust 开源项目。
介绍 PixieDust
小贴士
有趣的事实
我经常被问到我如何想出“PixieDust”这个名字,对此我回答说,我只是想让 Notebook 对非数据科学家来说变得简单,就像魔法一样。
PixieDust (github.com/ibm-watson-data-lab/pixiedust
) 是一个开源项目,主要由三个组件组成,旨在解决本章开头提到的三个目标:
-
一个为 Jupyter Notebooks 提供的辅助 Python 库,提供简单的 API 来将数据从各种来源加载到流行的框架中,如 pandas 和 Apache Spark DataFrame,然后交互式地可视化和探索数据集。
-
一种基于 Python 的简单编程模型,使开发者能够通过创建强大的仪表盘(称为 PixieApps)将分析“产品化”到 Notebook 中。正如我们将在接下来的章节中看到的,PixieApps 与传统的 BI(即 商业智能)仪表盘不同,因为开发者可以直接使用 HTML 和 CSS 创建任意复杂的布局。此外,他们还可以在其业务逻辑中嵌入对 Notebook 中创建的任何变量、类或函数的访问。
-
一种名为 PixieGateway 的安全微服务 Web 服务器,可以将 PixieApps 作为独立的 Web 应用程序运行,或作为可以嵌入任何网站的组件。PixieApps 可以通过 Jupyter Notebook 使用图形向导轻松部署,并且无需任何代码更改。此外,PixieGateway 支持将任何由 PixieDust 创建的图表作为可嵌入的网页共享,使数据科学家可以轻松地将结果传达给 Notebook 外部的受众。
需要注意的是,PixieDust display()
API 主要支持两种流行的数据处理框架:
-
pandas (
pandas.pydata.org
): 迄今为止最受欢迎的 Python 数据分析包,pandas 提供了两种主要的数据结构:DataFrame 用于处理类似二维表格的数据集,Series 用于处理一维列状数据集。注意
目前,PixieDust
display()
只支持 pandas DataFrame。 -
Apache Spark DataFrame (
spark.apache.org/docs/latest/sql-programming-guide.html
): 这是一个高层数据结构,用于操作 Spark 集群中分布式数据集。Spark DataFrame 建立在低层的 RDD(即 Resilient Distributed Dataset)之上,并且增加了支持 SQL 查询的功能。
另一种 PixieDust display()
支持的较少使用的格式是 JSON 对象数组。在这种情况下,PixieDust 会使用这些值来构建行,并将键作为列,例如如下所示:
my_data = [
{"name": "Joe", "age": 24},
{"name": "Harry", "age": 35},
{"name": "Liz", "age": 18},
...
]
此外,PixieDust 在数据处理和渲染层面都具有高度的可扩展性。例如,你可以向可视化框架添加新的数据类型,或者如果你特别喜欢某个绘图库,你可以轻松地将其添加到 PixieDust 支持的渲染器列表中(更多细节请参见接下来的章节)。
你还会发现,PixieDust 包含了一些与 Apache Spark 相关的附加工具,例如以下内容:
-
PackageManager:这使你可以在 Python Notebook 中安装 Spark 包。
-
Scala 桥接:这使你可以在 Python Notebook 中直接使用 Scala,通过
%%scala
魔法命令。变量会自动从 Python 转移到 Scala,反之亦然。 -
Spark 作业进度监控器:通过在单元格输出中显示进度条,跟踪任何 Spark 作业的状态。
在我们深入了解 PixieDust 的三个组件之前,建议先获取一个 Jupyter Notebook,可以通过注册云端托管解决方案(例如,Watson Studio,网址:datascience.ibm.com
)或在本地机器上安装开发版来实现。
注意
你可以按照以下说明在本地安装 Notebook 服务器:jupyter.readthedocs.io/en/latest/install.html
。
要在本地启动 Notebook 服务器,只需从终端运行以下命令:
jupyter notebook --notebook-dir=<<directory path where notebooks are stored>>
Notebook 主页将自动在浏览器中打开。有许多配置选项可以控制 Notebook 服务器的启动方式。这些选项可以添加到命令行或持久化到 Notebook 配置文件中。如果你想尝试所有可能的配置选项,可以使用--generate-config
选项生成一个配置文件,如下所示:
jupyter notebook --generate-config
这将生成以下 Python 文件<home_directory>/.jupyter/jupyter_notebook_config.py
,其中包含一组已禁用的自动文档选项。例如,如果你不希望 Jupyter Notebook 启动时自动打开浏览器,找到包含sc.NotebookApp.open_browser
变量的行,取消注释并将其设置为False
:
## Whether to open in a browser after starting. The specific browser used is
# platform dependent and determined by the python standard library 'web browser'
# module, unless it is overridden using the --browser (NotebookApp.browser)
# configuration option.
c.NotebookApp.open_browser = False
在做完更改后,只需保存jupyter_notebook_config.py
文件并重新启动 Notebook 服务器。
下一步是使用pip
工具安装 PixieDust 库:
-
从 Notebook 本身,输入以下命令在单元格中执行:
!pip install pixiedust
注意
注意:感叹号语法是 Jupyter Notebook 特有的,表示后续的命令将作为系统命令执行。例如,你可以使用
!ls
列出当前工作目录下的所有文件和目录。 -
使用单元格 | 运行单元格菜单或工具栏上的运行图标来运行单元格。你也可以使用以下键盘快捷键来运行单元格:
-
Ctrl + Enter:运行并保持当前单元格选中
-
Shift + Enter:运行并选中下一个单元格
-
Alt + Enter:运行并在下方创建一个新的空单元格
-
-
重新启动内核以确保
pixiedust
库已正确加载到内核中。
以下截图显示了首次安装pixiedust
后的结果:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_05.jpg
在 Jupyter Notebook 上安装 PixieDust 库
提示
我强烈推荐使用 Anaconda(anaconda.org
),它提供了优秀的 Python 包管理功能。如果你像我一样喜欢尝试不同版本的 Python 和库依赖,我建议你使用 Anaconda 虚拟环境。
它们是轻量级的 Python 沙箱,创建和激活非常简单(请参见conda.io/docs/user-guide/tasks/manage-environments.html
):
-
创建新环境:
conda create --name env_name
-
列出所有环境:
conda env list
-
激活环境:
source activate env_name
我还推荐你可以选择性地熟悉源代码,源代码可以在github.com/ibm-watson-data-lab/pixiedust
和github.com/ibm-watson-data-lab/pixiegateway
找到。
我们现在准备好在下一节中探索 PixieDust API,从sampleData()
开始。
SampleData – 一个简单的数据加载 API
将数据加载到 Notebook 中是数据科学家最常做的重复性任务之一,但根据使用的框架或数据源,编写代码可能会很困难且耗时。
让我们以一个具体的例子来说明,尝试从一个开放数据网站(例如 data.cityofnewyork.us
)加载 CSV 文件到 pandas 和 Apache Spark DataFrame。
注意
注意:接下来的所有代码都假定在 Jupyter Notebook 中运行。
对于 pandas,代码相当简单,因为它提供了一个直接从 URL 加载的 API:
import pandas
data_url = "https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD"
building_df = pandas.read_csv(data_url)
building_df
最后一条语句,调用building_df,
,将在输出单元中打印其内容。由于 Jupyter 会将单元格最后一条调用变量的语句作为打印指令,因此可以在不显式使用 print
的情况下实现此操作:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_06.jpg
pandas DataFrame 的默认输出
然而,对于 Apache Spark,我们首先需要将数据下载到文件中,然后使用 Spark CSV 连接器将其加载到 DataFrame 中:
#Spark CSV Loading
from pyspark.sql import SparkSession
try:
from urllib import urlretrieve
except ImportError:
#urlretrieve package has been refactored in Python 3
from urllib.request import urlretrieve
data_url = "https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD"
urlretrieve (data_url, "building.csv")
spark = SparkSession.builder.getOrCreate()
building_df = spark.read\
.format('org.apache.spark.sql.execution.datasources.csv.CSVFileFormat')\
.option('header', True)\
.load("building.csv")
building_df
输出略有不同,因为 building_df
现在是一个 Spark DataFrame:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_07.jpg
Spark DataFrame 的默认输出
尽管这段代码并不复杂,但每次都需要重复执行,并且很可能需要花时间做 Google 搜索来记住正确的语法。数据也可能采用不同的格式,例如 JSON,这需要调用不同的 API,无论是对于 pandas 还是 Spark。数据可能也不是很规范,可能在 CSV 文件中有错误行,或者 JSON 语法不正确。所有这些问题不幸的是并不罕见,并且符合数据科学的 80/20 法则,该法则表明数据科学家平均花费 80% 的时间来获取、清洗和加载数据,仅有 20% 的时间用于实际分析。
PixieDust 提供了一个简单的 sampleData
API 来帮助改善这种情况。当不带参数调用时,它会显示一个预先策划的、准备好进行分析的数据集列表:
import pixiedust
pixiedust.sampleData()
结果如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_08.jpg
PixieDust 内置数据集
预构建的策划数据集列表可以根据组织的需要进行自定义,这是朝着我们数据支柱迈出的好步骤,如上一章所述。
用户可以简单地再次调用 sampleData
API,传入预构建数据集的 ID,如果 Jupyter 内核中有 Spark 框架可用,则会获得一个 Spark DataFrame;如果不可用,则会回退为 pandas DataFrame。
在下面的示例中,我们在与 Spark 连接的 Notebook 上调用 sampleData()
。我们还调用 enableSparkJobProgressMonitor()
来显示涉及操作的 Spark 作业的实时信息。
注意
注意:Spark 作业是在 Spark 集群中的特定节点上运行的进程,处理特定子集的数据。对于从数据源加载大量数据的情况,每个 Spark 作业会分配一个特定的子集来处理(实际大小取决于集群中的节点数和总体数据的大小),并与其他作业并行运行。
在一个单独的单元格中,我们运行以下代码来启用 Spark 作业进度监视器:
pixiedust.enableSparkJobProgressMonitor()
结果如下:
Successfully enabled Spark Job Progress Monitor
接着,我们调用 sampleData
来加载 cars
数据集:
cars = pixiedust.sampleData(1)
结果如下所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_09.jpg
使用 PixieDust sampleData API 加载内置数据集
用户还可以传入指向可下载文件的任意 URL;PixieDust 目前支持 JSON 和 CSV 文件。在这种情况下,PixieDust 会自动下载该文件,缓存到临时区域,检测格式,并根据 Notebook 中是否可用 Spark 将其加载到 Spark 或 pandas DataFrame 中。请注意,即使 Spark 可用,用户也可以通过使用 forcePandas
关键字参数强制加载到 pandas 中:
import pixiedust
data_url = "https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD"
building_dataframe = pixiedust.sampleData(data_url, forcePandas=True)
结果如下:
Downloading 'https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD' from https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD
Downloaded 13672351 bytes
Creating pandas DataFrame for 'https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD'. Please wait...
Loading file using 'pandas'
Successfully created pandas DataFrame for 'https://data.cityofnewyork.us/api/views/e98g-f8hy/rows.csv?accessType=DOWNLOAD'
sampleData()
API 足够智能,能够识别指向 ZIP 和 GZ 类型压缩文件的 URL。在这种情况下,它会自动解压原始二进制数据,并加载归档中包含的文件。对于 ZIP 文件,它会查看归档中的第一个文件,而对于 GZ 文件,它会简单地解压内容,因为 GZ 文件不是归档文件,不包含多个文件。sampleData()
API 然后会从解压后的文件加载 DataFrame。
例如,我们可以直接从伦敦开放数据网站提供的 ZIP 文件加载区信息,并使用 display()
API 将结果显示为饼图,具体如下:
import pixiedust
london_info = pixiedust.sampleData("https://files.datapress.com/london/dataset/london-borough-profiles/2015-09-24T15:50:01/London-borough-profiles.zip")
结果如下(假设你的 Notebook 已连接到 Spark,否则将加载一个 pandas DataFrame):
Downloading 'https://files.datapress.com/london/dataset/london-borough-profiles/2015-09-24T15:50:01/London-borough-profiles.zip' from https://files.datapress.com/london/dataset/london-borough-profiles/2015-09-24T15:50:01/London-borough-profiles.zip
Extracting first item in zip file...
File extracted: london-borough-profiles.csv
Downloaded 948147 bytes
Creating pySpark DataFrame for 'https://files.datapress.com/london/dataset/london-borough-profiles/2015-09-24T15:50:01/London-borough-profiles.zip'. Please wait...
Loading file using 'com.databricks.spark.csv'
Successfully created pySpark DataFrame for 'https://files.datapress.com/london/dataset/london-borough-profiles/2015-09-24T15:50:01/London-borough-profiles.zip'
然后我们可以在 london_info
DataFrame 上调用 display()
,如图所示:
display(london_info)
我们在图表菜单中选择 饼图,在 选项 对话框中,将 Area name
列拖放到 键 区域,将 Crime rates per thousand population 2014/15
拖放到 值 区域,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_10.jpg
用于可视化 london_info DataFrame 的图表选项
在 选项 对话框中点击 确定 按钮后,我们得到以下结果:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_11.jpg
从指向压缩文件的 URL 创建饼图
许多时候,您可能找到了一个很棒的数据集,但文件中包含错误,或者对您重要的数据格式不正确,或者被埋在一些非结构化文本中需要提取到自己的列中。这个过程也被称为数据整理,可能非常耗时。在接下来的部分中,我们将看到一个名为pixiedust_rosie
的 PixieDust 扩展,它提供了一个wrangle_data
方法,可以帮助处理这个过程。
使用 pixiedust_rosie 整理数据
在受控实验中工作,大多数情况下与在真实世界中工作并不相同。我的意思是,在开发过程中,我们通常会选择(或者我应该说制造)一个设计良好、符合模式规范、没有数据缺失等特性的样本数据集。目标是专注于验证假设并构建算法,而不是数据清洗,这可能非常痛苦和耗时。然而,在开发过程的早期尽可能接近真实数据确实有不可否认的好处。为了帮助完成这项任务,我与两位 IBM 同事 Jamie Jennings 和 Terry Antony 合作,他们志愿构建了一个名为pixiedust_rosie
的 PixieDust 扩展。
这个 Python 包实现了一个简单的wrangle_data()
方法来自动清理原始数据。pixiedust_rosie
包目前支持 CSV 和 JSON 格式,但未来将添加更多格式。底层数据处理引擎使用了**Rosie 模式语言(RPL)**开源组件,这是一个专为开发人员设计、更高效、可扩展到大数据的正则表达式引擎。您可以在这里找到更多关于 Rosie 的信息:rosie-lang.org
。
要开始使用,您需要使用以下命令安装pixiedust_rosie
包:
!pip install pixiedust_rosie
pixiedust_rosie
包依赖于pixiedust
和rosie
,如果系统上尚未安装,将自动下载。
wrangle_data()
方法与sampleData()
API 非常相似。如果不带参数调用,它将显示预先筛选数据集的列表,如下所示:
import pixiedust_rosie
pixiedust_rosie.wrangle_data()
这将产生以下结果:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_12.jpg
预先筛选的数据集列表可用于wrangle_data()
。
您还可以通过预先筛选的数据集的 ID 或 URL 链接来调用它,例如:
url = "https://github.com/ibm-watson-data-lab/pixiedust_rosie/raw/master/sample-data/Healthcare_Cost_and_Utilization_Project__HCUP__-_National_Inpatient_Sample.csv"
pixiedust_rosie.wrangle_data(url)
在上面的代码中,我们在由url
变量引用的 CSV 文件上调用wrangle_data()
。该函数首先下载文件到本地文件系统,并对数据的一个子集执行自动化数据分类,以推断数据架构。随后启动一个架构编辑器 PixieApp,提供一组向导屏幕,允许用户配置架构。例如,用户将能够删除和重命名列,更重要的是,通过提供 Rosie 模式将现有列解构为新列。
工作流在以下图示中进行说明:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_13.jpg
wrangle_data()
工作流
wrangle_data()
向导的第一个屏幕显示了 Rosie 数据分类器推断出的架构,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_14.jpg
wrangle_data()
架构编辑器
上面的架构小部件显示了列名、Rosie 类型
(特定于 Rosie 的高级类型表示)和列类型
(映射到支持的 pandas 类型)。每一行还包含三个操作按钮:
-
删除列:这将从架构中删除列。此列将不会出现在最终的 pandas DataFrame 中。
-
重命名列:这将改变列的名称。
-
转换列:通过将列解构为新列来转换列。
用户随时可以预览数据(如上面的 SampleData 小部件所示),以验证架构配置是否按预期运行。
当用户点击转换列按钮时,显示一个新屏幕,允许用户指定用于构建新列的模式。在某些情况下,数据分类器能够自动检测这些模式,在这种情况下,会添加一个按钮询问用户是否应用这些建议。
下图显示了带有自动化建议的转换选定列屏幕:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_15.jpg
转换列屏幕
此屏幕显示四个小部件,包含以下信息:
-
Rosie 模式输入框是您可以输入自定义 Rosie 模式的位置,用于表示此列的数据。然后,您使用提取变量按钮告诉架构编辑器应该将模式中的哪一部分提取到新列中(更多细节稍后解释)。
-
有一个帮助小部件,提供指向 RPL 文档的链接。
-
显示当前列的数据预览。
-
显示应用了 Rosie 模式的数据预览。
当用户点击提取变量按钮时,部件将更新为如下:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_16.jpg
将 Rosie 变量提取到列中
此时,用户可以选择编辑定义,然后点击创建列按钮,将新列添加到架构中。新列示例小部件随后会更新,显示数据预览。如果模式定义包含错误语法,则此小部件会显示错误:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_17.jpg
应用模式定义后的新列预览
当用户点击提交列按钮时,主架构编辑器界面会再次显示,新增的列会被添加进来,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_18.jpg
带有新列的架构编辑器
最后一步是点击完成按钮,将架构定义应用到原始文件中,并创建一个 pandas 数据框,该数据框将作为变量在笔记本中使用。此时,用户将看到一个对话框,其中包含一个可以编辑的默认变量名,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_19.jpg
编辑结果 Pandas 数据框的变量名称
点击完成按钮后,pixiedust_rosie
会遍历整个数据集,应用架构定义。完成后,它会在当前单元格下方创建一个新单元格,其中包含生成的代码,调用display()
API 来显示新生成的 pandas 数据框,如下所示:
#Code generated by pixiedust_rosie
display(wrangled_df)
运行前面的单元格将让你探索和可视化新数据集。
我们在本节中探讨的wrangle_data()
功能是帮助数据科学家减少数据清理时间、更多时间进行数据分析的第一步。在下一节中,我们将讨论如何帮助数据科学家进行数据探索和可视化。
Display – 一个简单的交互式数据可视化 API
数据可视化是数据科学中另一个非常重要的任务,它在探索和形成假设中是不可或缺的。幸运的是,Python 生态系统拥有许多强大的库,专门用于数据可视化,例如以下这些流行的例子:
-
Matplotlib:
matplotlib.org
-
Seaborn:
seaborn.pydata.org
-
Bokeh:
bokeh.pydata.org
-
Brunel:
brunelvis.org
然而,类似于数据加载和清理,在笔记本中使用这些库可能会很困难且耗时。每个库都有自己独特的编程模型,API 学习和使用起来并不总是容易,特别是如果你不是一个有经验的开发者。另一个问题是,这些库没有提供一个高层次的接口来与常用的数据处理框架(如 pandas(也许 Matplotlib 除外)或 Apache Spark)进行对接,因此,在绘制数据之前,需要进行大量的数据准备工作。
为了帮助解决这个问题,PixieDust 提供了一个简单的display()
API,使得 Jupyter Notebook 用户可以通过交互式图形界面绘制数据,无需编写任何代码。这个 API 并不直接创建图表,而是通过调用渲染器的 API 来处理数据准备工作,根据用户的选择委托给相应的渲染器。
display()
API 支持多种数据结构(如 pandas、Spark 和 JSON)以及多种渲染器(如 Matplotlib、Seaborn、Bokeh 和 Brunel)。
举个例子,让我们使用内置的汽车性能数据集,开始通过调用display()
API 来可视化数据:
import pixiedust
cars = pixiedust.sampleData(1, forcePandas=True) #car performance data
display(cars)
当命令首次在单元格中调用时,系统会显示一个表格视图,随着用户在菜单中的导航,所选的选项会以 JSON 格式存储在单元格的元数据中,确保下次运行该单元格时可以重新使用。所有可视化的输出布局遵循相同的模式:
-
有一个可扩展的顶级菜单,用于在不同的图表之间切换。
-
有一个下载菜单,允许将文件下载到本地计算机。
-
有一个过滤切换按钮,允许用户通过过滤数据来精炼他们的探索。我们将在过滤部分讨论过滤功能。
-
有一个展开/折叠 Pixiedust 输出按钮,用于折叠或展开输出内容。
-
有一个选项按钮,点击后会弹出一个对话框,包含当前可视化的特定配置。
-
有一个分享按钮,可以让你将可视化结果发布到网络上。
注
注:此按钮仅在你部署了 PixieGateway 后可用,详细内容将在第四章,将数据分析发布到网络 - PixieApp 工具中讨论。
-
在可视化的右侧有一组上下文相关的选项。
-
有主可视化区域。
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_20.jpg
表格渲染器的可视化输出布局
要开始创建图表,首先在菜单中选择合适的类型。PixieDust 默认支持六种类型的图表:柱状图、折线图、散点图、饼图、地图和直方图。正如我们在第五章,Python 和 PixieDust 最佳实践及高级概念中看到的,PixieDust 还提供 API,允许你通过添加新的菜单项或为现有菜单添加选项来自定义这些菜单:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_21.jpg
PixieDust 图表菜单
当首次调用图表菜单时,将显示一个选项对话框,用于配置一组基本的配置选项,例如使用X轴和Y轴的内容、聚合类型等。为了节省时间,对话框将预填充 PixieDust 从 DataFrame 自动推测的数据架构。
在以下示例中,我们将创建一个条形图,显示按马力划分的平均油耗:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_22.jpg
条形图对话框选项
点击OK将在单元格输出区域显示交互式界面:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_23.jpg
条形图可视化
画布显示图表在中心区域,并在侧边展示与所选图表类型相关的上下文选项。例如,我们可以在Cluster By下拉框中选择origin字段,按原产国展示细分:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_24.jpg
聚类条形图可视化
如前所述,PixieDust 的display()
实际上并不创建图表,而是根据选定的选项准备数据,并且通过渲染引擎的 API 调用做重载工作,使用正确的参数。这种设计的目标是让每种图表类型支持多种渲染器,无需额外编程,尽可能为用户提供自由的探索空间。
开箱即用,PixieDust 支持以下渲染器,前提是已安装相应的库。对于未安装的库,PixieDust 日志中将生成警告,并且对应的渲染器将不会在菜单中显示。我们将在第五章中详细介绍 PixieDust 日志,Python 和 PixieDust 最佳实践与高级概念。
-
Matplotlib (
matplotlib.org
) -
Seaborn (
seaborn.pydata.org
)注意
该库需要使用以下命令安装:
!pip install seaborn.
-
Bokeh (
bokeh.pydata.org
)注意
该库需要使用以下命令安装:
!pip install bokeh.
-
Brunel (
brunelvis.org
)注意
该库需要使用以下命令安装:
!pip install brunel.
-
Google Map (
developers.google.com/maps
) -
Mapbox (
www.mapbox.com
)注意
注意:Google Map 和 Mapbox 需要 API 密钥,您可以在各自的站点上获取。
你可以使用Renderer下拉框在不同的渲染器之间切换。例如,如果我们想要更多的交互性来探索图表(如缩放和平移),我们可以使用 Bokeh 渲染器,而不是 Matplotlib,后者仅提供静态图像:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_25.jpg
使用 Bokeh 渲染器的簇状条形图
另一个值得一提的图表类型是 Map,当你的数据包含地理空间信息时,它特别有趣,例如经度、纬度或国家/州信息。PixieDust 支持多种类型的地理映射渲染引擎,包括流行的 Mapbox 引擎。
注意
在使用 Mapbox 渲染器之前,建议从 Mapbox 网站获取 API 密钥,网址如下:(www.mapbox.com/help/how-access-tokens-work
)。不过,如果没有密钥,PixieDust 将提供一个默认密钥。
为了创建一个地图图表,下面我们使用东北马萨诸塞百万美元住宅销售数据集:
import pixiedust
homes = pixiedust.sampleData(6, forcePandas=True) #Million dollar home sales in NE Mass
display(homes)
首先,在图表下拉菜单中选择Map,然后在选项对话框中,选择LONGITUDE
和LATITUDE
作为键,并在提供的输入框中输入 Mapbox 访问令牌。你可以在Values区域添加多个字段,它们将作为工具提示显示在地图上:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_26.jpg
Mapbox 图表的选项对话框
当点击OK按钮时,你将获得一个交互式地图,你可以使用样式(简单、分区图或密度图)、颜色和底图(亮色、卫星图、暗色和户外)选项来自定义该地图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_27.jpg
交互式 Mapbox 可视化
每种图表类型都有自己的一套上下文选项,这些选项不难理解,在此我鼓励你尝试每一个选项。如果遇到问题或有改进的想法,你可以随时在 GitHub 上创建一个新问题,网址为github.com/ibm-watson-data-lab/pixiedust/issues
,或者更好的是,提交一个包含代码更改的拉取请求(关于如何做这件事的更多信息可以在这里找到:help.github.com/articles/creating-a-pull-request
)。
为了避免每次单元格运行时重新配置图表,PixieDust 将图表选项存储为 JSON 对象在单元格元数据中,并最终保存到 Notebook 中。你可以通过选择View | Cell Toolbar | Edit Metadata菜单手动检查这些数据,如下所示的截图:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_28.jpg
显示编辑元数据按钮
一个Edit Metadata按钮将显示在单元格顶部,点击该按钮后会显示 PixieDust 的配置:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_29.jpg
编辑单元格元数据对话框
当我们在下一节讨论 PixieApps 时,这个 JSON 配置将变得非常重要。
过滤
为了更好地探索数据,PixieDust 还提供了一个内置的简单图形界面,可以让你快速筛选正在可视化的数据。你可以通过点击顶级菜单中的筛选切换按钮快速调出筛选器。为了简化操作,筛选器只支持基于单一列构建谓词,这在大多数情况下足以验证简单假设(根据反馈,未来可能会增强此功能,支持多个谓词)。筛选器的 UI 会自动让你选择要筛选的列,并根据其类型显示不同的选项:
-
数值类型:用户可以选择一个数学比较符并输入操作数的值。为了方便,UI 还会显示与所选列相关的统计值,这些值可以在选择操作数时使用:https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_30.jpg
对汽车数据集中的 mpg 数值列进行筛选
-
字符串类型:用户可以输入一个表达式来匹配列值,可以是正则表达式或普通字符串。为了方便,UI 还会显示有关如何构建正则表达式的基本帮助:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_31.jpg
对汽车数据集中的 name 字符串类型列进行筛选
点击 应用 按钮时,当前的可视化将更新,以反映筛选器的配置。需要注意的是,筛选器适用于整个单元格,而不仅仅是当前的可视化。因此,在切换图表类型时,筛选器仍然会继续应用。筛选器配置也会保存在单元格元数据中,因此在保存笔记本并重新运行单元格时,筛选器配置会被保留。
例如,以下截图将 cars
数据集可视化为一个柱状图,显示 mpg
大于 23
的行,根据统计框,23 是数据集的均值,并按年份聚集。在选项对话框中,我们选择 mpg
列作为键,origin
作为值:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_32.jpg
筛选后的汽车数据集柱状图
总结一下,在这一节中,我们讨论了 PixieDust 如何帮助解决三项复杂且耗时的数据科学任务:数据加载、数据清洗和数据可视化。接下来,我们将看到 PixieDust 如何帮助增强数据科学家与开发人员之间的协作。
使用 PixieApps 弥合开发人员和数据科学家之间的鸿沟
解决难度较大的数据问题只是数据科学团队任务的一部分。他们还需要确保数据科学结果能正确地投入实际应用,为组织创造商业价值。数据分析的操作化非常依赖于使用案例。例如,这可能意味着创建一个仪表板,用于为决策者整合见解,或者将一个机器学习模型(如推荐引擎)集成到一个网页应用中。
在大多数情况下,这就是数据科学与软件工程相遇的地方(或者有人会说,关键时刻)。团队之间的持续合作——而不是一次性的交接——是任务成功完成的关键。往往他们还需要应对不同的编程语言和平台,导致软件工程团队进行大量的代码重写。
我们在Twitter 标签情感分析项目中亲身体验到了这一点,当时我们需要构建一个实时仪表盘来可视化结果。数据分析部分是用 Python 编写的,使用了 pandas、Apache Spark 和一些绘图库,如 Matplotlib 和 Bokeh,而仪表盘则是用 Node.js(nodejs.org
)和 D3(d3js.org
)编写的。
我们还需要在分析和仪表盘之间构建数据接口,而且由于我们需要系统是实时的,我们选择使用 Apache Kafka 来流式传输格式化的分析结果事件。
以下图示概括了一种我称之为交接模式的方法,在这种模式下,数据科学团队构建分析并将结果部署在数据接口层中。然后,应用程序将使用这些结果。数据层通常由数据工程师处理,这也是我们在第一章中讨论的角色之一,编程与数据科学——一套新工具:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_33.jpg
数据科学与工程之间的交接
这种交接模式的问题在于,它不利于快速迭代。数据层的任何更改都需要与软件工程团队进行同步,以避免破坏应用程序。PixieApps 的理念是,在构建应用程序的同时,尽量保持与数据科学环境的接近,而在我们的案例中,数据科学环境就是 Jupyter Notebook。通过这种方式,分析结果直接从 PixieApp 中调用,PixieApp 嵌入在 Jupyter Notebook 中运行,因此数据科学家和开发人员可以轻松合作并迭代,从而实现快速改进。
PixieApp 定义了一个简单的编程模型,用于构建具有直接访问 IPython Notebook 内核(即运行 Notebook 代码的 Python 后台进程)的单页面应用程序。本质上,PixieApp 是一个 Python 类,封装了展示和业务逻辑。展示部分由一组特殊的方法组成,称为路由,这些方法返回任意的 HTML 片段。每个 PixieApp 都有一个默认路由,返回启动页面的 HTML 片段。开发人员可以使用自定义 HTML 属性来调用其他路由并动态更新页面的全部或部分内容。例如,一个路由可以调用一个从 Notebook 中创建的机器学习算法,或者使用 PixieDust 显示框架生成图表。
以下图示展示了 PixieApps 如何与 Jupyter Notebook 客户端前端和 IPython 内核交互的高层架构:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_34.jpg
PixieApp 与 Jupyter 内核的交互
作为 PixieApp 外观的预览,下面是一个hello world示例应用程序,具有一个按钮,显示我们在前一节中创建的汽车 DataFrame 的条形图:
#import the pixieapp decorators
from pixiedust.display.app import *
#Load the cars dataframe into the Notebook
cars = pixiedust.sampleData(1)
@PixieApp #decorator for making the class a PixieApp
class HelloWorldApp():
#decorator for making a method a
#route (no arguments means default route)
@route()
def main_screen(self):
return """
<button type="submit" pd_options="show_chart=true" pd_target="chart">Show Chart</button>
<!--Placeholder div to display the chart-->
<div id="chart"></div>
"""
@route(show_chart="true")
def chart(self):
#Return a div bound to the cars dataframe
#using the pd_entity attribute
#pd_entity can refer a class variable or
#a global variable scoped to the notebook
return """
<div pd_render_onload pd_entity="cars">
<pd_options>
{
"title": "Average Mileage by Horsepower",
"aggregation": "AVG",
"clusterby": "origin",
"handlerId": "barChart",
"valueFields": "mpg",
"rendererId": "bokeh",
"keyFields": "horsepower"
}
</pd_options>
</div>
"""
#Instantiate the application and run it
app = HelloWorldApp()
app.run()
当上面的代码在 Notebook 单元格中运行时,我们会得到以下结果:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_35.jpg
Hello World PixieApp
你可能对上面的代码有很多疑问,但不用担心。在接下来的章节中,我们将涵盖所有关于 PixieApp 的技术细节,包括如何在端到端的管道中使用它们。
操作化数据科学分析的架构
在前一节中,我们展示了 PixieApps 与 PixieDust 显示框架结合,提供了一种便捷的方法来构建强大的仪表板,直接连接到您的数据分析,允许算法和用户界面之间的快速迭代。这非常适合快速原型设计,但 Notebook 不适合用于生产环境,其中目标用户是业务线用户。一个明显的解决方案是使用传统的三层 Web 应用架构重写 PixieApp,例如,如下所示:
-
用于展示层的 React (
reactjs.org
) -
Web 层的 Node.js
-
一个面向机器学习评分或运行其他分析任务的 Web 分析层的数据访问库
然而,这只会比现有流程提供微小的改进,在这种情况下,现有流程仅包含通过 PixieApp 进行迭代实现的能力。
一个更好的解决方案是直接将 PixieApps 作为 Web 应用进行部署和运行,包括将分析嵌入周围的 Notebook 中,并且在此过程中,无需任何代码更改。
使用这种模型,Jupyter Notebooks 将成为简化开发生命周期的核心工具,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_36.jpg
数据科学流水线开发生命周期
-
数据科学家使用 Python Notebook 来加载、丰富和分析数据,并创建分析(机器学习模型、统计分析等)
-
在同一个 Notebook 中,开发人员创建 PixieApp 来实现这些分析。
-
一旦准备好,开发人员将 PixieApp 发布为 Web 应用,业务用户可以轻松地通过交互方式使用它,而无需访问 Notebooks。
PixieDust 提供了一个实现该解决方案的组件 PixieGateway。PixieGateway 是一个 Web 应用服务器,负责加载和运行 PixieApps。它建立在 Jupyter 内核网关之上(github.com/jupyter/kernel_gateway
),而 Jupyter 内核网关本身是建立在 Tornado Web 框架之上的,因此遵循如下所示的架构:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_37.jpg
PixieGateway 架构图
-
PixieApp 直接从 Notebook 发布到 PixieGateway 服务器,并生成一个 URL。在后台,PixieGateway 为 PixieApp 分配一个 Jupyter 内核来运行。根据配置,PixieApp 可以与其他应用共享内核实例,或者根据需求拥有专用内核。PixieGateway 中间件可以通过管理多个内核实例的生命周期进行横向扩展,这些内核实例可以是本地服务器上的,或者是集群上的远程内核。
注意
注意:远程内核必须是 Jupyter 内核网关。
使用发布向导,用户可以选择性地为应用定义安全性。提供多种选项,包括基本认证、OAuth 2.0 和 Bearer Token。
-
业务用户通过浏览器使用第一步生成的 URL 访问应用。
-
PixieGateway 提供了一个全面的管理控制台,用于管理服务器,包括配置应用程序、配置和监控内核、访问日志进行故障排除等。
-
PixieGateway 为每个活跃用户管理会话,并使用 IPython 消息协议(
jupyter-client.readthedocs.io/en/latest/messaging.html
)通过 WebSocket 或 ZeroMQ 调度请求到相应的内核进行执行,具体取决于内核是本地的还是远程的。
在将分析产品化时,这种解决方案相比经典的三层 Web 应用架构提供了显著的改进,因为它将 Web 层和数据层合并为一个Web 分析层,如下图所示:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_38.jpg
经典三层与 PixieGateway 网络架构的比较
在经典的三层架构中,开发人员必须维护多个 REST 接口,这些接口调用数据层的分析功能,并对数据进行处理,以满足展示层的要求,从而正确显示数据。因此,必须在这些接口中添加大量工程工作,增加了开发和代码维护的成本。相比之下,在 PixieGateway 的两层架构中,开发人员不需要担心创建接口,因为服务器负责通过内置的通用接口将请求分发到适当的内核。换句话说,PixieApp 的 Python 方法会自动成为展示层的接口,而无需任何代码更改。这种模型有利于快速迭代,因为 Python 代码的任何变化都能在重新发布后直接反映到应用程序中。
PixieApps 非常适合快速构建单页面应用和仪表板。然而,你可能还希望生成更简单的一页报告并与用户分享。为此,PixieGateway 还允许你通过 共享 按钮共享由 display()
API 生成的图表,生成一个链接到包含图表的网页的 URL。反过来,用户可以通过复制并粘贴为该页面生成的代码,将图表嵌入到网站或博客文章中。
注释
注:我们将在第四章中详细介绍 PixieGateway,将数据分析发布到 Web - PixieApp 工具,包括如何在本地和云端安装新实例。
为了演示此功能,我们使用之前创建的车辆 DataFrame:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_39.jpg
分享图表对话框
如果共享成功,下一页将显示生成的 URL 和嵌入到网页或博客文章中的代码片段:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_40.jpg
确认共享图表
点击链接将会带你到该页面:
https://github.com/OpenDocCN/freelearn-ds-pt5-zh/raw/master/docs/ds-py/img/B09699_02_41.jpg
将图表显示为网页
总结
在本章中,我们讨论了为什么我们的数据科学工具策略以 Python 和 Jupyter Notebook 为中心。我们还介绍了 PixieDust 的功能,通过以下特点提高用户生产力:
-
数据加载与清洗
-
无需编码即可进行数据可视化和探索
-
一个基于 HTML 和 CSS 的简单编程模型,称为 PixieApp,用于构建与 Notebook 直接交互的工具和仪表板
-
一种点选机制,将图表和 PixieApp 直接发布到网页
在下一章中,我们将深入探讨 PixieApp 编程模型,讨论 API 的各个方面,并附上大量代码示例。
更多推荐
所有评论(0)