数据驱动业务的艺术-全-
数据驱动业务的艺术(全)
原文:
annas-archive.org/md5/42d9640be578d0082a7e1589f07dd9bf译者:飞龙
前言
数据科学最有价值的贡献之一就是帮助企业做出正确的决策。理解这两个截然不同的领域及其与激烈竞争市场的交汇,要求你获取尽可能多的指导。
数据驱动商业的艺术是你获得数据驱动视角的宝贵指南,同时也是利用机器学习(ML)的力量来指导你商业决策的工具书。本书为公司内部多个角色提供了共同的讨论基础。
你将首先学习如何使用 Python 及其众多库进行机器学习(ML)。有经验的数据科学家可能会想跳过这个简短的介绍,但很快你就会进入书籍的核心内容,探索 Python 在机器学习中的多种应用方式,特别是如何通过现实世界中的商业问题,独立解决它们,从而将 ML 应用于商业决策领域。随着进展,你将获得实践性洞察,了解机器学习对你的业务能提供的价值,并掌握应用各种经过验证的机器学习方法的技术能力。
在本书结束时,你将学会如何基于数据驱动的方法制定商业决策,并掌握应用这些知识于现实世界的 Python 技能。
本书的目标读者
本书适用于数据科学家、机器学习工程师与开发者、数据工程师以及商业决策者,他们希望将数据科学应用于业务流程优化,并开发出能够在市场营销、销售、定价、客户成功、广告技术等领域实施数据科学项目所需的技能。其他希望探索数据科学如何改善商业运作的专业人士,以及那些希望通过强有力的商业案例支持技术提案的技术人员,也将从本书中受益。
本书内容
第一章,使用 Python 分析与可视化数据,作为使用 pandas 和 Seaborn 进行数据分析的入门。你将学习如何转换、可视化和分析数据,这些都是本书贯穿始终的基础工具。你将通过基于现实生活应用的实例,接触到这些库的使用。
第二章,在商业运营中使用机器学习,介绍了 scikit-learn,这是使用 Python 应用机器学习算法最受欢迎的框架。你将学习机器学习的基本概念,如何进行训练以及监督式和无监督式算法的推理。这些概念将通过练习得到巩固,并在后续章节中应用到优化各种商业应用的实际案例中。
第三章,通过市场洞察寻找商业机会,聚焦于使用 Python 和搜索趋势分析来从搜索引擎数据中获取有价值的信息。你将学习如何使用 Python 获取搜索引擎趋势信息,构建并可视化结果以验证假设,扩展查询并分析结果内容,使用 NLP 和 scikit-learn 分析结果。
第四章,通过联合分析了解客户偏好,将向你介绍联合分析方法,分析用户偏好调查数据,应用方法确定用户如何权衡每个属性,并预测新组合的排名。
第五章,通过价格需求弹性选择最佳价格,将向你介绍价格弹性概念,并利用该概念通过销售数据找到不同产品的最佳价格。在本章结束时,你将能够找到最大化收入的价格,并理解需求曲线。
第六章,产品推荐,展示了创建产品推荐和执行市场篮子分析的两种方法。你将了解协同过滤和先验算法,并学习如何实现它们,以使用销售数据创建产品推荐。
第七章,预测客户流失,将展示如何使用 Python 和 scikit-learn 预测消费者行为中的微妙变化。
第八章,通过客户细分进行用户分组,将帮助你了解和实践可以应用于建模数据的方法,以及哪些无监督机器学习方法可以用来发现这些群体,并找出它们的关键特征。最后,你将学习如何通过销售分析这些细分群体,如何通过使用 Seaborn 在清晰定义的仪表板中传达这些发现。
第九章,利用历史 Markdown 数据预测销售额,将指导你使用 pandas 和 Seaborn 分析促销活动对历史时间序列销售数据的影响,并使用 scikit-learn 优化库存和存储,以分析促销活动的影响并优化存储成本。
第十章,网站分析优化,将向你展示如何使用 Python 分析数字营销数据,通过分析数字广告活动的结果,基于客户生命周期价值预测计算投资回报,并优化程序化广告平台上的投资。
第十一章,在商业中创建数据驱动的文化,与商业领袖沟通,了解他们如何应用数据科学和分析来改进业务运营。我们将与多位首席数据官和首席数据科学家进行交流,收集他们在多个公司中应用这些方法的具体实例。
为了充分利用本书
| 本书涵盖的软件/硬件 | 操作系统要求 |
|---|---|
| Python 3.x | Windows、macOS 或 Linux |
如果您使用的是本书的数字版,我们建议您亲自输入代码,或从本书的 GitHub 仓库中获取代码(相关链接在下一节提供)。这样做可以帮助您避免因复制粘贴代码而导致的潜在错误。
下载示例代码文件
您可以从 GitHub 上下载本书的示例代码文件,网址为 github.com/PacktPublishing/The-Art-of-Data-Driven-Business-Decisions。如果代码有更新,将会在 GitHub 仓库中进行更新。
我们还提供了其他代码包,您可以在我们的书籍和视频丰富目录中找到,网址为 github.com/PacktPublishing/。快去看看吧!
使用的约定
本书中使用了一些文本约定。
文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。例如:“将下载的 WebStorm-10*.dmg 磁盘映像文件挂载为系统中的另一个磁盘。”
代码块的格式如下:
results_df = pd.DataFrame(results).dropna()
results_df.columns = ['client','slope','std']
results_df.index = results_df.client
results_df = results_df.drop(['client'],axis=1)
results_df.head()
当我们希望引起您对代码块中特定部分的注意时,相关的行或项会以粗体显示:
df.columns.tolist()
>>> ['period', 'sub_market', 'client_class', 'division', 'brand','cat', 'product', 'client_code', 'client_name', 'kgs_sold']
任何命令行输入或输出将如下所示:
!pip install --upgrade openpyxl scikit-surprise
粗体:表示新术语、重要单词或您在屏幕上看到的词汇。例如,菜单或对话框中的单词通常以粗体显示。举个例子:“从管理面板中选择系统信息。”
提示或重要说明
如下所示。
联系我们
我们非常欢迎读者的反馈。
一般反馈:如果您对本书的任何内容有疑问,请通过电子邮件联系我们,邮箱地址为 customercare@packtpub.com,并在邮件主题中注明书名。
勘误:尽管我们已经尽力确保内容的准确性,但仍难免会出现错误。如果您发现本书中的错误,我们非常感谢您报告给我们。请访问 www.packtpub.com/support/errata 并填写表单。
盗版: 如果您在互联网上发现我们作品的任何形式的非法复制,请告知我们其位置或网站名称。请通过 copyright@packt.com
如果您有兴趣成为作者:如果您在某一主题上有专业知识,并且有意撰写或为书籍做出贡献,请访问 authors.packtpub.com。
分享您的想法
一旦您阅读了《数据驱动业务的艺术》,我们很乐意听取您的想法!请点击此处直接转到亚马逊评论页面 并分享您的反馈。
您的评价对我们和技术社区至关重要,将帮助我们确保我们提供的内容质量卓越。
下载本书的免费 PDF 版本
感谢您购买本书!
您喜欢随时随地阅读,但不能随身携带印刷书籍吗?
您购买的电子书是否与您选择的设备不兼容?
别担心,现在您每购买一本 Packt 书籍,都可以免费获得该书的无 DRM PDF 版本。
可在任何设备上的任何地方阅读。直接从您喜爱的技术书籍中搜索、复制和粘贴代码到您的应用程序中。
福利不止如此,您还可以独享折扣、新闻通讯和每日优质免费内容
按照以下简单步骤获取这些好处:
- 扫描下方的 QR 码或访问以下链接

packt.link/free-ebook/9781804611036
-
提交您的购书证明
-
就这样!我们将直接将您的免费 PDF 和其他好处发送至您的电子邮件
第一部分:使用 Python 进行数据分析与预测
第一部分将介绍主要工具,使用 Python 分析、预测和可视化数据。核心框架如 Pandas、Scikit-learn 和 Seaborn 将被介绍,确保您学会如何正确探索和操作数据,了解机器学习算法的基础以预测和聚类数据,并进行有效的数据可视化来讲述数据故事。
本部分涵盖以下章节:
-
第一章,使用 Python 分析和可视化数据
-
第二章,在业务运营中使用机器学习
第一章:使用 Python 进行数据分析和可视化
高级分析和数据科学如今在大多数企业中发挥着重要作用。它帮助组织跟踪、管理和收集绩效指标,从而改善组织决策。企业经理可以利用创新的分析和机器学习技术帮助他们决定如何最佳地与客户互动、提升企业绩效并增加销售额。数据科学和分析可以用于创建以用户为中心的产品,并做出明智的决策。通过比较不同的产品特性、研究消费者反馈和市场趋势,企业能够开发能够吸引客户并保持长期吸引力的商品和服务。
本书面向所有希望了解数据科学、高级分析和机器学习技术的方法的人,特别是针对那些研究由这些方法影响的商业案例的人。这些案例大多基于真实的应用案例,并展示了这些方法在不同行业的公司中产生的积极影响。因此,无论是那些希望通过数据科学提升技能的资深商业分析师,还是希望找到可以应用于最大化特定操作的管理者,都会从本书中讨论的示例中受益。
在本章中,我们将介绍本书中将用来管理数据、操作数据和可视化数据的初步组件。具体来说,我们将讨论以下内容:
-
数据科学在商业中的应用及其与业务分析师或数据分析师等角色的主要区别
-
使用诸如 NumPy 等统计编程库来应用矩阵代数和统计方法
-
使用 pandas 存储数据,pandas 是一个广泛用于数据科学领域的数据分析和处理库
-
使用 Seaborn 进行可视化,并了解不同类型的图表如何在不同场景下使用
接下来,我们将讨论您需要满足的技术要求,以便能够跟随本章中的示例。
技术要求
要能够跟随本章的步骤,您需要满足以下要求:
-
一个运行 Python 3.7 及以上版本的 Jupyter notebook 实例。如果您有 Google Drive 帐户,也可以使用 Google Colab notebook 来运行这些步骤。
-
基本的数学和统计学概念理解。
在商业中使用数据科学和高级分析
大多数时候,关于数据科学家与商业分析师之间的区别会引发讨论,因为这两个角色都专注于从数据中获取洞察。从某种角度看,数据科学可以被视为通过分析原始数据背后的模式来进行预测。商业智能是回顾性的,发现过去和当前的趋势,而数据科学则是前瞻性的,预测未来的趋势。
商业决策在很大程度上依赖于数据科学和先进的分析方法,因为这些方法帮助管理者理解决策如何影响结果。因此,数据科学家越来越需要将常见的机器学习技术与对潜在因果关系的了解结合起来。这些发展催生了“决策科学家”这一职位,这是一位专注于利用技术支持业务和决策的技术专家。然而,与“数据科学家”或“大数据科学家”这一职位相比,“决策科学家”这一职位的含义则更加深刻。
很多时候,商业分析师、数据科学家和数据分析师的角色容易混淆。商业分析师更可能解决业务问题并提出解决方案,而数据分析师通常更多地直接与数据本身打交道。虽然这两个职位都需求量大且通常薪酬丰厚,但数据科学更注重预测,因为它分析的是原始数据中隐藏的模式。
使用 NumPy 进行统计学和代数运算
NumPy 是一个用于处理数组的 Python 库。此外,它还提供了处理矩阵、傅里叶变换和线性代数领域的函数。NumPy 现在支持大规模、多维数组和矩阵,并且提供了一系列复杂的数学操作,可以在这些数组上执行。这些操作使用大量复杂的数学函数来处理庞大的多维数组和矩阵,并进行机器学习中的基本科学计算,因此非常有用。它提供了 n 维数组,这是一个简单而有效的数据结构。学习 NumPy 是每个 Python 数据科学家入门的第一步,因为它构成了几乎所有工具包功能的基础。
数组是一个由所有相同类型的值组成的网格,通过一个非负整数元组来索引,这是 NumPy 使用的基本构建块。类似于代数中矩阵的维度定义,数组的秩由其维度的数量决定。一个数字元组,表示数组在每个维度上的大小,构成了数组的形状:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr)
print(type(arr))
NumPy 数组是一个容器,可以容纳一定数量的元素,这些元素必须是相同类型的,正如之前所指定的那样。大多数数据结构都使用数组来执行其算法。就像你可以切片一个列表一样,你也可以切片一个 NumPy 数组,但它支持多维切片。与索引类似,切片 NumPy 数组会返回一个原始数组的视图。
在 Python 中,切片意味着从一个给定的索引到另一个给定的索引选择元素。我们可以通过使用 [start:end] 对数组进行切片,选择数组中的某些元素,其中 start 表示开始的元素位置,end 表示结束的位置。我们还可以使用 [start:end:step] 来定义步长:
print('select elements by index:',arr[0])
print('slice elements of the array:',arr[1:5])
print('ending point of the array:',arr[4:])
print('ending point of the array:',arr[:4])
有三种不同的索引技术:字段访问、基本切片和高级索引。基本切片是 Python 基本切片概念的 n 维扩展。通过传递 start、stop 和 step 参数给内建的 slice 函数,可以创建一个 Python 切片对象。切片使得编写清晰、简洁的代码成为可能。当一个可迭代元素按位置被“索引”时,它指的是该元素在可迭代对象中的位置。根据元素的索引,从一个可迭代对象中获取元素子集被称为“切片”。
要合并(拼接)两个数组,我们必须通过使用 np.concatenate() 函数将两个数组中的每个元素复制到 result 中:
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.concatenate((arr1, arr2))
print(arr)
数组也可以使用 NumPy 的堆叠方法进行连接。我们可以沿第二个轴将两个 1D 数组合并,堆叠在一起,这个过程称为堆叠。stack() 方法接收一个我们希望连接的数组列表,并指定轴:
arr = np.stack((arr1, arr2), axis=1)
print(arr)
axis 参数可以用来引用我们想要进行拼接的轴:
arr = np.stack((arr1, arr2), axis=0)
print(arr)
NumPy 的 mean() 函数用于计算指定轴上的算术平均值:
np.mean(arr,axis=1)
你需要使用 NumPy 的 mean() 函数并设置 axis=0 来计算按列的平均值。要按行计算平均值,应该使用 axis=1:
np.mean(arr,axis=0)
在接下来的章节中,我们将介绍 pandas,一个用于数据分析和操作的库。pandas 就像 NumPy 一样,是数据科学中最广泛使用的 Python 库之一。它提供了高性能、易于使用的数据分析工具。与 NumPy 提供的多维数组对象不同,pandas 提供了一种称为 DataFrame 的内存中的二维表对象。
使用 pandas 存储和操作数据
pandas 是一个开源工具包,建立在 NumPy 之上,为 Python 程序员提供高性能、易用的数据结构和数据分析能力。它使得快速分析、数据准备和清洗成为可能。它在性能和产出上都表现卓越。
pandas 是一个数据分析的包,因其包含许多内置的辅助功能,通常用于金融时间序列数据、经济数据以及任何形式的表格数据。对于科学计算,NumPy 是管理大型多维数组的快速方法,且可与 SciPy 和 pandas 包一起使用。
通过将字典传递给DataFrame构造函数,可以从字典构建一个 DataFrame:
import pandas as pd
d = {'col1': [1,5,8, 2], 'col2': [3,3,7, 4]}
df = pd.DataFrame(data=d)
df
pandas 的groupby函数是一个强大而多功能的函数,允许我们将数据拆分成单独的组,以便进行计算和更好的分析:
df = pd.DataFrame({'Animal': ['Dog', 'Dog',
'Rat', 'Rat','Rat'],
'Max Speed': [380., 370., 24., 26.,25.],
'Max Weight': [10., 8.1, .1, .12,.09]})
df
“拆分”、“应用”和“合并”这三步让我们最容易记住“groupby”操作的功能。拆分是指根据特定的列将数据分成不同的组。举例来说,我们可以将销售数据按月分组:
df.groupby(['Animal']).mean()
pandas 的groupby技术非常强大。通过值计数,你可以根据某一列进行分组,并按该列的值对另一列进行计数。我们可以使用groupby和value_counts来统计每个人完成的活动次数:
df.value_counts()
我们还可以使用aggregate()方法对行进行聚合,这允许你对 DataFrame 的某一轴应用一个或多个函数名称。默认值是 0,即索引(行)轴。需要注意的是,agg()方法是aggregate()方法的别名:
df.agg("mean", axis="rows",numeric_only=True)
我们还可以传递多个函数用于每一列的选择:
df.agg({'Max Speed' : ['sum', 'min'], 'Max Weight' : ['mean', 'max']})
在给定轴上的分位数是通过quantile()方法确定的。默认情况下是按行轴来计算。当指定列轴(axis='columns')时,quantile()方法会按列计算分位数,并返回每行的均值。以下代码将为我们提供整个 DataFrame 的 10%分位数:
df.quantile(.1)
我们还可以传递一个分位数组:
df.quantile([.1, .5])
pivot()函数用于根据提供的索引或列值重塑给定的 DataFrame,并且是我们可以用来改变数据的不同类型函数之一。此函数不支持数据聚合;多个值会在列中产生MultiIndex:
df = pd.DataFrame(
{'type': ['one', 'one', 'one', 'two', 'two', 'two'],
'cat': ['A', 'B', 'C', 'A', 'B', 'C'],
'val': [1, 2, 3, 4, 5, 6],
'letter': ['x', 'y', 'z', 'q', 'w', 't']})
df.pivot(index='type', columns='cat', values='val')
数据透视表是 pandas 最强大的功能之一。数据透视表让我们从数据中提取洞见。pandas 提供了一个类似的功能,叫做pivot_table()。它是一个简单的函数,但能非常快速地产生强大的分析结果。
接下来的步骤是学习如何可视化数据,以便进行恰当的故事讲述和解释。
使用 Seaborn 可视化模式
Seaborn 是一个基于 Matplotlib 的 Python 数据可视化库。它提供了一个精致的绘图工具,用于创建引人注目且具有教育意义的统计图形。
Seaborn 和 Matplotlib 的主要区别在于 Seaborn 如何处理 pandas DataFrame。使用简单的函数集,Python 可以提供美丽的图形。当处理 DataFrame 和数组时,Matplotlib 表现出色,它将坐标轴和图形视为对象,并提供了几种有状态的绘图 API。
在这里,我们将开始使用“tips”数据集进行示例,该数据集包含数字和分类变量的混合:
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset("tips")
f, ax = plt.subplots(figsize=(12, 6))
sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time")
在前面的代码片段中,我们导入了 Seaborn 和 Matplotlib;后者允许用户控制图表创建的某些方面,例如图形大小,我们将其定义为 12x6 英寸。这创建了 Seaborn 将放置可视化内容的布局。
我们使用scatterplot()函数来创建一个点的可视化,其中X轴表示total_bill变量,Y轴表示tip变量。在这里,我们使用hue参数根据time分类变量为不同的点上色,从而允许我们用分类维度绘制数值数据:

图 1.1:Seaborn 散点图,颜色取决于分类变量
生成的图形显示了根据我们指定的颜色代码(在我们这种情况下是收到的小费、它们与总账单金额的关系以及是否为午餐或晚餐时段)分布的数据。
我们可以得出的解释是,总账单金额和收到的小费之间可能存在线性关系。但如果我们仔细观察,会发现最高的账单总额出现在晚餐时段,同时也导致了最高的小费值。
在商业领域,这些信息可能非常有用,但首先需要通过适当的假设检验方法进行验证,可以使用 t 检验来验证这些假设,再加上线性回归分析来得出总金额与小费分布之间存在关系的结论,同时考虑到发生这一过程的时间差异。我们将在下一章中探讨这些分析。
我们现在可以看到,简单的探索图如何帮助我们构建假设,并在此基础上做出决策,从而更好地改进商业产品或服务。
我们还可以将色调和样式分配给不同的变量,这样就能独立地改变颜色和标记。这使我们可以在同一个图表中引入另一个分类维度,在 Seaborn 中可以使用style参数,根据我们参考的分类变量分配不同类型的标记:
f, ax = plt.subplots(figsize=(12, 6))
sns.scatterplot(data=tips, x="total_bill", y="tip", hue="day", style="time")
前面的代码片段将创建一个 12x6 英寸的布局,并添加关于time分类变量的信息,如下图所示:

图 1.2:Seaborn 散点图,颜色和形状依赖于分类变量
这种类型的图表允许我们将大量信息集成到单个图中,这可能是有益的,但也可能导致信息过载,一次难以消化。重要的是始终考虑我们想要展示的信息的理解,使利益相关者能够一目了然地看到关系。
在这里,一开始很难看出任何对一周中哪些天进行解释的信息。这是因为已经显示了大量信息。通过其他类型的分析,如统计测试、相关性和因果关系,可以获得不能仅通过观察图表获得的这些差异。
另一种通过 Seaborn 创建的图形添加更多维度的方式是将数值变量表示为散点图中点的大小。可以将数值变量分配给 size,以对点的面积应用语义映射。
我们可以通过大小控制标记区域的范围,并将 legend 参数设置为 full,以强制每个唯一值都出现在图例中:
f, ax = plt.subplots(figsize=(12, 6))
sns.scatterplot(
data=tips, x="total_bill", y="tip", hue="size",
size="size", sizes=(20, 200), legend="full"
)
上述代码片段创建了一个散点图,其中点的大小和颜色取决于 size 变量。这对于在这类图中再添加另一个数值维度非常有用:

图 1.3:Seaborn 散点图,大小依赖于第三变量
表示数据的另一重要方式是查看时间序列信息。我们可以使用 Seaborn 包显示时间序列数据,无需对数据进行任何特殊处理。
在下面的示例中,我们正在创建一个带有日期的 pandas DataFrame,使用 Matplotlib 创建一个 15 x 8 英寸的图形,然后使用 Seaborn 的 lineplot 函数显示信息:
df = pd.DataFrame({"Dates":
['01/01/2019','01/02/2019','01/03/2019','01/04/2019',
'01/05/2019','01/06/2019','01/07/2019','01/08/2019'],
"Count": [727,716,668,710,718,732,694,755]})
plt.figure(figsize = (15,8))
sns.lineplot(x = 'Dates', y = 'Count',data = df)
上述示例创建了一个将日期放在 x 轴上,count 变量放在 y 轴上的精彩图表:

图 1.4:Seaborn 时间轴线图
在下面的示例中,我们将加载一个称为 FMRI 数据集的预定义数据集,其中包含时间序列数据。
首先,我们将加载一个包含长格式数据的示例数据集,然后绘制不同事件和地区的响应。为此,我们将创建一个 15 x 8 英寸的 Matplotlib 图形,并使用 lineplot 函数显示信息,使用 hue 参数显示关于地区的分类信息,并使用 style 参数显示关于事件类型的分类信息:
fmri = sns.load_dataset("fmri")
f, ax = plt.subplots(figsize=(15, 8))
sns.lineplot(x="timepoint", y="signal", hue="region", style="event",data=fmri)
上述代码片段创建了一个展示信息的图表,允许我们研究变量随时间变化的情况,依据数据的不同类别特征:

图 1.5:Seaborn 线图与置信区间
Seaborn lineplot 函数的一个特点是它展示了在 95% 置信区间内所有数据点的置信区间;实线表示均值。这种方式在展示包含多个数据点的时间序列数据时非常有用。趋势可以通过均值来可视化,同时也能让我们感知数据的分散程度,这在分析行为模式时是非常重要的。
我们可以通过条形图来可视化数据。Seaborn 使用 barplot 函数来创建条形图:
f, ax = plt.subplots(figsize=(12, 6))
ax = sns.barplot(x="day", y="total_bill", data=tips,ci=.9)
上述代码使用 Matplotlib 创建了一个 12 x 6 英寸的图形,其中生成了 Seaborn 条形图。在这里,我们将在 x 轴上显示日期,在 y 轴上显示总账单,显示置信区间作为触须出现在条形上方。上面的代码生成了以下图表:

图 1.6:Seaborn 条形图
在上述图表中,我们无法详细看到触须,因为数据的分散程度非常小。我们可以通过绘制一组垂直条形图并按两个变量进行分组,来更好地查看这一点:
f, ax = plt.subplots(figsize=(12, 6))
ax = sns.barplot(x="day", y="total_bill", hue="sex", data=tips)
上述代码片段在 12 x 6 英寸的 Matplotlib 图形上创建了一个条形图。不同之处在于我们使用 hue 参数来显示性别差异:

图 1.7:Seaborn 条形图与类别数据
从这张图中可以得出的一个结论是,女性的总账单通常低于男性,只有星期六是均值存在差异的那一天,尽管在分散程度上有一个更低的基准点。
我们可以通过使用 catplot 向可视化中添加另一个类别维度,将 barplot 与 FacetGrid 结合来创建多个图表。这允许我们在额外的类别变量中进行分组。使用 catplot 比使用 FacetGrid 创建多个图表更安全,因为它确保了不同小面板之间变量顺序的同步:
sns.catplot(x="sex", y="total_bill",hue="smoker", col="time",data=tips, kind="bar",height=6, aspect=.7)
上述代码片段生成了一个包含不同条形图的类别图。注意,图表的大小是通过 height 和 aspect 变量来控制的,而不是通过 Matplotlib 图形:

图 1.8:Seaborn 条形图与两个类别变量
在这里,我们可以看到在午餐时间,男性吸烟者的平均值低于非吸烟者,而女性吸烟者的平均值高于非吸烟者。这种趋势在晚餐时反转,晚餐时男性吸烟者的平均值高于女性吸烟者。
在分析模式时使用直方图分析趋势是一种很好的工具。我们可以使用 Seaborn 的hisplot函数。在这里,我们将使用penguins数据集,并创建一个 Matplotlib 图形,大小为 12 x 6 英寸:
penguins = sns.load_dataset("penguins")
f, ax = plt.subplots(figsize=(12, 6))
sns.histplot(data=penguins, x="flipper_length_mm", bins=30)
前面的代码创建了一个分组数据中翻转长度的直方图,分为 30 个箱子:

图 1.9: Seaborn 直方图绘图
在这里,我们可以添加一个核密度线估计,软化直方图,提供关于数据分布形状的更多信息。
以下代码添加了kde参数,设为True以显示此行:
f, ax = plt.subplots(figsize=(12, 6))
sns.histplot(data=penguins, x="flipper_length_mm", kde=True)

图 1.10: 使用 KDE 估计密度的 Seaborn 直方图绘图
在这里,我们可以看到数据接近一些叠加的标准分布,这可能意味着我们正在观察不同类型的数据。
我们还可以通过在分类变量species上使用hue参数向图形添加更多维度:
f, ax = plt.subplots(figsize=(12, 6))
sns.histplot(data=penguins, x="flipper_length_mm", hue="species")

图 1.11: 使用分类数据的 Seaborn 直方图绘图
正如我们所怀疑的那样,我们正在观察企鹅的不同物种的叠加,每种物种都具有正态分布,尽管有些比其他物种更倾斜。
pairplot函数可用于绘制数据集中的多个成对的双变量分布。对角线图是单变量图,显示了 DataFrame 中变量(n, 2)组合的关系作为一组图。pairplot用于确定最显著的集群或最佳的特征组合以解释两个变量之间的关系。在我们的数据集中构建线性分离或一些简单的线条也有助于创建一些基本的分类模型:
sns.pairplot(penguins,height=3)
前面的代码创建了一个数据的pairplot,其中每个框的高度为 3 英寸:

图 1.12: 变量关系和选择特征的直方图
变量名称显示在矩阵的外部边界上,使其易于理解。每个变量的密度图显示在对角线上的框中。每个变量之间的散点图显示在左下角的框中。
我们还可以使用hue参数将分类维度添加到可视化中:
sns.pairplot(penguins, hue="species", diag_kind="hist",height=3)

图 1.13: 带分类标签的变量关系和直方图
尽管非常有用,但这个图表可能计算开销很大,这个问题可以通过仅查看部分变量而非整个数据集来解决。
我们可以通过减少显示的图形数量来缩短渲染可视化所需的时间。我们可以通过指定每个轴上要显示的变量类型来实现这一点,如下代码块所示:
sns.pairplot(
penguins,
x_vars=["bill_length_mm", "bill_depth_mm",
"flipper_length_mm"],
y_vars=["bill_length_mm", "bill_depth_mm"],
height=3
)

图 1.14: 选定特征的变量关系和直方图
箱型图,有时也被称为描述性统计中的箱线图,是一种在解释性数据分析中常用的图表类型。箱型图使用数据的四分位数(或百分位数)和均值,直观地描述数值数据的分布情况及偏斜度。
我们可以通过 Seaborn 的boxplot函数来使用这些功能,如下所示:
f, ax = plt.subplots(figsize=(12, 6))
ax = sns.boxplot(x="day", y="total_bill", data=tips)

图 1.15: Seaborn 箱型图
Seaborn 箱型图结构非常简单。分布情况通过箱型图可视化。当你想比较两个组之间的数据时,它们非常有用。箱型图也可以称为箱线图。每个箱子显示数据集的四分位数,而须线则延伸以展示其余的分布。
在这里,我们可以使用hue参数来指定我们想要展示的某种类别变量类型,同时指定我们想使用的 Seaborn 默认颜色调色板:
f, ax = plt.subplots(figsize=(12, 6))
ax = sns.boxplot(x="day", y="total_bill", hue="smoker",data=tips, palette="Set3")

图 1.16: 带有分类数据的 Seaborn 箱型图
总是会有一个问题:何时使用箱型图。箱型图用于显示数值数据的分布,尤其是在跨多个组进行比较时。它们旨在通过一瞥提供高层次信息,并提供诸如对称性、偏斜度、方差和异常值等数据集的详细信息。
总结
在本章中,我们介绍了如何使用 pandas 和 NumPy 存储和操作数据,以及如何使用 Seaborn 可视化数据模式的初步概念。这些元素不仅用于探索数据,还用于创建可视化叙事,让我们能够理解数据中的模式,并以简单而实用的方式进行沟通。
在下一章中,我们将基于此了解如何利用机器学习和描述性统计来验证假设、研究相关性与因果关系,并且构建预测模型。
第二章:在业务操作中使用机器学习
机器学习是一个研究领域,专注于理解和开发“学习”过程,或使用数据来提高在特定任务集上的表现。它被认为是人工智能的一个组成部分。其中,机器学习是一项技术,使企业能够高效地从非结构化数据中提取知识。在几乎不需要编程的情况下,机器学习——更准确地说,机器学习算法——可以用来从给定的数据集进行迭代学习,理解模式、行为等。
在本章中,我们将学习如何做到以下几点:
-
使用统计分析验证观察到的效果差异
-
分析变量之间的相关性和因果关系,并建立模型
-
为聚类和机器学习模型准备数据
-
开发用于回归和分类的机器学习模型
技术要求
为了能够跟随本章的步骤,你需要满足以下要求:
-
运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果你有 Google Drive 账户,也可以使用 Google Colab Notebook 运行这些步骤。
-
具备基本的数学和统计概念知识。
-
下载书中 GitHub 页面提供的示例数据集,原始来源是
python.cogsci.nl/numerical/statistics/。
使用 t 检验验证变化的效果
当衡量某些操作对特定用户群体的影响时,我们需要验证这些操作是否已经显著影响了目标群体。为了做到这一点,我们可以使用 t 检验。
t 检验是一种统计检验,用于比较两组的均值,以确定某种方法或治疗是否对感兴趣的人群产生影响,或者两个组是否存在差异;它通常用于假设检验。
当两个组中的数据集不涉及相同的值时,独立地选择不同的 t 检验样本。这些样本可能包括两组随机选择的、不相关的患者群体,用来研究药物的效果。例如,其中一组接受处方治疗,另一组则作为对照组,接受安慰剂。这会产生两个独立的、无关的样本集。简单来说,t 检验用于比较两组的均值。它通常用于假设检验,以确定一个方法或治疗是否真正影响了感兴趣的人群,或者两个组之间是否存在差异。
t 检验在商业背景下用于比较两个不同的均值,并确定它们是否代表相同的总体,尤其在验证促销活动对销售提升的影响时非常有用。此外,它还使企业能够理解其结果是否为偶然现象的概率。
我们将学习如何使用 SciPy 包和 Matzke 等人(2015)数据集进行独立样本 t 检验。该数据集中的参与者经历了一项记忆挑战,需回忆出一组单词。在保持间隔期间,一组参与者专注于显示屏上的中央固定点,而另一组志愿者则不断地水平移动眼睛,有些人认为这有助于记忆。
为了判断横向眼动组的记忆表现(CriticalRecall)是否优于注视组,我们可以使用 SciPy 库中的 ttest_ind 函数:
from scipy.stats import ttest_ind
import pandas as pd
dm = pd.read_csv('matzke_et_al.csv')
dm_horizontal = dm[dm.Condition=='Horizontal']
dm_fixation = dm[dm.Condition=='Fixation']
t, p = ttest_ind(dm_horizontal.CriticalRecall, dm_fixation.CriticalRecall)
print('t = {:.3f}, p = {:.3f}'.format(t, p))

图 2.1:T 检验结果
t 检验的 p 值可以在输出结果中找到,它是你评估 t 检验结果所需的全部信息。只需将输出的 p 值与所选的 alpha 水平进行比较,即可在所需的 alpha(显著性)水平上进行假设检验:
import seaborn as sns
import matplotlib.pyplot as plt # visualization
sns.barplot(x='Condition', y='CriticalRecall', data=dm)
plt.xlabel('Condition')
plt.ylabel('Memory performance')
plt.show()
如果 p 值小于设定的显著性阈值(例如 0.05),你可以拒绝原假设。两个均值之间的差异在统计上具有显著性。你的样本数据足够有说服力,支持两个总体均值不相等的结论:

图 2.2:总体分布
高 t 值(也称为 t 值)表示组间差异明显,而低 t 值则表示组间相似。自由度,或研究中可能变化的值,对于确定原假设的显著性和真实性至关重要。
在我们的例子中,结果表明存在显著差异(p = 0.0066)。然而,固定注视组表现优于其他组,且其效果与预期方向相反。
另一种测试两个总体差异的方法是配对样本 t 检验,它比较同一组的两个变量均值。为确定平均值是否偏离 0,该过程计算每次出现时两个变量之间的差异。独立或不相关组的均值使用非配对 t 检验进行比较。非配对 t 检验假设组间的方差相等,而配对 t 检验则不要求方差相等。该过程还自动计算 t 检验的效应量。配对 t 检验用于数据为匹配对的情况,而两样本 t 检验用于两个样本在统计上独立的数据。
让我们使用 Moore、McCabe 和 Craig 的数据集。在这里,评估了痴呆症患者在满月和其他月相期间的攻击行为。这是一个被试内设计,因为在两个时间点对每位参与者进行了测量。
你可以使用 ttest_rel SciPy 函数来检验攻击性在满月和其他月相之间是否有所不同:
from scipy.stats import ttest_rel
dm = pd.read_csv('moon-aggression.csv')
t, p = ttest_rel(dm.Moon, dm.Other)
print('t = {:.3f}, p = {:.3f}'.format(t, p))
如下图所示,有一个有趣且显著的效应,因为 p 值从不为 0,正如输出所示。这一效应表明,人们确实在满月期间最为暴力:

图 2.3:攻击性数据集的 T 检验结果
另一种比较两个组之间差异的方法是统计方法方差分析(ANOVA),该方法用于检查不同均值之间的差异。罗纳德·费舍尔于 1918 年创立了这一统计检验方法,并一直沿用至今。简而言之,ANOVA 分析可以确定三个或更多独立组的均值是否在统计上存在差异。那么,ANOVA 会取代 t 检验吗?其实不会。ANOVA 用于比较三个或更多组之间的均值,而 t 检验则用于比较两个组之间的均值。
在商业环境中,ANOVA 可以用于预算管理,例如,将预算与成本进行比较,以管理收入和库存。ANOVA 还可以用于通过比较预算与成本来管理收入和库存。例如,为了更好地了解未来的销售表现,ANOVA 还可以用于通过检查数据模式来预测趋势。在评估市场研究中经常使用的多项量表时,ANOVA 尤其有用。使用 ANOVA 可能会帮助你作为市场研究员理解不同群体的反应。你可以通过接受零假设开始测试,即观察到的所有组的均值是相等的。
在下一个示例中,让我们重新审视 Moore、McCabe 和 Craig 提供的心率信息。性别和组别是该数据集中两个与主题相关的因素,还有一个因变量(心率)。你需要以下代码来检查性别、组别或它们的交互作用是否对心率产生影响。
我们将使用 anova_lm 的组合,这并不是非常优雅,但关键部分是公式:
from statsmodels.stats.anova import anova_lm
dm = pd.read_csv('heartrate.csv')
dm = dm.rename({'Heart Rate':'HeartRate'},axis=1) # statsmodels doesn't like spaces
df = anova_lm(ols('HeartRate ~ Gender * Group', data=dm).fit())
print(df)
结果显示心率与所有因素相关:性别(F = 185.980,p < .001)、组别(F = 695.647,p < .001)以及性别与组别的交互作用(F = 7.409,p = .006)。

图 2.4:ANOVA 检验结果
现在我们已经验证了多个组之间确实存在差异,接下来可以开始对这些关系进行建模。
使用多元线性回归建模关系
被称为多元线性回归的统计方法利用两个或更多的自变量来预测因变量的结果。通过这种方法,分析师可以计算模型的方差以及每个自变量对整体方差的相对贡献。涉及多个解释变量的回归,无论是线性还是非线性的,都属于多元回归范畴。
多元回归分析的目的是让研究人员能够评估结果(因变量)与多个预测变量之间关系的强度,以及每个预测变量在关系中的重要性,通常是通过消除其他预测变量的统计效应来实现。
多元回归包含多个自变量,而线性回归只考虑一个自变量来影响关系的斜率。
企业可以使用线性回归分析趋势并生成估算或预测。例如,如果一家公司的销售额在过去几年中每月逐渐上升,该公司可以通过对销售数据和每月销售额进行线性分析,预测未来几个月的销售额。
让我们使用 Moore、McCabe 和 Craig 的数据集,其中包含高中生的平均绩点(GPA)和数学及语言知识的 SAT 成绩。我们可以使用以下代码来测试satm和satv是否与gpa(独立)相关。
我们将使用OLS的 SciPy 函数来评估这个关系,该关系作为相关变量的组合传递,然后拟合到数据上:
from statsmodels.formula.api import ols
dm = pd.read_csv('gpa.csv')
model = ols('gpa ~ satm + satv', data=dm).fit()
print(model.summary())

图 2.5:OLS 结果
结果显示,仅数学科目的 SAT 成绩与平均绩点(GPA)有独特的关系,而语言科目的 SAT 成绩则没有。
在下一节中,我们将探讨相关性的概念,即当变量以相似的方式变化时,称为相关;而因果关系则是指一个变量影响另一个变量。
建立相关性和因果关系
被称为相关性的统计量表示了两个变量之间线性关系的紧密程度,可以通过图形方式理解为两个曲线重叠的程度。这是一种描述简单关系的典型方法,而不明确指出因果关系。
相关矩阵显示了相关值,用于量化每一对变量之间的线性关系的紧密程度。相关系数的范围是-1 到+1。如果两个变量有相似的上升和下降趋势,相关值为正。
统计中通常测量的四种相关性类型是斯皮尔曼相关系数、皮尔逊相关系数、肯达尔秩相关系数和点二列相关系数。
为了使组织能够根据预测事件结果做出数据驱动的决策,通常使用相关性和回归分析来预测未来的结果。相关性分析的两个主要优点是它能够快速进行假设检验,并帮助企业决定要进一步探究哪些变量。为了确定两个变量之间线性关系的强度,相关性分析的主要方法是应用 Pearson 的 r 公式。
使用 pandas 数据框中的 corr 方法,我们可以计算列的成对相关性,并删除 NA/null 值。该技术可以作为参数传递,pearson 或 kendall 为标准相关系数,spearman 为斯皮尔曼等级相关性,或者 kendall 为 Kendall Tau 相关系数。
pandas 数据框中的 corr 方法返回一个浮动矩阵,对角线上为 1,并且无论调用函数的行为如何,都是对称的:
import numpy as np
import pandas as pd
df = pd.DataFrame([(.2, .3,.8), (.0, .6,.9), (.6, .0,.4), (.2, .1,.9),(.1, .3,.7), (.1, .5,.6), (.7, .1,.5), (.3, .0,.8),],columns=['dogs', 'cats','birds'])
corr_mat = df.corr()
我们可以使用 seaborn 热图来绘制结果的相关性矩阵:
import seaborn as sn
sn.heatmap(corr_mat, annot=True)

图 2.6: 相关性矩阵
利用成对相关性进行特征选择的主要目标是找到高度相关的特征组,并仅保留其中一个,这样可以通过尽可能少的特征来最大化模型的预测值。
成对相关性是在 DataFrame 的行或列与 Series 或 DataFrame 的行或列之间计算的。相关性是在对齐了 DataFrame 的两个轴之后计算的。接下来,我们可以看到一个例子,可能会更清楚一些:
df1=pd.DataFrame( np.random.randn(3,2), columns=['a','b'] )
df2=pd.DataFrame( np.random.randn(3,2), columns=['a','b'] )
使用 corr 来比较同一数据框内的数值型列。非数值型列将自动被跳过:
corr_mat = df1.corr()
sn.heatmap(corr_mat, annot=True)
plt.show()

图 2.7: 相关性矩阵
我们还可以使用 corrwith 比较 df1 和 df2 的列。请注意,只有具有相同名称的列才会被比较:
df1.corrwith(df2)
为了简化操作,如果我们希望 pandas 忽略列名并仅比较 df1 的第一行与 df2 的第一行,可以将 df2 的列名重命名为与 df1 匹配:
df1.corrwith(df2.set_axis( df1.columns, axis='columns', inplace=False))
需要注意的是,在这种情况下,df1 和 df2 必须具有相同的列数。
最后,你也可以将这两个数据集水平合并,并使用 corr。其优点在于,它基本上独立于列的数量和命名规则,但缺点是你可能会得到超出需要或想要的输出:
corr_mat = pd.concat([df1,df2],axis=1).corr()
sn.heatmap(corr_mat, annot=True)
plt.show()

图 2.8: 相关性热图
现在我们已经确认了通过相关性分析可以找出两个变量之间的相关性,我们可以尝试通过因果分析验证这些变量是否真正相互影响。
一个变量对另一个变量的影响能力被称为因果关系。第一个变量可能会产生第二个变量,或者可能会改变第二个变量的发生频率。
因果关系是一个事件、过程、状态或对象影响另一个事件、过程、条件或对象发展的过程,其中原因和结果是相互依赖的。那么,相关性与因果关系有什么区别呢?相关性并不自动意味着因果关系,即使因果关系和相关性可能同时存在。在 A 行动导致 B 结果的情况下,因果关系明确适用。另一方面,相关性只是两者之间的关系。
我们可以使用以下数据集来研究变量之间的因果关系:
import numpy as np
import pandas as pd
import random
ds = pd.DataFrame(columns = ['x','y'])
ds['x'] = [int(n>500) for n in random.sample(range(0, 1000),
100)]
ds['y'] = [int(n>500) for n in random.sample(range(0, 1000),
100)]
ds.head()
为了研究因果关系,我们可以尝试估算两组之间的均值差异。两组之间均值的绝对差异被称为均值差异,通常称为均值之差。它让你了解在临床研究中,实验组和对照组的平均值有多大差异。
在下一个示例中,我们将估算提升量作为均值差异的量化值,并计算确定的标准误差。我们将使用 90 作为正态分布范围内的置信区间,这将得到一个 z 值为 1.96:
base,var = ds[ds.x == 0], ds[ds.x == 1]
delta = var.y.mean() - base.y.mean()
delta_dev = 1.96 * np.sqrt(var.y.var() / var.shape[0]
+base.y.var() / base.shape[0])
print("estimated_effect":,delta, "standard_error": delta_dev)

图 2.9: 人群之间的估计差异
我们还可以使用列联 卡方 检验来比较具有二分依赖变量的两组。例如,我们可以使用是/否响应尺度对男性和女性进行对比。列联卡方检验基于与简单卡方分析相同的原理,该分析比较预期结果与实际结果。
这种统计技术用于将实际结果与预测结果进行比较。此测试的目的是确定观察到的数据与预期数据之间的差异是否是偶然的,还是研究变量之间的相关性。结果会生成一个列联矩阵,从中我们可以推断,如果 C 接近零(或等于零),则变量之间相互独立且没有关联。如果 C 不为零,则存在某种关系;C 只能取正值:
from scipy.stats import chi2_contingency
contingency_table = (
ds
.assign(placeholder=1)
.pivot_table(index="x", columns="y",
values="placeholder", aggfunc="sum")
.values)
_, p, _, _ = chi2_contingency(contingency_table,
lambda_="log-likelihood")
在这里,我们将仅尝试解释 p 值:
print("P-value:",p)

图 2.10: 结果 p 值
现在我们将使用一组合成生成的数据集:
data_1 = pd.read_csv('observed_data_1.csv' )
data_1.plot.scatter(x="z", y="y", c="x", cmap="rainbow", colorbar=False)

图 2.11: 数据分布图
连续随机变量的概率密度函数可以通过 核密度估计(KDE)seaborn 方法来估计。图中曲线下方的区域代表了数据值的概率分布:
import seaborn as sns
sns.kdeplot(data_1.loc[lambda df: df.x == 0].y, label="untreated")
sns.kdeplot(data_1.loc[lambda df: df.x == 1].y, label="treated")

图 2.12:密度图
为了研究因果关系,研究人员必须建立一个模型,以描述在因果建模中与特定现象相关的思想之间的联系。
多重因果性——即任何给定结果可能有多个原因——被纳入因果模型。例如,社会地位、年龄、性别、种族和其他因素可能会影响一个人的投票行为。此外,一些独立或解释变量可能是相互关联的。
外部效度可以通过因果模型来解决(即一个研究的结果是否适用于未研究的群体)。在某些情况下,因果模型可以结合数据提供一些单一数据集无法解决的问题的答案。
我们可以使用causalinference包中的est_via_ols函数,通过最小二乘法估计平均处理效应。
这里,y是处理后的潜在结果,D 是处理状态,X 是协变量或个体特征的向量。
控制的参数是adj,一个整数,可以是 0、1 或 2。该参数指示如何执行协变量调整。将adj设置为 0 时,将不包括任何协变量。将adj设置为 1 时,分别包括处理指示变量 D 和协变量 X,或者将adj设置为 2 时,还包括 D 与 X 之间的交互项。默认值是 2。
!pip install causalinference
from causalinference import CausalModel
cm = CausalModel(
Y=data_1.y.values,
D=data_1.x.values,
X=data_1.z.values)
cm.est_via_ols(adj=1)
print(cm.estimates)

图 2.13:因果模型结果
估计结果显示,变量之间存在负相关关系。负估计可能表明,D 的应用使 Y 的概率降低了 48%。观察所有估计分布以得出结论非常重要。
假设或反事实现实的分析是因果分析,因为我们必须对我们没有目睹的反事实结果做出假设,以评估处理效应:
data_2 = pd.read_csv('observed_data_2.csv')
data_2.plot.scatter(x="z", y="y", c="x", cmap="rainbow", colorbar=False)
之前加载的数据将向我们展示因果模型中的不同值:

图 2.14:数据分布
我们将使用新加载的值构建新的因果模型:
cm = CausalModel(
Y=data_2 .y.values,
D=data_2 .x.values,
X=data_2 .z.values)
cm.est_via_ols(adj=1)
我们可以打印处理效应估计值,以验证我们的因果模型是否有效:
print(cm.estimates)

图 2.15:使用新数据的因果模型结果
估计结果告诉我们,关系已经变为正相关。
因果模型是验证数据中变量关系建模和方向的好方法。
在下一节中,我们将深入探讨如何使用缩放方法准备我们的数据,以适应机器学习,具体取决于数据的分布情况。
特征缩放到一个范围
在使用机器学习模型时,数据预处理非常重要,以便解决诸如梯度爆炸或分布不均等问题。
为了将原始特征向量转换为更适合下游估算器的表示,sklearn.preprocessing包提供了多个常用的实用函数和转换器类。
许多在scikit-learn中使用的机器学习估算器通常需要数据集标准化;如果单个特征不大致符合标准正态分布,它们的表现可能很差:均值为 0,方差为 1 的高斯分布。
通常,标准化数据集对学习算法是有利的。如果数据集中存在异常值,建议使用鲁棒的缩放器或转换器。在包含边际异常值的数据集上,分析不同缩放器对包含异常值的数据影响时,突出显示了多个缩放器、转换器和归一化器的作用。
实际上,我们通常忽略分布的形状,简单地通过将非恒定特征除以其标准差并通过减去每个特征的均值来使数据适应缩放。
例如,学习算法的目标函数的多个组件(如支持向量机的 RBF 核函数或线性模型的 l1 和 l2 正则化项)可能假设所有特征都围绕零居中,或者它们的方差在相同数量级上。如果一个特征的方差比其他特征大几个数量级,它可能主导目标函数,阻止估算器根据其他特征进行正确推断。
StandardScaler实用程序类,作为预处理模块的一部分,能够快速简单地对类数组数据集执行以下操作:
from sklearn import preprocessing
x_train = pd.DataFrame([[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]],columns=['x','y','z'])
scaler = preprocessing.StandardScaler().fit(x_train)
以下代码将拟合缩放器到数据,假设我们的分布是标准的:
scaler.mean_
现在我们可以可视化数据的均值:

图 2.16:数据的均值
我们也可以可视化缩放:
scaler.scale_
数据显示为一组值:

图 2.17:列的缩放
最后,我们可以使用transform方法来缩放数据:
x_scaled = scaler.transform(x_train)
另一种标准化方法是将每个特征的最大绝对值缩放到单位一,或缩放到一个预定的最小值和最大值之间,通常是零和一。可以使用MaxAbsScaler或MinMaxScaler来实现这一点。
对非常小的特征标准差的鲁棒性以及在稀疏数据中保留零值是使用这种缩放的两个原因。
要将一个玩具数据矩阵缩放到[0, 1]范围,请参考以下示例:
min_max_scaler = preprocessing.MinMaxScaler()
x_train_minmax = min_max_scaler.fit_transform(x_train)
如果我们的分布与标准高斯分布不同,可以使用非线性变换。变换有两种不同的类型:幂变换和分位数变换。分位数和幂变换都通过单调变换保持每个特征的值的秩,因此它们可以保留特征的秩。
基于公式,它是特征的累积分布函数与所需输出分布的分位数函数,分位数变换将所有特征转换为相同的目标分布。这两个事实被应用于该公式:如果是具有连续累积分布函数的随机变量,则均匀分布;如果是具有均匀分布的随机变量,则具有分布。分位数变换通过排名变换平滑掉不典型的分布,相比缩放技术,它对异常值的敏感性较低。然而,它会扭曲特征内部和特征之间的相关性和距离。
Sklearn 提供了一系列称为幂变换的参数变换,旨在将数据从任何分布转换为尽可能接近高斯分布的分布。
我们可以使用 QuantileTransformer 将数据映射到均匀分布,它提供了一种非参数变换,将数据映射到一个值介于 0 和 1 之间的均匀分布:
from sklearn.datasets import load_iris
data = load_iris()
x, y = data['data'],data['target']
quantile_transformer = preprocessing.QuantileTransformer(
n_quantiles=5)
x_train_qt = quantile_transformer.fit_transform(x)
x_train_qt[:5]
我们可以看到得到的数组:

图 2.18:转换后的数据
也可以通过设置 output_distribution='normal',使用 QuantileTransformer 将数据映射到正态分布。以下示例使用了之前的 iris 数据集:
quantile_transformer = preprocessing.QuantileTransformer(
n_quantiles=5,output_distribution='normal')
x_trans_qt = quantile_transformer.fit_transform(x)
quantile_transformer.quantiles_

图 2.19:通过分位数方法转换的数据
上述代码将使用分位数变换对数据进行缩放,定义五个分位数,并期望输出具有正态分布。
为了确定要使用的适当分布,我们需要深入分析我们变量的分布,因为错误的变换可能会导致我们丧失可能需要考虑的重要细节。
在下一节中,我们将通过使用 scikit-learn 的聚类算法深入探讨无监督学习。
聚类数据与降维
聚类过程涉及将总体或数据点分成若干组,使得每组内的数据点彼此之间比其他组的数据点更相似。简单来说,目标是将具有相似特征的人群排序成簇。它在商业分析中常常被使用。如何将大量可用数据整理成有用的结构,是当前组织面临的一个问题。
图像分割、网页分组、市场细分和信息检索是聚类如何帮助企业更好地管理数据的四个例子。数据聚类对于零售公司非常有益,因为它影响销售努力、客户保持和客户购物行为。
向量量化技术“K-means 聚类”的目标,起源于信号处理,是将 n 个观测值划分为 k 个簇,每个簇的原型是与其均值最接近的观测值。K-means 聚类是一种无监督的技术,它使用输入数据本身,并且不需要标签化的响应。K-means 聚类是一种流行的聚类方法。通常,实践者首先研究数据集的结构。数据点被 K-means 分为不同的、不重叠的组。
在下一个代码中,我们可以使用KMeans来拟合数据,以便将每个数据点标记为某个给定的簇:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=len(set(y)), random_state=0).fit(x)
kmeans.labels_

图 2.20:聚类数据
我们可以预测每个新的数据实例属于哪个聚类:
kmeans.predict(x[0].reshape(1,-1))

图 2.21:预测数据
我们还可以可视化聚类中心:
kmeans.cluster_centers_

图 2.22:聚类中心
KMeans 使我们能够在变量过多时找到共同数据的特征,并且对分割很有帮助。但有时需要将维度减少到一组具有共同特征的变量。
为了将数据投影到一个低维环境中,我们可以使用TruncatedSVD类。
接下来,我们将数据拟合到两个组件中,以减少维度:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(x)
我们应该努力解释尽可能多的方差,简单来说,就是我们模型能够解释整个数据集的程度:
print(pca.explained_variance_ratio_)
print(pca.singular_values_)

图 2.23:PCA 奇异值
在我们对数据进行预处理、减少维度并进行聚类之后,现在可以构建机器学习模型,预测未来的行为。
在接下来的章节中,我们将构建机器学习模型,用于预测回归和分类任务中的新数据标签。
构建机器学习模型
我们可以构建的最简单的机器学习模型之一是线性回归,它通过最小化数据集中观察到的目标与线性近似预测目标之间的残差平方和,拟合一个线性模型,并使用系数。
从实现角度来看,这实际上是普通最小二乘法或非负最小二乘法,通过预测器对象进行封装。
我们可以通过使用 Sklearn 中的LinearRegression类来非常简单地实现这一点:
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_diabetes
data_reg = load_diabetes()
x,y = data_reg['data'],data_reg['target']
reg = LinearRegression().fit(x, y)
reg.score(x, y)

图 2.24:模型回归得分
前面的代码将为我们的数据拟合一个线性回归模型,并打印出数据的得分。
我们还可以打印出系数,这可以很好地估计每个变量对解释我们要预测的变量的贡献:
reg.coef_

图 2.25:回归系数
我们还可以打印截距变量:
reg.intercept_

图 2.26:回归截距
最后,我们可以使用模型进行预测:
print('Predicted:',reg.predict(x[10].reshape(
1,-1)),'Actual:',y[10])

图 2.27:预测的回归值
在这里,我们预测的是一个连续变量,但我们也可以使用分类器而非回归来预测分类变量。
Sklearn 让我们可以选择使用逻辑回归(logit 和 MaxEnt)分类器,在多类情况下,如果将 'multi_class' 选项设置为 'ovr',训练算法会使用一对多(OvR)方案;如果将 'multi_class' 选项设置为 'multinomial',则会使用交叉熵损失。该类使用 'liblinear' 库,'newton-cg'、'sag'、'saga' 和 'lbfgs' 求解器来实现正则化逻辑回归。请记住,正则化是默认启用的。它可以处理稠密和稀疏输入。为了获得最佳速度,仅使用 64 位浮动矩阵;所有其他输入格式将会转换。
"newton-cg"、"sag" 和 "lbfgs" 求解器唯一支持的正则化是具有原始公式的 L2 正则化。"liblinear" 求解器支持 L1 和 L2 正则化,但只有 L2 惩罚项具有对偶公式。唯一支持弹性网正则化的求解器是 "saga" 求解器。
在拟合模型时,底层的 C 程序通过随机数生成器选择特征。因此,对于相同的输入数据,稍有不同的输出是常见的。如果发生这种情况,可以尝试使用较小的 tol 参数:
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_digits
data_class = load_digits()
x,y = data_class['data'],data_class['target']
clf = make_pipeline(StandardScaler(),
LogisticRegression(penalty='l2',C=.1))
clf.fit(x, y)
clf.predict(x[:2, :])

图 2.28:逻辑回归结果
我们还可以对模型进行评分,以评估预测的精度:
clf.score(x, y)

图 2.29:用户数据
为了验证模型,我们可以使用交叉验证,这允许我们评估估算器的性能。这是学习预测函数参数并在同一数据集上评估它的一个方法学错误。一个仅仅重复它刚刚看到的样本标签的模型会得分较高,但无法对尚未见过的数据做出预测。过拟合是这种情况的术语。为了避免这个问题,在进行(有监督的)机器学习实验时,通常会预留一部分可用数据作为测试集(x 测试,y 测试)。
需要注意的是,“实验”一词不仅仅指学术目的,因为机器学习实验有时也始于商业环境。网格搜索方法可用于寻找最优参数。
在scikit-learn中,可以使用train_test_split辅助函数快速将数据随机划分为训练集和测试集。我们加载iris数据集,并在其上训练一个线性支持向量机:
x, y = load_iris(return_X_y=True)
x.shape, y.shape

图 2.30:数据形状
现在,我们可以快速采样一个训练集,同时保留 40%的数据用于测试(评估)我们的分类器:
from sklearn.model_selection import train_test_split
from sklearn import svm
x_train, x_test, y_train, y_test = train_test_split(x, y,
test_size=0.4, random_state=0)
我们可以通过查看numpy数组的形状来验证生成的训练数据集的形状:
x_train.shape, y_train.shape

图 2.31:训练数据形状
我们可以对test数据集执行相同操作:
x_test.shape, y_test.shape

图 2.32:测试数据形状
最后,我们可以在训练数据上训练我们的机器学习模型,并使用test数据集对其进行评分,该数据集包含模型在训练过程中未见过的数据点:
clf = svm.SVC(kernel='linear', C=.7).fit(x_train, y_train)
clf.score(x_test, y_test)

图 2.33:逻辑回归评分
在比较不同超参数设置时,仍然有可能出现过拟合现象,例如 SVM 中必须手动选择的 C 参数。这是因为参数可以调整直到估计器的表现最佳。在这种方法中,模型可能会“泄漏”测试集的信息,从而导致评估度量无法准确反映泛化性能。这个问题可以通过进一步从数据集中留出一部分作为“验证集”来解决:首先在训练集上进行训练,然后在验证集上进行评估,当实验成功时,最后可以在测试集上进行最终评估。
然而,通过将可用数据划分为三组,我们大幅减少了可用于训练模型的样本数量,且根据训练集和验证集的随机化,结果可能有所不同。
交叉验证(简称 CV)是一种可以用来解决这个问题的方法。在进行交叉验证时,不再需要验证集,但仍应保留一个测试集以供最终评估。其基本策略,称为 k 折交叉验证,将训练集划分为 k 个较小的子集(其他方法见下文,但通常遵循相同的原则)。每一个 k 个“折”操作如下进行:
各个折叠被用作模型的训练数据,随后使用剩余的部分数据进行验证(即,用作测试集来计算准确度等性能指标)。
在循环中计算的数字的平均值就是由 k 折交叉验证提供的性能指标。尽管这种方法计算开销较大,但它不会浪费大量数据(不像固定一个任意的验证集),这是在样本较少的应用中,如逆推断,的一个显著优势。
我们可以通过在估计器上调用cross_val得分帮助函数来计算交叉验证指标,并且该数据集是应用交叉验证的最简单方法。以下示例展示了如何拆分数据,开发模型,并连续五次计算得分(每次使用不同的拆分)以衡量线性核支持向量机在iris数据集上的准确性:
from sklearn.model_selection import cross_val_score
scores = cross_val_score(clf, x, y, cv=5)
因此,平均得分和标准差由以下公式给出:
print('Mean:',scores.mean(),'Standard Deviation:',
scores.std())
估计器的评分技术默认用于计算每次 CV 迭代时的得分:

图 2.34:CV 平均得分
通过应用scoring参数可以更改此设置:
from sklearn import metrics
scores = cross_val_score(clf, x, y, cv=5, scoring='f1_macro')
scores
由于iris数据集中的样本在目标类别中均匀分布,准确率和 F1 分数几乎相等:

图 2.35:交叉验证(CV)得分
当cv参数是整数时,CV 得分默认使用KFold或StratifiedKFold策略,如果估计器来自ClassifierMixin,则使用后者。
也可以通过传递一个使用ShuffleSplit Sklearn 类的 CV 迭代器来使用其他 CV 策略:
from sklearn.model_selection import ShuffleSplit
n_samples = x.shape[0]
cv = ShuffleSplit(n_splits=5, test_size=0.3, random_state=0)
cross_val_score(clf, x, y, cv=cv)
上述代码将向我们展示在多个测试样本折叠上的 CV 得分,这可以用来防止过拟合问题:

图 2.36:使用洗牌拆分的结果
上述结果向我们展示了 CV 得分的结果。
概述
在本章中,我们学习了如何使用描述性统计和机器学习模型量化群体之间的差异,这些差异可以用来验证商业假设以及评估某些营销活动的效果提升。我们还学习了如何通过相关性和因果分析研究变量之间的关系,并如何使用线性模型对这些关系进行建模。最后,我们构建了机器学习模型来预测和分类变量。
在下一章中,我们将学习如何利用网络搜索结果,并将其应用于市场研究的背景下。
第二部分:市场与客户洞察
在本部分,您将学习如何通过利用某些工具获取并分析市场数据。本部分将教您如何获取搜索趋势,使用相似查询丰富趋势,使用抓取工具获取数据,并通过有效的可视化监控关键业务绩效 KPI 来构建结果,从而做出更好的决策。
本部分涵盖以下章节:
-
第三章,通过市场洞察寻找商业机会
-
第四章,通过联合分析理解客户偏好
-
第五章,通过价格需求弹性选择最优价格
-
第六章,产品推荐
第三章:利用市场洞察发现商业机会
近年来,"洞察"一词在创新市场测试者中使用得越来越频繁。大多数时候,它没有明确的定义,有时暗示在使用的数据中存在隐藏的模式,或者它可以在商业环境中用于创造新的收入来源,更清晰地定义特定市场的条件和偏好,或了解不同地理区域或群体中不同的客户偏好。
在本章中,我们将使用搜索引擎趋势分析不同金融资产在多个市场中的表现。总体来说,我们将关注以下内容:
-
使用 Pytrends 包通过 Google Trends 数据收集不同术语的相对表现信息
-
查找这些洞察模式的变化,以识别消费者偏好的转变
-
使用关于相似查询的信息,理解与我们研究的每个金融产品相关的搜索模式
之后,我们将逐步进行分析,在本章结束时,你将能够做到以下几点:
-
利用搜索引擎趋势识别可能对某一产品或服务感兴趣/购买/订阅的地区
-
理解这些关系的行为正在发生变化,并适应这些变化
-
扩展搜索空间分析,使用与原始查询相关的术语,以更好地理解潜在的市场需求
本章包含以下内容:
-
使用 Pytrends 理解搜索趋势
-
安装 Pytrends 和市场排名
-
查找搜索趋势模式的变化
-
使用相似的查询来获取关于新趋势的洞察
-
分析相似查询在时间上的表现
让我们开始使用 Google Trends API 分析趋势,通过一组例子和一些假设情境,许多公司和企业可能会面临这种情境。例如,收集有关特定市场的情报,以便广告宣传新产品和服务。
技术要求
为了能够跟随本章中的步骤,你需要满足以下要求:
-
一个运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果你有 Google Drive 账户,你也可以使用 Google Colab Notebook 来运行步骤。
-
理解基本的数学和统计概念。
使用 Pytrends 理解搜索趋势
在市场营销中,市场调研指的是寻找有关特定目标市场的相关、可操作且新颖的知识,这在规划商业策略时始终至关重要。这意味着目标是获取能够帮助我们更好了解特定市场需求以及企业如何通过其产品和服务满足这些需求的信息。我们通过数据分析来发现之前未见的模式,从而获取这些知识。这一新的洞察使得公司能够做到以下几点:
-
通过实际满足需求来实现创新
-
更好地理解特定市场中的客户
-
监测品牌知名度等
在本章中,我们将分析一组金融资产及其在不同市场中的表现,以及人们在搜索这些资产时的搜索行为。我们特别跟踪的四种资产是比特币、房地产、债券和股票。这些资产是加密货币、房地产市场、债券(即贷款给某公司或政府)以及股票(代表公司部分所有权)的金融资产。在这种情况下,我们将模拟一个想要拥有特定资产组合并希望通过更好地满足目标市场的搜索模式来吸引特定市场的公司。
我们将使用 Pytrends 包,它通过 Google Trends API 访问数据,且该 API 在一些例外情况下是免费的。
Google Trends API 提供与任何浏览器界面上显示的信息相同的数据,但在处理大量数据时,通过 API 访问数据更加便捷。
这个数据存在一些需要仔细考虑的局限性,具体如下:
-
该 API 对在给定时间内可以检索的响应数量有所限制。这意味着在短时间内不能发出大量请求。
-
结果以相对的方式显示。所使用的查询如果移除其中一个并重新请求,将会得到不同的结果。这是因为结果是相对的。
-
根据 Google 的定义,度量单位在 0 到 100 之间计算,考虑到特定地点某个查询的搜索总量的比例。
-
请求在同一时间内可以提供的词语数量是有限制的。每次请求最多只能比较五个词语。这一限制不适用于结果,结果有时可能非常详细。
既然我们已经理解了我们在寻找什么,那么让我们开始分析吧。
安装 Pytrends 并对市场进行排名
作为第一步,我们需要安装一个包来分析网页搜索数据。我们将安装 Pytrends 包,它是 Google Trends API 的一个封装器。为此,打开一个运行 Python 3.7 的新 Jupyter Notebook,并在新单元格中运行以下命令来安装该包:
pip install pytrends
安装完包后,我们可以开始分析。我们可以向 API 发出几种类型的查询,如下所示:
-
随时间变化的兴趣
-
历史小时兴趣
-
按地区的兴趣
-
相关主题
-
相关查询
-
热搜
-
实时搜索趋势
-
热门榜单
-
建议
在这种情况下,我们希望获得有关给定搜索词集在各地区的兴趣信息。我们将按照以下步骤进行:
-
导入
pandas包来存储结果并绘制数据。 -
初始化 Pytrends API,我们将传入一组搜索词,并以过去 12 个月作为搜索的参数构建有效载荷。
-
最后,我们将把结果存储在一个名为
regiondf_12m的pandasDataFrame 中:import pandas as pdfrom pytrends.request import TrendReqpytrend = TrendReq()#provide your search termskw_list=['bitcoin','stocks','real estate','bonds']#search interest per regionpytrend.build_payload(kw_list, timeframe='today 12-m')# Interest by Regionregiondf_12m = pytrend.interest_by_region()
在结果中,可能会有一些地区没有我们所搜索的词的结果,因此我们可以通过对行求和并检查是否和为零来删除这些行。如果为零,我们将删除这一行。如果你在给定时间内发出过多请求,Google 会限制你进行新的查询,你将收到 429 错误代码,这表明你可能需要等待一段时间才能再次查询。
-
现在,我们可以利用这个逻辑创建一个括号中的掩码,传递给
regiondf_12mDataFrame,以删除没有结果的行:# #looking at rows where all values are not equal to 0regiondf_12m = regiondf_12m[regiondf_12m.sum(axis=1)!=0] -
最后,我们可以使用
pandasDataFrame 的plot方法来可视化结果,绘制一个显示结果的柱状图:# visualizeregiondf_12m.plot(figsize=(14, 8), y=kw_list,kind ='bar')
执行前面的代码块将提示我们得到类似这样的结果:

图 3.1:过去 12 个月各地区的相对搜索趋势重要性
在这里,重要的是要记住,搜索趋势是作为给定区域总搜索量的一个分数建模的,因此所有这些结果仅与它们所指示的地区相关。
仅从这个柱状图分析结果可能有点困难,因此我们将深入分析我们分析的每一个搜索词的具体情况。
-
现在,我们将绘制过去 12 个月里
bitcoin搜索词表现较好的地区。为此,我们需要选择相应的列,对数值进行升序排序,并使用plot方法绘制柱状图,显示在这些地区搜索词相对于总区域搜索量的较大比例:regiondf_12m['bitcoin'].sort_values(ascending= False).plot(figsize=(14, 8),y=regiondf_12m['bitcoin'].index,kind ='bar')
这段代码生成以下结果:

图 3.2:过去 12 个月比特币是热门搜索趋势的国家
这个图表显示了bitcoin在过去 12 个月里成为热门搜索趋势的国家。这可以作为市场中给定趋势成熟度的指标,因为这意味着在至少一年内,这些地区相对于其他搜索趋势的搜索量是持续存在的。
- 接下来,我们将对其余的搜索词执行相同的操作,并将比较各个市场之间的差异。
接下来的代码块以相同的方式过滤、排序并绘制数据,不过这次是针对 real estate 搜索词:
regiondf_12m['real estate'].sort_values(ascending=
False).plot(figsize=(14, 8),
y=regiondf_12m['real estate'].index,kind ='bar')
这一代码块的结果是一个柱状图,显示了不同地区对这一术语的搜索结果:

图 3.3:在过去 12 个月中,房地产是搜索趋势排名前列的国家
作为第一个区别,我们可以看到房地产的结果在结果分布上有所不同。虽然我们可以在 图 3.1 中看到结果在各个结果之间趋于更均匀分布,但在房地产结果中,数据显示只有少数国家对这一术语的搜索量较大。这可能表明这种类型的投资与当地的法规和条件更加紧密相关。我们可以看到,澳大利亚、新西兰、美国和加拿大是唯一搜索量超过 40 分的国家。
-
接下来的代码块将展示我们关于搜索词“股票”的表现:
regiondf_12m['stocks'].sort_values(ascending= False).plot(figsize=(14, 8),y=regiondf_12m['stocks'].index,kind ='bar')
这显示了过去 12 个月中 stocks 搜索词的结果:

图 3.4:在过去 12 个月中,股票是搜索趋势排名前列的国家
在这里,趋势倾向于重复,但出现在不同的国家。在这种情况下,前五个国家是印度、美国、菲律宾、加拿大和沙特阿拉伯。这里的区别在于,印度是唯一一个超过 40 分的国家。这可能是推测这些地区人们在投资选择方面思考方式的一种方式。
-
最后,我们需要重复相同的过程,但对于
bonds搜索词,通过更改之前代码中使用的列来实现:regiondf_12m['bonds'].sort_values(ascending= False).plot(figsize=(14, 8),y=regiondf_12m['bonds'].index, kind ='bar')
执行这段代码将返回以下结果:

图 3.5:在过去 12 个月中,债券是搜索趋势排名前列的国家
可以看出,债券似乎是用户在搜索投资选项时使用较少的选择。澳大利亚在债券搜索占比方面处于领先地位,同时房地产搜索也位列前茅,这表明债券和房地产似乎是澳大利亚比股票或比特币更具吸引力的选项。
现在我们已经确定了每个资产在受欢迎程度方面表现最好的地区,我们将研究这些趋势模式的变化。
寻找搜索趋势模式的变化
搜索趋势不是静态变量;事实上,它们会随时间变化。我们将获得过去 3 个月按地区划分的兴趣结果,然后与过去 12 个月的结果进行比较,看看结果的变化:
-
为了找出搜索趋势的变化模式,我们将在不同的时间框架内构建负载。
-
最后,我们将把结果存储在一个名为
regiondf_3m的pandas数据框中:#search interest per regionpytrend = TrendReq()pytrend.build_payload(kw_list, timeframe='today 3-m')# Interest by Regionregiondf_3m = pytrend.interest_by_region() -
我们需要删除没有搜索词结果的行:
# #looking at rows where all values are not equal to 0regiondf_3m = regiondf_3m[regiondf_3m.sum(axis=1)!=0] -
现在,我们可以使用
pandas数据框的plot方法来可视化结果:# visualizeregiondf_3m.plot(figsize=(14, 8), y=kw_list, kind ='bar')
这段代码会生成下一个可视化结果:

图 3.6:过去 3 个月的兴趣变化
一眼看去,要找到结果中可能的变化是非常复杂的,所以我们将以一种方式排列数据,只展示这些趋势的变化:
-
第一步是创建一个包含过去 12 个月和过去 3 个月信息的数据框:
df = pd.DataFrame([regiondf_3m['stocks'],regiondf_12m['stocks'],regiondf_3m['bitcoin'],regiondf_12m['bitcoin'],regiondf_3m['real estate'],regiondf_12m['real estate'],regiondf_3m['bonds'],regiondf_12m['bonds']]).T
在这里,数据框是通过连接不同搜索词的结果构建的。
-
接下来,我们需要重命名列并创建表示搜索词随时间变化的兴趣差异的列:
cols = ['stocks_3m','stocks_12m','bitcoin_3m','bitcoin_12m','real_estate_3m','real_estate_12m','bonds_3m','bonds_12m']df.columns = colsdf['diff_stocks'] = df['stocks_12m'] - df['stocks_3m']df['diff_bitcoin'] = df['bitcoin_12m'] - df['bitcoin_3m']df['diff_real_estate'] = df['real_estate_12m'] - df['real_estate_3m']df['diff_bonds'] = df['bonds_12m'] - df['bonds_3m']# Inspect the new created columnsdf.head() -
现在,我们可以限制新创建的列的值:
n_cols = ['diff_stocks','diff_bitcoin','diff_real_estate','diff_bonds']df = df[n_cols]df.head()
这将生成下一个结果,展示搜索趋势的相对变化:

图 3.7:比较过去 3 个月与 12 个月之间的相对差异
-
有些国家没有变化,因此我们将通过沿着轴求和并将结果与零进行比较,来创建一个掩码,过滤掉这些情况:
# Create a mask for of the null resultsmask = df.abs().sum(axis=1)!=0df = df[mask]df.head()
需要注意的是,这种比较仅在绝对值的基础上进行。
上述代码向我们展示了经过筛选的结果。

图 3.8:筛选后的相对差异结果
- 现在,我们可以展示所获得的结果。我们将首先查看那些搜索“股票”一词时发生变化的地区。
为此,我们将使用图 3.8中的结果,根据我们想要的列进行筛选,剔除没有变化的行,按升序排序,然后使用pandas数据框的plot方法可视化结果:
data = df['diff_stocks'][df['diff_stocks']!=0]
data = data.sort_values(ascending = False)
data.plot(figsize=(14, 8),y=data.index, kind ='bar')
这将生成下一个结果:

图 3.9:过去 12 个月中,搜索词股票在不同国家的流行度变化
结果显示,与之相比,股票作为搜索趋势在阿联酋和美国的流行度有所巩固,而在巴基斯坦和俄罗斯的流行度则有所下降。这些结果不能直接解释为对资产本身价值认知的变化,而是流量变化的指示。流量的增加可能由正面或负面因素引起,因此深入分析总是很重要。
-
现在,我们将查看“比特币”搜索趋势变化的差异:
df['diff_bitcoin'][df['diff_bitcoin']!=0].sort_values(ascending = False).plot(figsize=(14, 8), y=df['diff_bitcoin'][df['diff_bitcoin']!=0].index,kind ='bar')
代码生成下一个图表:

图 3.10:在过去 12 个月中,“比特币”这一搜索词的流行变化国家
关于这个图表,首先需要注意的是,最近几个月的大多数变化是积极的,而非负面的。这可能是过去几个月全球范围内更加广泛采纳的一个标志。
在增长方面,领先的国家是新西兰、巴基斯坦和南非,而阿联酋则处于下降的领先地位。
-
下一个代码块展示了“债券”这一搜索词的相同效果:
df['diff_bonds'][df['diff_bonds']!=0].sort_values(ascending = False).plot(figsize=(14, 8),y = df['diff_bonds'][df['diff_bonds']!=0].index,kind ='bar')
结果将在下一个图表中展示:

图 3.11:在过去 12 个月中,“债券”这一搜索词的流行变化国家
可以看到一个有趣的趋势变化,现在大多数国家的搜索量都有所下降。这可能是全球投资模式一致变化的一个标志,某些国家受影响的程度较大。还可以注意到,新西兰现在在所分析的时期内是搜索趋势下降最多的国家,而在比特币的情况下,它曾是增长最快的国家。美国也是如此,虽然出现了下降趋势,但在“股票”这一搜索词的变化中却表现出正面的变化。
-
最后,下面的代码块显示了“房地产”搜索词的变化:
df['diff_real_estate'][df['diff_real_estate']!=0].sort_values(ascending = False).plot(figsize=(14, 8),y=df['diff_real_estate'][df['diff_real_estate']!=0].index,kind ='bar')

图 3.12:在过去几个月中,“房地产”这一搜索词的流行变化国家
在这种情况下,变化也可以被视为全球范围内负面变化大于正面变化。虽然一些国家如越南和泰国经历了正向变化,但更多的国家在搜索词上出现了下降。这不仅可能由投资决策引起,还可能与搜索模式相关。根据地区的不同,在大多数情况下,房地产被视为一种投资,而在其他情况下,它仅仅是为了居住或商业目的。
在接下来的部分中,我们将深入探讨经历显著变化的国家,并尝试更详细地理解其原因。
使用相关查询获取有关新趋势的洞察
如果我们想要找到与我们正在搜索的搜索词最相关的术语的更多信息,可以使用相关查询获取与我们搜索的查询相似的查询。这是有用的,因为它不仅提供了背景信息,还提供了可以进一步分析的趋势信息。
在接下来的代码块中,我们将定义一系列区域,在给定的时间范围内查找相关查询。在这种情况下,我们将查看美国、加拿大、新西兰和澳大利亚。结果将被整理成一个单一的pandas数据框:
geo = ['US','CA','NZ','AU']
d_full = pd.DataFrame()
for g in geo:
pytrend.build_payload(kw_list=['bitcoin','stocks'],
geo=g,timeframe='today 3-m')
#get related queries
related_queries = pytrend.related_queries()
# Bitcoin top
d = related_queries['bitcoin']['top']
d['source_query'] = 'bitcoin'
d['type'] = 'top'
d['geo'] = g
d_full = pd.concat([d_full,d],axis=0)
# Bitcoin rising
d = related_queries['bitcoin']['rising']
d['source_query'] = 'bitcoin'
d['type'] = 'rising'
d['geo'] = g
d_full = pd.concat([d_full,d],axis=0)
# stocks top
d = related_queries['stocks']['top']
d['source_query'] = 'stocks'
d['type'] = 'top'
d['geo'] = g
d_full = pd.concat([d_full,d],axis=0)
# stocks rising
d = related_queries['stocks']['rising']
d['source_query'] = 'stocks'
d['type'] = 'rising'
d['geo'] = g
d_full = pd.concat([d_full,d],axis=0)
这段代码将遍历定义的区域,并合并相关查询的结果,这些查询是在给定时间范围内的顶级结果,以及在该地区上升的查询。
最后,我们重置索引并显示前 10 行:
d_full = d_full.reset_index(drop=True)
d_full.head(10)
这将产生以下输出:

图 3.13:美国地区与比特币相似的查询
我们可以利用相关查询的信息,对与原始搜索词相关的频率和术语进行分析。这将为我们提供更好的背景,以了解特定地区某一资产的相关因素。这意味着,在这种情况下,我们可以看到搜索比特币的人也在搜索以太坊,因此我们可以预期它们之间存在一定的相关性:
-
让我们使用一种简单的方法,通过使用词云来展示这些查询中最频繁的术语,从而展示最重要的术语。我们将使用
wordcloud和matplotlib包:from wordcloud import WordCloudfrom wordcloud import ImageColorGeneratorfrom wordcloud import STOPWORDSimport matplotlib.pyplot as plt -
我们将使用美国与“股票”相关的顶级相关查询作为数据:
data = d_full[(d_full['source_query']=='stocks')&(d_full['type']=='top')&(d_full['geo']=='US')] -
之后,我们将合并所有结果列,替换源查询以避免冗余,并去除停用词。最后,代码的最后部分将生成图形并显示结果:
text = " ".join(i for i in data['query']).replace('stock','')stopwords = set(STOPWORDS)wordcloud = WordCloud(stopwords=stopwords, background_color="white").generate(text)plt.figure( figsize=(12,8))plt.imshow(wordcloud, interpolation='bilinear')plt.axis("off")plt.show()
这将生成下一个词云,大小适应术语的相对频率。请注意,我们始终需要至少一个词才能绘制,否则我们将收到一个错误,提示我们得到的词数为 0。

图 3.14:美国“股票”搜索词的频繁相关查询
结果显示,“股票”这个术语主要与以下术语相关:
-
penny:指的是便士股市场
-
now,today:对日常频繁更新的信息和实时更新的需求
-
dividend:偏好支付股息的股票
-
amazon,tesla:受欢迎的股票
-
reddit,news:信息来源
-
购买,投资:用户的行为
当试图获取有关驱动用户的模式的信息时,这些信息可能非常有趣。
-
接下来是与之前相同的代码,但现在它引用了在美国与股票相关的上升搜索词:
data = d_full[(d_full['source_query']=='stocks')&(d_full['type']=='rising')&(d_full['geo']=='US')]text = " ".join(i for i in data['query']).replace('stock','')stopwords = set(STOPWORDS)wordcloud = WordCloud(stopwords=stopwords,background_color="white").generate(text)plt.figure( figsize=(12,8))plt.imshow(wordcloud, interpolation='bilinear')plt.axis("off")plt.show()
下一幅图将展示我们原始搜索词相关的上升查询中找到的术语。这些术语的大小根据它们的频率变化,提供了它们相对重要性的信息。

图 3.15:美国与搜索词股票相关的常见热门查询
它显示了不同层次的信息,正如我们所看到的,目前正在发生的事件,例如短期内对衰退的恐惧,表明市场看涨,还有一些新股票名称,如 Twitter、Disney 和 Haliburton。
-
我们通过查看与新西兰比特币搜索词相关的热门查询,重复相同的练习:
data = d_full[(d_full['source_query']=='bitcoin')&(d_full['type']=='top')&(d_full['geo']=='NZ')]text = " ".join(i for i in data['query']).replace('bitcoin','')stopwords = set(STOPWORDS)wordcloud = WordCloud(stopwords=stopwords,background_color="white").generate(text)plt.figure( figsize=(12,8))plt.imshow(wordcloud, interpolation='bilinear')plt.axis("off")plt.show()
这生成了下一个词云。

图 3.16:新西兰与搜索词比特币相关的常见热门查询
在这里,我们可以看到特斯拉与比特币以及道琼斯之间的关系。后者可能表明那些想要投资比特币的人,已经在股市中有投资,或者正在考虑投资其中之一。
-
下一个焦点是新西兰与比特币相关的上升术语:
data = d_full[(d_full['source_query']=='bitcoin')&(d_full['type']=='rising')&(d_full['geo']=='NZ')]text = " ".join(i for i in data['query']).replace('bitcoin','')stopwords = set(STOPWORDS)wordcloud = WordCloud(stopwords=stopwords,background_color="white").generate(text)plt.figure( figsize=(12,8))plt.imshow(wordcloud, interpolation='bilinear')plt.axis("off")plt.show()
结果展示在下一个词云中。

图 3.17:新西兰与搜索词比特币相关的常见热门查询
结果显示了其他加密货币的名称,如 Cardano 和 Luna,同时我们还可以看到“崩盘”这个词,可能与后者有关。
这些可视化图表能够帮助我们一眼就识别出一些越来越重要的术语,它们通过表示每个术语的流行趋势来使用大小来展示。人类一次性吸收大量详细信息的能力有限,因此我们需要提前思考数据的讲故事方式。
分析相关查询随时间变化的表现
在获取更多关于背景信息后,我们可以追踪这些查询随时间的演变。这可以为我们提供关于在我们雷达下逐渐升温的本地趋势的有价值信息。我们将通过以下步骤进行:
-
我们将选择与美国比特币相关的上升查询:
query_data = d_full[(d_full['source_query']=='bitcoin' )&(d_full['type']=='rising')&(d_full['geo']=='US')]query_data.head()
这将产生以下输出:

图 3.18:美国与比特币相关的上升查询
-
我们将使用排名前五的查询,追踪它们在过去 12 个月中的表现:
kw_list = query_data.head()['query'].tolist() # list of keywords to get data# build payloadpytrend.build_payload(kw_list, cat=0, timeframe='today 12-m')# Interest over Timedata = pytrend.interest_over_time()data = data.reset_index() -
最后,我们将使用 Plotly 库来展示结果,以便能够更互动地展示信息:
import plotly.express as pxfig = px.line(data, x="date", y=kw_list, title='Keyword Web Search Interest Over Time')fig.show()
这展示了以下图表,我们可以看到随时间推移,趋势的上升演变:

图 3.19:查询性能随时间的变化
我们可以看到,查询在 2021 年 11 月左右达到了峰值,且表现不一。
总结
获取不同市场中特定实时趋势的信息可能会非常昂贵,但使用网页搜索引擎流量可以为我们提供有价值的工具,以分析不同地区。在本章中,我们主要关注了按国家级别进行的地区分析,但我们也可以使用不同的地区地理编码,例如 US-NY 代表纽约。
这些信息还可以与销售数据结合使用,以获得有价值的相关性,通过因果分析,能够生成一个变量,用于预测行为,正如我们将在接下来的章节中看到的那样。
接下来的章节将继续关注潜在价值的理解,但这次将聚焦于使用联合分析对产品特征进行评分的层面。
第四章:通过联合分析理解客户偏好
联合分析是用于产品和定价研究的知名方法,可以识别客户偏好,并利用这些知识选择产品特性、评估价格敏感性、预测市场份额以及预测消费者对新产品或服务的接受程度。
联合分析经常用于各种商品,包括消费品、电器、人寿保险政策、退休社区、奢侈品和航空旅行等多个领域。它可以应用于各种情境,旨在了解客户最可能购买的产品类型以及消费者在产品中最看重(和最不看重)的特性。因此,它在产品管理、市场营销和广告中被广泛使用。
联合分析对各种规模的企业都有益处,即使是小型的本地餐馆和杂货店。
在本章中,您将学习联合分析在市场研究中的应用及实验如何进行。然后,您将使用普通最小二乘(OLS)模型进行联合分析,并使用不同的机器学习(ML)模型预测新产品特性的表现。
本章包括以下主题:
-
理解联合分析
-
设计联合实验
-
确定产品的相关属性
-
使用 Python 和 Statsmodels 的 OLS
-
使用更多产品特性
-
预测新特性组合
让我们使用一些消费品零售产品的模拟数据来进行分析。
技术要求
为了能够按照本章的步骤操作,您需要满足以下要求:
-
运行 Python 3.7 及以上版本的 Jupyter 笔记本实例。如果您有 Google Drive 帐户,也可以使用 Google Colab 笔记本运行这些步骤。
-
对基本数学和统计概念的理解。
理解联合分析
联合分析是一种研究性统计方法,用于市场研究,以确定人们如何评估构成单一产品或服务的不同属性(特征、功能和好处)。
联合分析起源于数学心理学,其目标是确定有限数量属性的组合对受访者选择和决策的影响最大。受访者被呈现一组潜在产品或服务,并通过分析如何从这些产品中选择,隐含评估了产品或服务的每个组成部分。您可以决定使用这些隐含评分(效用或分数)创建估计市场份额、销量甚至新设计的盈利能力的市场模型。
可设计不同类型的联合研究:
-
基于排名的联合
-
基于评分的联合
-
基于选择的联合
联合分析还广泛应用于社会科学和实际科学领域,包括运筹学、产品管理和营销。它通常用于服务设计、广告吸引力分析以及新产品设计的消费者接受度测试。尽管联合分析已被应用于产品布局,但也有一些人反对这种用法。
联合分析方法是系统决策分析工具中一类更广泛的权衡分析工具的子集,也被称为多属性组合建模、离散选择建模或表达偏好研究。联合分析将产品或服务拆解为其称为属性的组成部分,并测试这些组件的不同组合,以确定消费者的偏好。
在下一个图示中,我们可以看到产品是不同水平特征的组合——例如,一袋薯片可以通过品牌、口味、尺寸和价格等水平来表示:

图 4.1:不同产品作为特征的独特组合
下面是这些属性和水平组合可能出现在联合选择任务中的选项方式:

图 4.2:联合选择任务
联合分析采用了一种更加现实的方式,而不是仅仅询问你喜欢产品的哪些方面或哪些特征最重要。
在调查中,每个人被要求在具有相同属性水平但不同组合的产品之间做出选择。这个模型被称为基于选择的联合分析,在大多数情况下,由 8 到 12 个“对抗”产品组成。这个过程旨在模拟购买行为,越接近实际情况,效果越好。属性选择和对抗设置被设计为需要领域知识的实验。这些信息随后被用来衡量各个属性对用户购买模式的影响。
现在我们已经了解了联合分析是什么以及如何用它来衡量顾客对某些产品特征的看法,接下来我们将探讨如何设计这些实验。
设计联合实验
产品或服务领域描述为一组属性。例如,一台笔记本电脑可能具有屏幕尺寸、品牌和价格等特征。因此,每个属性可以划分为多个层级——例如,屏幕尺寸可以是 13、14 或 15 英寸。受访者将看到一组由所有或部分配置属性层级组合而成的产品、原型、模型或图片,以供选择、排序或展示进行评估。你将被要求进行排名。每个示例足够相似,消费者会认为它是一个不错的替代品,但又足够不同,使得受访者能够清晰地识别出他们的偏好。每个示例由独特的产品特征组合构成。数据可以包括单独的评分、排名,或从不同组合中进行选择。
联合设计包含四个不同的步骤:
-
确定研究类型
-
确定相关特征:
-
与管理决策相关
-
在现实中有不同的层级
-
预计会影响偏好
-
易于理解并清晰定义
-
显示没有过度相关性(价格和品牌是例外)
-
至少应存在两个层级
-
-
指定属性的层级:
-
明确无误
-
相互排斥
-
现实的
-
-
设计问卷:随着属性和层级组合数量的增加,备选方案的数量呈指数增长。应对指数增长的方式之一是采用分数因子设计方法,这种方法通常用于减少需要检查的资料数,同时确保有足够的数据用于统计分析。这反过来会产生一组控制良好的“资料”供受访者考虑。
在设计联合实验时考虑这些要点,将使我们能够准确地建模并复制我们希望了解的消费者行为模式。我们必须时刻记住,这项分析的目的是通过尽可能模拟实际购买行为,揭示隐藏的模式。
确定产品相关的属性
如前所述,我们将进行联合分析,衡量一组用户对某个产品或服务特征的重视程度。为此,我们将进行多变量分析,确定最优的产品概念。通过评估整个产品(总体效用值),可以计算出各个元素对购买决策的影响程度(部分效用值)。例如,当用户购买 PC 时,可以确定哪些因素影响这一决策,以及影响的程度(重要性)。同样的方法也可以扩展到更多特征。
要使用的数据以不同的笔记本电脑特征组合形式呈现,涉及 RAM、存储和价格。不同的用户对这些组合进行排序。
我们将在下一个示例中使用以下 Python 模块:
-
Pandas:Python 包,用于数据分析和数据操作。
-
NumPy:Python 包,允许使用矩阵和数组,并可以使用数学和统计函数在这些矩阵上进行操作。
-
Statsmodels:Python 包,提供 SciPy 在统计计算方面的补充,包括描述性统计以及统计模型的估计和推断。它提供了用于估计多种不同统计模型的类和函数。
-
Seaborn 和 Matplotlib:Python 包,用于有效的数据可视化。
-
接下来的代码块将导入必要的包和函数,并创建一个带有模拟数据的示例 DataFrame:
import numpy as npimport pandas as pdimport seaborn as snsimport matplotlib.pyplot as pltimport statsmodels.api as sm# data: Laptop spec datadata = pd.DataFrame([[6000, '4GB', '128GB', 3],[6000, '8GB', '512GB', 9],[8000, '4GB', '512GB', 5],[8000, '8GB', '128GB', 7],[6000, '4GB', '128GB', 4]],columns=['price', 'memory','storage', 'score'])data.head()
这将产生以下输出:

图 4.3:产品特征及其评分
-
接下来的代码将把我们的数据分离成预测变量和目标变量:
X = data[[col for col in data.columns if col != 'score']]y = data['score']X.head()
这将产生以下输出:

图 4.4:产品特征
-
以下代码行将使用编码的分类变量创建虚拟变量:
X_dum = pd.get_dummies(X, columns=X.columns)X_dum.head()
这将产生以下输出:

图 4.5:产品及其特征的独热编码表示
现在,信息已经被正确编码,我们可以使用不同的预测模型来尝试预测,根据产品特征,判断每个产品的评分。在下一节中,我们可以使用 OLS 回归模型来确定变量的重要性,并推断出用户在选择购买产品时最看重的特征。
使用 Python 和 Statsmodels 的 OLS 方法
OLS,一种线性最小二乘法方法,在统计学中用于估计线性回归模型中未确定的参数。通过最小化因变量的观测值与通过自变量的线性函数预测的值之间的差异的平方和,OLS 根据最小二乘原则,从一组解释变量中推导出线性函数的参数。
提醒一下,线性回归模型建立了一个因变量(y)和至少一个自变量(x)之间的关系,公式如下:

在 OLS 方法中,我们必须选择
和
的值,使得计算值与观测值之间的差异平方和最小化。
OLS 可以用几何学的术语来描述,即所有数据点到回归面之间的平方距离之和。这个距离是平行于调整面轴线的,距离越小,面就越能更好地拟合数据。当误差是同方差且彼此不相关时,OLS 是一种特别有用的方法,当回归中使用的变量是外生的时,效果尤为显著。
当误差具有有限方差时,OLS 方法提供一个最小方差的无偏估计。OLS 在额外假设误差呈正态分布的前提下,是最大似然估计器。
我们将使用 Python 的 statsmodels 模块来实现 OLS 线性回归方法:
model = sm.OLS(y, sm.add_constant(X_dum))
result = model.fit()
result.summary()
OLS 回归过程用于估计多元数据中的线性关系,得到了一个 R 平方值为 0.978:

图 4.6:OLS 模型摘要
结果摘要显示了我们一些基本信息,以及相关的指标。其中一些包括 R 平方值、用于训练模型的观测数、模型的自由度、协方差及其他信息。
在这个摘要中需要解释的一些最重要的方面是以下值:
-
'score'变量。由于模型的 R 平方值随着添加的变量不会下降,即使它们只贡献了很少的部分,模型也可能看起来更准确。这个属性在分析多个自变量对模型的有效性时非常重要。较低的调整 R 平方值可能表明某些变量没有充分地贡献到模型中,因为调整后的 R 平方会根据变量的数量惩罚 R 平方公式。 -
null假设,或者说是否准确地表述变量的效应为零。你可以看到,这种情况的概率是 15.3%。 -
截距:如果我们模型中的所有变量都设置为 0,那么截距就是结果。这是我们的 b,一个常数,用来声明经典线性方程“y = mx + b”中的起始值。这些是交点下的变量。系数是我们表格中的第一个有用列。它是我们部分的值,它衡量了每个变量变化对自变量的影响。“y = mx + b”中的“m”就是罪魁祸首,其中“m”是变量系数在自变量中的变化率,或者是因变量单位变化的结果。如果系数是负的,它们之间存在反向关系,这意味着一个变量上升时,另一个变量会下降。
-
std error:系数的标准差,或者说系数在数据点之间的变化程度,是通过 std error(或标准误差)变量来估计的,它衡量了系数测量的准确性,并与之相关。在 t 统计量较高的情况下,这意味着系数的重要性较高,这通常是由于标准误差相对较低而系数较高所产生的。
-
P>|t|:摘要中的一个最重要的统计量是 p 值。t 统计量用于生成 p 值,它表示在我们的模型中,系数由随机因素决定的可能性。一个低 p 值(如 0.278)表示有 27.8% 的概率表明所提供的变量对因变量没有影响,并且我们的结果是偶然的。p 值将与一个预先设定的 alpha 值(或阈值)进行比较,以便我们能够为我们的系数附加显著性,在适当的模型分析中进行检验。
-
[0.025 和 0.975]:是我们的系数值在 95% 数据范围内或两倍标准差范围内的测量值吗?超出这些范围的值通常可以视为离群值。数据是否包含在两个标准差之间,超出这个范围的数据可以被视为离群值?
-
Omnibus:使用偏度和峰度作为度量,Omnibus 描述了残差分布的正态性。0 表示完全正态。一个叫做 Prob (Omnibus) 的统计检验用于确定残差是否符合正态分布的可能性。1 表示完全正态的分布。偏度的范围是从 0 到完美对称,衡量数据的对称程度。峰度衡量数据的尖峭程度,或者它在正态曲线上的集中程度。较高的峰度意味着较少的离群值。
-
Durbin-Watson:数据中的同方差性,即误差的均匀分布,是通过 Durbin-Watson 统计量来衡量的。异方差性则表示不均匀分布,例如当数据点增多时,相对误差也增大。理想的同方差性应该在 1 和 2 之间。使用偏度和峰度来测量相同的 Omnibus 和 Prob (Omnibus) 值的替代方法是 Jarque-Bera (JB) 和 Prob。这些理想方法帮助我们相互验证。衡量我们的模型对所处理数据变化的敏感度的指标是条件数。许多不同的条件强烈暗示多重共线性,这是一个用来描述两个或多个自变量彼此强相关,并且通过冗余错误地影响我们预测变量的术语。
在我们的案例中,我们将使用数据的结果,并将变量名称、权重和 p 值存储在 DataFrame 中,稍后我们将用它来绘制数据:
data_res = pd.DataFrame({'name': result.params.keys(),
'weight': result.params.values,
'p_val': result.pvalues})
data_res = data_res[1:]
data_res
查看 p 值时,如果显著性水平低于 5%,这可能表明该变量在统计学上不显著:
data_res = data_res.sort_values(by='weight')
data_res
这将产生以下输出:

图 4.7:产品特征的权重和 p 值
虽然考虑 p 值以建立统计显著性水平很重要,但模型应该包含所有变量。在这里,我们可以使用 Prob (F 统计量),在我们的案例中该值大于 0.05,因此我们无法拒绝原假设。通过查看一个非常高的 R 平方值,我们可以看到模型存在过拟合。我们需要更多的数据来创建一个显著的模型,但这个挑战我会留给你。
下一个截图显示了按相对权重排序的产品特征。在这种情况下,用户对 8GB 内存的评价较高,其次是 128GB 存储:
sns.set()
xbar = np.arange(len(data_res['weight']))
plt.barh(xbar, data_res['weight'])
plt.yticks(xbar, labels=data_res['name'])
plt.xlabel('weight')
plt.show()
这将产生以下输出:

图 4.8:按权重排序的产品特征
可以看出,内存在评价中的贡献最大,其次是存储。
我们已经看到 OLS 如何作为一种手段来估计每个产品特征的重要性。在这种情况下,它被建模为对单一变量的回归,但我们可以包括有关响应者类型的信息,以发现不同的客户群体如何对产品特征做出反应。
在下一部分,我们评估一个具有更多特征的消费品案例。
使用更多的产品特征
在这个示例中,我们将使用一个包含比前一个示例更多特征的数据集。在这种情况下,我们将模拟来自一家脆片零售商的数据,该零售商已要求一些客户根据他们的偏好程度对其产品进行排名:
-
以下代码块将读取数据集,该数据集为 CSV 文件,并将提示我们显示结果:
# Load dataconjoint_dat = pd.read_csv('/content/conjoint_data.csv')conjoint_dat
这将产生以下输出:

图 4.9:脆片数据
-
我们可以看到数据仅包含分类值,因此需要使用
get_dummiespandas 函数将这些分类数据转换为独热编码向量表示,这就是我们在下一个代码块中所做的:conjoint_dat_dum = pd.get_dummies(conjoint_dat.iloc[:,:-1], columns = conjoint_dat.iloc[:,:-1].columns)conjoint_dat_dum
我们可以看到现在我们已经创建了一组列,这些列用 1 和 0 描述了产品特征:

图 4.10:脆片数据的独热编码表示
-
我们现在可以构建一个 OLS 模型,使用产品特征的独热编码向量表示,并将排名作为目标变量:
main_effects_model_fit = sm.OLS(conjoint_dat['ranking'].astype(int), sm.add_constant(conjoint_dat_dum))result = main_effects_model_fit.fit()result.summary()
这将产生以下输出:

图 4.11:OLS 回归结果
模型摘要中的AIC和BIC术语,分别代表赤池信息准则和贝叶斯信息准则,是模型选择标准中常用的术语;然而,它们是不可互换的。BIC 是一个从包含变量参数数量不等的参数模型类中选择模型的方法,而 AIC 可以被看作是估计统计模型拟合优度的度量。BIC 对额外参数的惩罚比 AIC 更大。BIC 对自由参数的惩罚比 AIC 更严厉。
AIC 通常会寻找未发现的具有高维实际情况的模型。这表明 AIC 模型并不准确。另一方面,BIC 仅会遇到True模型。此外,BIC 是一致的,尽管 AIC 可能不一致,可以这么说。BIC 会指示出可能的欠拟合风险,而 AIC 则更适合用来检查模型是否存在过度拟合的风险。尽管 BIC 比 AIC 更宽容,但随着数量的增加,它变得不那么宽容。通过 AIC,交叉验证可以渐近相等。另一方面,BIC 对于精确估计非常有用。
在比较 BIC 和 AIC 时,BIC 对额外参数的惩罚比 AIC 更高。AIC 通常会寻找一个具有高维实际情况的未识别模型。另一方面,BIC 仅会找到True模型。AIC 是不一致的,而 BIC 是一致的。尽管 BIC 比 AIC 更宽容,但随着数量的增加,它变得不那么宽容。BIC 对自由参数的惩罚比 AIC 更严厉。
-
接下来的代码将创建一个数据框,用于存储来自分析的最重要的值,在此情况下为权重和 p 值:
data_res = pd.DataFrame({'name': result.params.keys(),'weight': result.params.values,'p_val': result.pvalues})data_res = data_res[1:]data_res
这将产生以下输出:

图 4.12:OLS 变量权重和 p 值
-
现在我们已经排列好了这些值,我们可以检查每一个关系的显著性,以检查假设的有效性并可视化权重:
xbar = np.arange(len(data_res['weight']))plt.barh(xbar, data_res['weight'])plt.yticks(xbar, labels=data_res['name'])plt.xlabel('weight')plt.show()
这将产生以下输出
:

图 4.13:按重要性排序的变量权重
在这种情况下,可以看出有一些因素是正相关的,比如 100 克的重量和产品为无脂肪的选项,而其他因素是负相关的,比如 400 克的重量,接着是产品不是无脂肪的。
可能出现的一个问题是:如果我们有一个回归模型来估计产品得分,能否使用同一个模型预测客户如何对新产品组合做出反应? 从理论上讲,我们可以,但在这种情况下,我们必须使用评分系统而非排名,并且需要更多更多的数据。为了演示的目的,我们将继续进行,因为这些数据难以获得,甚至更难披露。
预测新特征组合
现在我们已经正确训练了预测模型,除了捕获产品特征的重要性外,我们还可以利用它为我们提供关于新产品特征表现的信息:
-
在完成 EDA(探索性数据分析)之后,我们将开发一些预测模型并进行比较。我们将使用已创建虚拟变量的数据框架,并将所有变量缩放到 0 到 1 的范围内:
from sklearn.preprocessing import StandardScalerX = conjoint_dat_dumy = conjoint_dat['ranking'].astype(int)# The target variable will be normalized from a ranking to a 1 to 10 scorey = y.apply(lambda x: int(x/len(y)*10))features = X.columns.valuesscaler = StandardScaler()scaler.fit(X)X = pd.DataFrame(scaler.transform(X))X.columns = features
这将导致以下输出:

图 4.14:标准化变量
最受欢迎的机器学习模型之一是逻辑回归,它是一种利用自变量进行预测的算法。该算法可以用于分类和回归任务。它是一种监督学习算法,需要数据标注。该算法利用示例答案来拟合模型,以达到目标变量,在我们这里就是产品排名。从数学角度来看,该模型旨在根据一组自变量 X 预测 Y。逻辑回归可以分为二元逻辑回归和多项式逻辑回归。这两者的特点可以描述如下:
-
二元:所有类型的逻辑回归中使用最广泛的是二元逻辑回归,算法旨在区分 0 和 1,这一任务被视为分类问题。
-
多项式:当目标或自变量具有三个或更多潜在值时,使用多项式逻辑回归。例如,利用胸部 X 光的特征可以指示三种可能的结果之一(无病、肺炎或纤维化)。在这种情况下,示例根据特征被排序成三种可能的结果之一,使用的是多项式逻辑回归。当然,目标变量可以有超过三个可能的值。
-
有序逻辑回归:如果目标变量本质上是有序的,则使用有序逻辑回归。在这种类型中,每个类别都有定量意义,并且具有明显的顺序。此外,目标变量包含的不仅仅是两个类别。例如,考试结果根据定量标准进行分类和排序。简单来说,成绩可以是 A、B 或 C。
-
线性回归和逻辑回归的主要区别在于,逻辑回归用于解决分类问题,而线性回归用于解决回归问题。回归问题中的目标变量可能具有连续值,例如产品价格或参与者年龄,而分类问题则涉及预测仅具有离散值的目标变量,例如确定一个人的性别或肿瘤是良性还是恶性:
# Logistic Regressionfrom sklearn.model_selection import train_test_splitfrom sklearn.linear_model import LogisticRegressionfrom sklearn import metricsX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=101)# Running logistic regression modelmodel = LogisticRegression()result = model.fit(X_train, y_train)
通过将训练集特征及其匹配的目标类别值输入模型,模型可以被训练。这将帮助模型学习如何对新案例进行分类。评估模型在尚未遇到的实例上的表现至关重要,因为只有当它能够准确分类那些不属于训练集的示例时,模型才会有用。
误差平方的平均值由真实值来衡量。
-
MSE 始终为正,尽管如果预测完全准确,MSE 可以为 0。它包括估计器的方差(估计值的分布范围)和偏差(估计值与实际值之间的差异):
prediction_test = model.predict(X_test)# Print the prediction accuracyprint(metrics.mean_squared_error(y_test, prediction_test))
这将产生以下输出:

图 4.15:简单回归的 MSE
-
MSE 始终为 0 或正值。如果 MSE 很大,这意味着线性回归模型预测不准确。重要的一点是,MSE 对异常值很敏感。这是因为每个数据点的误差是平均的。因此,如果异常值的误差很大,MSE 将被放大。MSE 没有“目标”值。然而,MSE 是衡量模型如何拟合数据的良好指标。它还可以帮助你判断是否更倾向于选择一个模型而非另一个:
# To get the weights of all the variablesweights = pd.Series(model.coef_[0],index=X.columns.values)print(weights.sort_values(ascending = False)[:10].plot(kind='bar'))
这将产生以下输出:

图 4.16:线性回归顶级变量贡献
在这里,我们可以看到模型识别为与得分正相关的前 10 个变量。
-
接下来的代码将显示那些与结果负相关的变量:
print(weights.sort_values(ascending = False)[-10:].plot(kind='bar'))
这将产生以下输出:

图 4.17:线性回归负向变量贡献
随机森林是一种监督学习(SL)算法。它既可以用于分类,也可以用于回归。此外,它是最具适应性和用户友好的算法。森林中有很多树。森林中的树木越多,它应该越强大。在随机选择的数据样本上,随机森林生成决策树,从每棵树中获取预测结果,然后投票选出最佳选项。它们还提供了特征重要性的相当准确的指示。
随机森林的应用包括特征选择、图片分类和推荐引擎。它们可以用于分类可靠的贷款候选人、发现欺诈行为和预测疾病。
随机森林是一种元估计器,它通过采用均值来提高预测准确性并减少过拟合。它在多个数据集子样本上拟合多个决策树分类器。如果bootstrap = True(默认设置),子样本的大小由max_samples参数指定;否则,每棵树都是使用完整的数据集构建的。在随机森林中,集成中的每棵树都是使用从训练集中提取的样本(即,带有替代的样本)构建的。此外,在构建树时,每次拆分节点时,都会发现所有输入特征或任何max_features大小子集的最佳分割。
这两种随机源用于降低森林估计器的方差。实际上,单个决策树常常过拟合并具有较大的方差。通过将随机性引入森林,会生成具有部分不相关预测误差的决策树。通过对这些预测进行平均,可以帮助某些误差消失。通过合并多棵树,随机森林减少了方差,有时会稍微增加一些失真。实际上,方差减少通常较大,从而产生一个更强大的整体模型。
scikit-learn 的实现并不是让每个分类器选择单一类别,而是通过对其概率预测取平均来组合多个分类器:
from sklearn.ensemble import RandomForestClassifier
model_rf = RandomForestClassifier(n_estimators=1000 , oob_score = True, n_jobs = -1,random_state =50, max_features = "auto",max_leaf_nodes = 30)
model_rf.fit(X_train, y_train)
# Make predictions
prediction_test = model_rf.predict(X_test)
print(metrics.mean_squared_error(y_test, prediction_test))
这将产生以下输出:

图 4.18:随机森林均方误差
机器学习模型的一个可选算法是随机森林算法,该算法在 scikit-learn 包中实现,包括RandomForestRegressor和RandomForestClassifier类。这些类可以被拟合到数据中,从而得到一个模型,该模型随后可以用来进行新预测或获取关于特征重要性的信息,这是联合分析的重要核心,如以下示例所示:
importances = model_rf.feature_importances_
weights = pd.Series(importances, index=X.columns.values)
weights.sort_values()[-10:].plot(kind = 'barh')
这将产生以下输出:

图 4.19:随机森林特征重要性
XGBoost 库是一个高效、适应性强且便于分布式梯度增强的库。它在梯度提升框架内应用机器学习技术,并提供并行树增强(也称为 GBDT 和 GBM),能够快速准确地解决各种数据科学问题。相同的代码可以处理涉及超过万亿个样本的问题,并在大规模分布式环境中运行。
它是最近在多个机器学习竞赛中获胜算法的主要推动力。由于其无与伦比的速度和性能,它在监督学习任务中经常超过所有其他算法。
主要算法可以在 GPU 集群或甚至 PC 网络上运行,因为该库是可并行化的。这使得在数亿训练实例上进行训练并以高性能解决机器学习任务成为可能。
在赢得一项重要的物理学竞赛后,它迅速被机器学习社区所接受,尽管最初是用 C++编写的:
from xgboost import XGBClassifier
model = XGBClassifier()
model.fit(X_train, y_train)
preds = model.predict(X_test)
metrics.mean_squared_error(y_test, preds)
这产生了以下输出:

图 4.20:随机森林 MSE
我们可以看到,随机森林的均方误差(MSE)甚至大于线性回归。这是因为数据不足以获得更好的得分,所以在接下来的步骤中,我们需要考虑增加数据点的数量来进行分析。
摘要
在本章中,我们学习了如何进行联合分析,这是一种统计工具,可以帮助我们揭示消费者偏好,这些偏好通常难以确定。我们进行分析的方式是使用 OLS 估算不同特征组合的表现,并尝试隔离每个可能配置对客户总体感知的影响,以揭示消费者认为的价值所在。
这使我们能够创建一个概述,了解驱动用户购买产品的因素,甚至能够通过使用机器学习算法预测新特征组合的表现。
在下一章中,我们将通过研究价格变化与销售数量之间的关系,使用价格弹性来学习如何调整商品价格。
第五章:使用价格需求弹性选择最佳价格
需求的价格弹性衡量的是产品消费量对价格变化的响应程度。如果价格调整导致供给或需求发生显著变化,则商品是有弹性的。如果商品的价格调整对需求或供给没有显著影响,则其为无弹性。产品的弹性受替代品可获得性的影响。如果该产品是必需品且没有合适的替代品,价格上涨时需求不会改变,从而使其具有无弹性。
在本章中,我们将学习以下内容:
-
什么是价格弹性,如何利用它来最大化收入?
-
探索数据以确定定价模式及其周围的消费者行为
-
确定不同产品的需求曲线
-
优化价格以最大化所有商品的收入
在这种情况下,我们将使用餐车销售数据来分析不同定价策略的影响。
技术要求
为了能够跟随本章的步骤,你需要满足以下要求:
-
一个运行 Python 3.7 或更高版本的 Jupyter Notebook 实例。如果你有 Google Drive 账户,也可以使用 Google Colab 笔记本来运行这些步骤。
-
理解基本的数学和统计学概念。
理解价格需求弹性
价格弹性概念用于解释销售的商品数量对价格上涨或下跌的响应程度。这对于需要预测价格变动如何影响公司财务的经理来说,具有重要价值。
从数学角度来看,其表达式如下:

这里,每个术语代表以下内容:
-
:价格弹性 -
Q:需求商品的数量
-
:需求商品数量的变化 -
P:需求商品的价格
-
:需求商品价格的变化
价格弹性是衡量需求量对价格变化敏感度的指标,几乎所有商品在价格上涨时需求都会下降,但某些商品的需求下降幅度更大。价格弹性在保持其他因素不变的情况下,衡量价格上涨 1%时需求量的百分比变化。当弹性为-2 时,价格每上涨 1%,需求量就会下降 2%。在某些特定情况下,价格弹性为负数。当商品的弹性为 2 时,几乎可以肯定地表明该弹性的正式定义为-2。
更多弹性(more elastic)意味着商品的弹性更大,无论符号如何。需求法则的两个不常见例外,费布伦商品和吉芬商品,是两类具有正弹性的商品。当商品的需求的弹性绝对值小于 1 时,需求被认为是缺乏弹性的,意味着价格变化对需求量的影响相对较小。如果商品的需求弹性大于 1,则被认为是有弹性的。一种需求弹性为-2 的商品,其数量下降是价格上涨的两倍,而需求弹性为-0.5 的商品,其数量下降仅为价格上涨的一半。
当价格选择使得弹性恰好为 1 时,收入最大化。税收对商品的影响(或“负担”)可以通过商品的弹性来预测。为了确定价格弹性,采用了多种研究技术,如测试市场、历史销售数据分析和联合分析。
探索数据
实验的初始数据源是位于办公楼外的食品卡车销售情况。由于成本压力,审查过高的价格至关重要。食品卡车的经营者必须了解价格上涨如何影响店内汉堡的需求。换句话说,要确定价格能够提高多少,了解汉堡的价格弹性至关重要。实际上,价格弹性衡量的是产品价格对需求的影响程度。
我们将在下一个示例中使用以下 Python 模块:
-
Pandas:一个用于数据分析和数据处理的 Python 包。
-
NumPy:这是一个支持大型多维数组和矩阵的库,并提供大量高层次的数学函数来操作这些数组。
-
statsmodels:一个 Python 包,提供对 SciPy 的补充,用于统计计算,包括描述性统计和统计模型的估计与推断。它提供了用于估计多种不同统计模型的类和函数。它是一个用于统计方法和描述性统计的 Python 包,以及其他模型的统计模型。
-
Seaborn 和 Matplotlib:用于有效数据可视化的 Python 包。
-
以下代码块将加载所有所需的包,加载数据并显示数据的前五行:
import pandas as pdimport numpy as npimport statsmodels.api as smfrom statsmodels.formula.api import olsimport matplotlib.pyplot as pltimport seaborn as sns; sns.set(style="ticks",color_codes=True)data = pd.read_csv('foodcar_data.csv',parse_dates=['DATE'])data.head()
这将产生下一个数据框,展示了包含食品卡车销售的商品交易数据以及其他一些外部变量的数据:

图 5.1:待分析的食品卡车销售数据
数据包含以下变量:
-
SELLER:销售商品的标识符
-
CAT:表示商品是单独销售(0)还是作为组合的一部分销售(2)的变量
-
ITEM_ID:售出的物品标识符
-
ITEM_NAME:物品的全名
-
DATE:物品销售的时间
-
YEAR:从日期中提取的年份
-
HOLIDAY:布尔变量,指示当天是否为节假日
-
WEEKEND:布尔变量,指示是否为周末
-
SCHOOLBREAK:布尔变量,指示是否为学校假期
-
AVG_TEMPERATURE:当天的温度(华氏度)
-
在下一个命令中,我们将进行描述性统计分析,之前已经删除了ITEM_ID列,因为尽管它被理解为一个数值变量,但它代表的是一个分类维度。
data.drop(['ITEM_ID'],axis=1).describe()
这将产生以下输出:

图 5.2:数据的统计描述摘要
-
我们感兴趣的一件事是,我们将在数据中找到的不同类型的产品。在这种情况下,我们将只选择SELLER、ITEM_ID和ITEM_NAME数据,并删除重复项:
d = data[['SELLER','ITEM_ID','ITEM_NAME']].drop_duplicates()print(d)
这将产生以下输出:

图 5.3:正在出售的产品
-
通过将这些数据转换为虚拟变量,我们可以看到,卖家列实际上展示了不同的产品组合,其中一些是单独出售的,而另一些是作为组合出售的,例如4104,它是一个汉堡和一瓶水:
pd.concat([d.SELLER, pd.get_dummies(d.ITEM_NAME)], axis=1).groupby(d.SELLER).sum().reset_index(drop=True)
这将产生以下输出:

图 5.4:产品组合销售
-
我们开始探索数据的一种方式是运行配对图。这个 Seaborn 图可以让我们看到值的分布以及变量之间的关系。如果我们想在第一眼看到它们之间是否存在关系,它非常有用:
sns.pairplot(data[['PRICE','QUANTITY']],height=5,aspect=1.2)
这将产生以下输出:

图 5.5:价格与数量的配对图
一眼看去,我们可以看到价格与数量之间有许多重叠的趋势。我们还可以看到两个非常明显的组,一个位于左下象限,另一个位于右上象限。
-
我们可以使用 Seaborn 直方图绘图并将
CAT变量作为色调,来探讨价格与CAT变量之间的关系,进一步分析这些差异。下一段代码正是做了这一点,首先创建了一个matplotlib图形,然后用定义好的直方图填充它。这对于某些需要此设置来正确定义图形大小的 Seaborn 图非常有用:f, ax = plt.subplots(figsize=(10, 6))fig = sns.histplot(x='PRICE',data=data,hue='CAT',palette=['red','blue'])
这将产生以下输出:

图 5.6:按 CAT 区分的价格直方图
从前面的图表中我们可以看到,CAT值为2的产品价格比其他产品更高。
-
这些是作为套餐出售的产品,因此这是一个合理的结果。我们可以通过运行新的散点图进一步探索这些价格差异——这次,我们将寻找
PRICE、QUANTITY和ITEM_NAME之间的关系:sns.pairplot(data[['PRICE','QUANTITY','ITEM_NAME']], hue = 'ITEM_NAME', plot_kws={'alpha':0.1},height=5,aspect=1.2)
这将产生以下输出:

图 5.7:价格、数量和商品名称的散点图
我们现在可以更详细地看到每个商品的价格差异。在这个案例中,汉堡是价格最高的,而水是最便宜的(这很合理)。有趣的是,对于相同的商品,我们看到不同的价格,并且销量也有所不同。
我们可以得出结论,必须通过对每个商品进行明确区分来分析价格差异,因为我们很可能会对每个商品有不同的需求弹性和最优定价。
-
下一个代码块将创建一个
10x6英寸的 Matplotlib 图形,并填充一个 Seaborn 直方图,展示每个类别中商品名称的分布:f, ax = plt.subplots(figsize=(10, 6))fig = sns.histplot(x='ITEM_NAME',data=data,hue='CAT', palette=['red','blue'])
这将产生以下输出:

图 5.8:每个类别的商品名称直方图
从这个直方图中,我们可以看到汉堡是唯一属于 0 类别的商品,因此它是单独销售的。这意味着可乐、水和咖啡与汉堡一起作为套餐销售。这是有用的信息,它展示了食品车老板的销售方式,从而帮助我们思考更好的定价或产品组合方式,以提供给客户。
-
下一个代码块将过滤数据,仅包含
CAT=2的商品,并创建一个 Seaborn 关系图来解释价格与销量之间的关系:d = data[data['CAT']==2]sns.relplot(x=d['PRICE'],y=d['QUANTITY'],height=7,aspect=1.2)

图 5.9:CAT = 2 商品的价格与质量关系
在这里显示的关系图中,我们可以看到属于第二类商品(即作为套餐出售的商品)之间价格和质量的关系。除了那些价格明显高于其他数据点的离群值外,大多数商品的价格都处于某个范围内。虽然我们同时观察了多个商品,但通过意识到价格较低的商品售出数量较多,我们仍然可以看到一定的关系。接下来,我们的任务是深入挖掘这些关系的具体细节,以便能够确定最大化收益的价格。
-
在下一个代码块中,我们将尝试捕捉价格和销量随时间变化的情况。我们将创建一个新的数据集,并以标准化的方式查看价格和数量,通过减去均值并除以范围:
d = data[['DATE','PRICE','QUANTITY']].sort_values( ['DATE'], ascending=True)d['PRICE'] = (d['PRICE'] - d['PRICE'].mean())/((d['PRICE'].max() - d['PRICE'].min()))d['QUANTITY'] = (d['QUANTITY'] - d['QUANTITY'].mean())/((d['QUANTITY'].max() - d['QUANTITY'].min())) -
一旦我们将价格和数量标准化到-1 到 1 的范围内,我们将把
d['DATE']设置为索引,最后应用滚动平均来平滑曲线,并使用 pandas DataFrame 对象的绘图方法:d.index = d['DATE']d = d.drop(['DATE'],axis=1)d.rolling(window=60).mean().plot(figsize=(20,8))

图 5.10:价格与质量关系随时间变化
有趣的是,降价始终会导致销售数量的增加。在这种情况下,我们可以看到最大幅度的降价发生在 2020 年上半年,这导致了销售数量的增加。我们需要问的问题是,这次降价所丧失的收入是否已经通过销量的增加得到了弥补。
-
在下一个代码块中,我们将深入探讨不同变量与价格之间的相关性。为此,我们将使用 pandas DataFrame 的
corr()方法,以及 NumPy 库来创建一个掩码,以“遮盖”对角线以上的重复值,还将使用matplotlib库创建一个12x12英寸的图形,并用 Seaborn 热图进行填充:import numpy as npdf_corr = data.drop(['DATE','SELLER','STORE'],axis=1).corr()mask = np.triu(np.ones_like(df_corr, dtype=bool))df_corr = df_corr.mask(mask).round(3)fig, ax = plt.subplots(figsize=(12,12))sns.heatmap(df_corr, annot=True,ax=ax)

图 5.11:变量之间的相关性
这里展示的数据给了我们一个关于变量如何相关的概念。我们可以看到价格与温度之间存在负相关关系(也许在天气炎热时人们不太愿意买咖啡和汉堡组合),与学校假期之间有正相关关系(也许孩子们在这些日子里也会购买产品),而与周末之间存在负相关关系,这可能表明食品车的位置在周末的流量较少。
请谨慎看待这些结论,因为我们需要逐项逐案例分析来验证这些假设,并始终记住最大原则:相关性不代表因果关系。
现在我们已经探索了数据,以理解看似相同的商品之间的价格差异,以及了解这种分布的样子,我们可以尝试估算需求曲线。了解需求曲线可以帮助我们建立价格和销售数量之间的关系,这也是我们将在下一节中做的事情。
寻找需求曲线
经济学中的需求曲线是一个图表,描绘了某种商品的价格与在该价格下所需数量之间的关系。个别需求曲线用于描述个别消费者之间的价格与数量交互,而市场需求曲线则用于描述所有消费者(即市场需求曲线)。
一般认为需求曲线向下倾斜是由于需求法则的作用。对于大多数商品来说,价格上升时需求下降。但在一些特殊情况下,这一法则并不适用。这些情况包括投机泡沫、费布伦商品和吉芬商品,当价格上涨时,消费者反而更倾向于购买这些商品。
需求曲线与供给曲线结合使用,以确定均衡价格。在这一理想点,买卖双方已经达成了对商品或服务实际价值的共识,从而使我们能够生产出足够的商品以满足需求,而不会出现短缺或过剩。
在代码中探索需求曲线
为了找到每个商品的需求曲线,首先,我们将每个商品的数据隔离到一个单独的数据框中:
-
在下一段代码中,我们为
burger_2752创建一个数据框,深入探讨这个特定商品的价格与需求关系,因为我们怀疑每个商品都有其特定的需求曲线:burger_2752 = data[data['ITEM_ID']==2752].drop(['ITEM_ID','ITEM_NAME'],axis=1)burger_2752.head()

图 5.12:汉堡数据
-
一旦我们隔离了数据,我们可以开始确定价格与数据之间的关系。为此,我们将使用
statsmodels包,特别是将"QUANTITY ~ PRICE"作为 OLS 函数的参数。这样,OLS 函数将Quantity视为因变量,Price视为自变量。我们也可以传入其他变量作为因变量,但目前我们只关注Price:model = ols("QUANTITY ~ PRICE", burger_2752).fit() -
一旦模型正确拟合数据,我们可以打印出给定商品的关系斜率,
价格弹性,以及 OLS 模型的其他参数:price_elasticity = model.params[1]print("Price elasticity of the product: " + str(price_elasticity))print(model.summary())

图 5.13:汉堡 OLS 模型总结
分析模型表现的一种方法是查看截距和误差的回归图。这些散点图帮助我们理解线性回归中因变量和自变量之间的关系。
-
下一段代码将创建一个尺寸为
12x8英寸的 Matplotlib 图形,并使用statsmodels创建模型的偏回归图:fig = plt.figure(figsize=(12,8))fig = sm.graphics.plot_partregress_grid(model, fig=fig)

图 5.14:汉堡 OLS 模型偏回归图
-
我们分析的下一步是创建一个函数来打包模型的创建,确定价格弹性,并返回价格弹性和模型本身。该函数以每个商品的数据作为参数:
def create_model_and_find_elasticity(data):model = ols("QUANTITY ~ PRICE", data).fit()price_elasticity = model.params[1]print("Price elasticity of the product: " + str(price_elasticity))print(model.summary())fig = plt.figure(figsize=(12,8))fig = sm.graphics.plot_partregress_grid(model, fig=fig)return price_elasticity, model -
现在我们已经定义了函数,我们将创建两个字典来存储结果:一个用于弹性,另一个用于模型本身。之后,我们将遍历数据中所有独特的项目,将函数应用于数据子集,最后存储每个项目的结果:
elasticities = {}models = {}for item_id in data['ITEM_ID'].unique():print('item_id',item_id)price_elasticity, item_model =create_model_and_find_elasticity(data[data['ITEM_ID']==item_id])elasticities[item_id] = price_elasticitymodels[item_id]= item_model -
运行所有唯一项之后,我们可以打印每个项目的弹性结果。

图 5.15: 产品项目弹性
现在我们已经确定了每个项目的价格弹性,我们可以模拟不同的可能价格,并找到能够最大化收入的点。
使用需求曲线优化收入
一旦我们建立了价格与数量之间的关系,就可以模拟每一个可能价格的收入。为此,我们将找到每个项目的最低和最高价格,设定一个阈值,创建可能价格的区间,并使用存储的模型来预测销售数量。下一步是通过将价格与数量相乘来确定总收入。需要注意的是,在这种分析中,通常最好查看收入而非利润,因为大多数时候我们没有每个项目的成本数据。我们将通过以下步骤探索如何进行:
-
下一个代码块将获取
burger_2752的数据,确定上下价格边界,使用 NumPy 的范围创建一个区间,最后,使用训练好的模型预测销售数量,从而预测收入:start_price = burger_2752.PRICE.min() - 1end_price = burger_2752.PRICE.max() + 10test = pd.DataFrame(columns = ["PRICE", "QUANTITY"])test['PRICE'] = np.arange(start_price, end_price,0.01)test['QUANTITY'] = models[2752].predict(test['PRICE'])test['REVENUE'] = test["PRICE"] * test["QUANTITY"]test.head()

图 5.16: 每个价格下预测的收入的前几行
-
为了能够直观地呈现变量之间的关系,无论度量单位如何,我们将通过减去均值并除以范围来标准化数据变量。最后,我们将使用绘图方法来可视化数据:
test['PRICE'] = (test['PRICE']-test['PRICE'].mean())/(test['PRICE'].max()-test['PRICE'].min())test['QUANTITY'] = (test['QUANTITY']-test['QUANTITY'].mean())/(test['QUANTITY'].max()-test['QUANTITY'].min())test['REVENUE'] = (test['REVENUE']-test['REVENUE'].mean())/(test['REVENUE'].max()-test['REVENUE'].min())test.plot(figsize=(12,8),title='Price Elasticity - Item 2752)

图 5.17: burger_2752 需求曲线
从需求曲线中我们可以看到,这个项目是非弹性的,意味着即使价格上涨,这种产品的销售仍然会继续增加。为了获得更清晰的图像,我们将对另一个项目重复相同的练习。
-
我们将使用
coffee数据并重复相同的练习来可视化需求曲线:coffee_3052 = data[data['ITEM_ID']==3052]start_price = coffee_3052.PRICE.min() - 1end_price = coffee_3052.PRICE.max() + 10test = pd.DataFrame(columns = ["PRICE", "QUANTITY"])test['PRICE'] = np.arange(start_price, end_price,0.01)test['QUANTITY'] = models[3052].predict(test['PRICE'])test['REVENUE'] = test["PRICE"] * test["QUANTITY"]test['PRICE'] = (test['PRICE']-test['PRICE'].mean())/(test['PRICE'].max()-test['PRICE'].min())test['QUANTITY'] = (test['QUANTITY']-test['QUANTITY'].mean())/(test['QUANTITY'].max()-test['QUANTITY'].min())test['REVENUE'] = (test['REVENUE']-test['REVENUE'].mean())/(test['REVENUE'].max()-test['REVENUE'].min())test.plot(figsize=(12,8),title='Price Elasticity - Item 3052')
通过运行之前的代码,我们可以看到需求曲线是凹形的,且弹性为负,这意味着如果价格上涨,销售的单位数会减少。虽然这会由于销售单位减少而导致收入下降,但也意味着由于价格上涨,收入会增加。

图 5.18: coffee_3052 需求曲线
- 现在,我们可以将这个过程转换为一个可以应用于每个项目数据的函数,从而获得需求曲线,并确定最优价格。
确定最优价格的方式非常简单。我们只需要在where语句中找到最大值,这样就能返回收入最高时的价格:
def find_optimal_price(data, model,item_id):
start_price = data.PRICE.min() - 1
end_price = data.PRICE.max() + 10
test = pd.DataFrame(columns = ["PRICE", "QUANTITY"])
test['PRICE'] = np.arange(start_price, end_price,0.01)
test['QUANTITY'] = model.predict(test['PRICE'])
test['REVENUE'] = test["PRICE"] * test["QUANTITY"]
test['P'] = (test['PRICE']-test['PRICE'].mean())/(test['PRICE'].max()-test['PRICE'].min())
test['Q'] = (test['QUANTITY']-test['QUANTITY'].mean())/(test['QUANTITY'].max()-test['QUANTITY'].min())
test['R'] = (test['REVENUE']-test['REVENUE'].mean())/(test['REVENUE'].max()-test['REVENUE'].min())
test[['P','Q','R']].plot(figsize=(12,8),title='Price Elasticity - Item'+str(item_id))
ind = np.where(test['REVENUE'] == test['REVENUE'].max())[0][0]
values_at_max_profit = test.drop(['P','Q','R'],axis=1).iloc[[ind]]
values_at_max_profit = {'PRICE':values_at_max_profit['PRICE'].values[0],'QUANTITY':values_at_max_profit['QUANTITY'].values[0],'REVENUE':values_at_max_profit['REVENUE'].values[0]}
return values_at_max_profit
-
现在我们已经有了确定最大利润的函数,我们可以计算所有项目的最优价格并将其存储在一个字典中:
optimal_price = {}for item_id in data['ITEM_ID'].unique():print('item_id',item_id)optimal_price[item_id] =find_optimal_price(data[data['ITEM_ID']==item_id],models[item_id],item_id)
在对数据中的所有项目运行此代码后,我们可以确定那些能够最大化食品卡车收入的参数。

图 5.19:item_6249 的需求曲线
我们可以看到,剩下的另外两个待分析的项目,其弹性也为负值,这意味着价格越高,销售的单位数量就越少。

图 5.20:item_4273 的需求曲线
-
现在我们已经得到了最优价格,我们可以打印出导致收入最大化的参数。接下来的代码块遍历了
optimal_price字典:for item_id in optimal_price:print(item_id,optimal_price[item_id])![图 5.21:每个项目的最优参数]()
图 5.21:每个项目的最优参数
通过这种方式,我们可以确定最优价格,无论项目的特性如何,以及客户愿意为每个项目支付的金额。
总结
在本章中,我们深入探讨了项目价格与销售数量之间的关系。我们研究了不同项目有不同的需求曲线,这意味着在大多数情况下,价格越高,售出的商品数量越少,但并非总是如此。价格与销售数量之间的关系可以通过价格弹性来建模,它能帮助我们了解价格上涨时,商品销售数量会减少多少。
我们查看了食品卡车的销售数据,旨在确定每个项目的最佳价格,并发现这些项目具有不同的弹性,对于每个项目,我们可以确定一个价格,该价格将最大化收入。
在下一章,我们将专注于改进我们捆绑和推荐产品的方式,通过分析市场购物篮来推荐经常一起购买的有意义的产品。
第六章:产品推荐
产品推荐本质上是一个过滤系统,旨在预测并展示用户可能感兴趣的商品。它用于生成推荐,保持用户对产品和服务的兴趣,并向他们提供相关建议。在本章中,我们将学习如何做到以下几点:
-
识别正在减少销售的客户
-
为尚未购买的产品向客户提供个性化产品建议
-
基于已购买产品创建具体的产品推荐,使用市场篮子分析和 Apriori 算法
让我们确定理解这些步骤并跟随本章的需求。
本章内容包括以下主题:
-
目标是识别回购量下降的客户
-
理解产品推荐系统
-
使用 Apriori 算法进行产品捆绑
技术要求
为了能够跟随本章的步骤,您需要满足以下要求:
-
需要运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果你有 Google Drive 账号,也可以使用 Google Colab Notebook 来运行这些步骤。
-
具备基本的数学和统计学概念。
目标是识别回购量下降的客户
企业的一个重要方面是,老客户的购买量总是超过新客户,因此密切关注老客户并在发现他们行为发生变化时采取行动是至关重要的。我们可以做的其中一项就是识别出购买模式下降的客户,并为他们推荐他们还没有购买的新产品。在这种情况下,我们将查看消费品配送中心的数据,识别这些购买量下降的客户:
-
首先,我们将导入必要的库,包括:用于数据处理的 pandas,处理缺失值和掩码的 NumPy,以及用于协同过滤产品推荐的 scikit-surprise。
-
我们将探索数据,确定合适的策略将数据标准化为正确的格式。
-
一旦数据结构化,我们将设置线性回归模型,确定具有负斜率的客户,以识别消费模式下降的客户。这些信息将帮助我们为这些客户制定具体的行动计划,避免客户流失。
让我们从以下步骤开始:
-
我们的第一步是加载这些包并安装
scikit-surprise包,用于协同过滤,这是一种根据类似用户的评分来过滤出用户可能喜欢的项目的方法。它通过将一小部分与特定用户口味相似的用户行为联系起来,为用户推荐产品:!pip install --upgrade openpyxl scikit-surpriseimport pandas as pdimport numpy as np -
为了提高可读性,我们将限制最多显示的行数为 20 行,设置最多显示 50 列,并将浮动数值显示为精确到 2 位小数:
pd.options.display.max_rows = 20pd.options.display.max_columns = 50pd.options.display.precision = 2 -
现在我们可以加载要分析的数据:
df = pd.read_csv('/content/distributed_products.csv')df.head()
在下图中,我们展示了按周期销售商品的历史销售情况,包括客户和产品的详细信息以及销售数量:

图 6.1:消费品交易数据
数据由不同客户的购买订单组成,涵盖不同的产品和不同的周期。数据中有一个周期列,记录了购买发生的年份和月份。
-
我们可以通过查看列列表继续探索数据:
df.columns.tolist()>>> ['period', 'sub_market', 'client_class', 'division', 'brand','cat', 'product', 'client_code', 'client_name', 'kgs_sold'] -
现在,让我们查看总客户数进行分析:
len(df.client_name.unique())>>> 11493
在这个案例中,我们有接近 12,000 个客户。为了本章的演示目的,我们将重点关注最重要的客户,基于消费最多的标准。
-
现在,我们将找出那些销售逐渐减少的客户。我们将通过汇总信息,列出那些总购买千克数最高的客户,以确定最佳客户。我们将使用
groupby方法按周期求和,计算每个客户在每个周期购买的千克数:kgs_by_period = df[['period','client_name','kgs_sold']]kgs_by_period = kgs_by_period.groupby(['period','client_name']).sum().reset_index()kgs_by_period.head()
在下图中,我们可以看到按客户和周期划分的商品总千克数:

图 6.2:按客户和周期聚合的商品总重量(单位:千克)
-
现在我们有了所有客户的列表,我们将按每个周期的购买次数对他们进行特征化:
unique_clients = kgs_by_period.client_name.value_counts().reset_index()unique_clients.columns = ['client_name','purchases']unique_clients.head()
在下一个数据框中,我们可以看到每个客户的购买次数:

图 6.3:按购买次数统计的用户数据
-
现在,我们将按购买次数筛选出前五名客户,保留在 8 个周期中至少有 5 次购买的客户。这个限制是为了找到那些大部分时间是常规客户的客户,限值是随意设定的:
unique_clients = unique_clients[unique_clients.purchases>5] -
现在,我们可以再次查看客户的总数:
unique_clients.shape>>> (7550, 2)
如我们所见,大多数客户有超过 5 个周期的购买记录,因此我们减少了约 30%的总用户数。
-
现在,我们将列出所有周期中销售的商品总千克数,筛选出购买周期少于 5 个周期的客户:
kgs_by_client = df[['client_name','kgs_sold']]kgs_by_client = kgs_by_client[kgs_by_client.client_name.isin(unique_clients.client_name)] -
现在,为了获得所有周期中销售的商品总千克数,我们将使用
groupby方法并按升序排序值:kgs_by_client = kgs_by_client.groupby(['client_name']).sum().reset_index()kgs_by_client.columns = ['client','total_kgs']kgs_by_client = kgs_by_client.sort_values(['total_kgs'],ascending= False) -
作为下一步,仅为了可视化和演示,我们将客户限制为前 25 名客户:
kgs_by_client = kgs_by_client.head(25)kgs_by_client.head()
然后,我们可以看到按总千克数排序的前 25 名客户:

图 6.4:购买千克数最多的客户
-
现在我们有了按千克数销售最多的客户的信息,我们可以创建一个直方图来理解他们的消费模式。我们将使用
plot方法为 pandas 数据框创建一个柱状图:kgs_by_client.plot(kind='bar',x='client',y='total_kgs',figsize=(14,6),rot=90)
这将得到以下结果:

图 6.5:销售千克数最多的客户图表
-
为了捕捉那些消费水平下降的客户,我们将创建一个掩码,筛选出所有客户中最顶尖的,来可视化每个客户和期间购买的千克数:
mask = kgs_by_period.client_name.isin(kgs_by_client.client)kgs_by_period = kgs_by_period[mask]kgs_by_period = kgs_by_period.sort_values(['kgs_sold'],ascending=False)kgs_by_period
这是在按重量筛选出顶尖客户后的过滤数据:

图 6.6:按期间和客户销售的千克数
-
最后,我们将透视 DataFrame 以进行可视化,并将 NaN 值填充为 0,因为这表示客户在该期间没有购买任何商品:
dfx = kgs_by_period.pivot(index='period',columns='client_name',values='kgs_sold').reset_index()dfx.columns.name = ''dfx = dfx.fillna(0)dfx
下一个 DataFrame 已经透视过,并且更适合用于机器学习模型:

图 6.7:透视数据
-
现在,我们可以可视化整个期间的消费情况:
import seaborn as snsimport matplotlib.pyplot as plt # visualizationf, ax = plt.subplots(figsize=(20, 6))# Load the long-form example gammas datasetg = sns.lineplot(data=dfx.drop(['period'],axis=1))# Put the legend out of the figureg.legend(loc='center left', bbox_to_anchor=(1, 0.5))
折线图让我们一目了然地看到销售量最大的客户:

图 6.8:按客户和期间销售的千克数折线图
-
为了识别那些有下降趋势的曲线,我们将确定每月销售额的斜率和标准差。这将帮助我们通过观察斜率识别消费下降的客户,并识别那些消费波动较大的用户:
from scipy import statsresults = []for i in range(1,dfx.shape[1]):client = dfx.columns[i]slope, intercept, r_value, p_value, std_err = stats.linregress(dfx.index,dfx.iloc[0:,i])results.append([client,slope,std_err])print('Client Name:',client,'; Buy Tendency (Slope):',round(slope,3),'; Common Standard deviation:',round(std_err,3))
从打印结果中可以看到,一些客户的斜率为负数,表示他们的消费模式显示出月度购买量的下降:

图 6.9:客户购买趋势的斜率
在这种情况下,值是以绝对值显示的,但更好的做法是将其显示为每个客户中位购买量的百分比,以保持一致性。你可以应用这个更改并评估差异。
-
接下来,我们将把结果存储在 DataFrame 中,并用它来可视化结果:
results_df = pd.DataFrame(results).dropna()results_df.columns = ['client','slope','std']results_df.index = results_df.clientresults_df = results_df.drop(['client'],axis=1)results_df.head()
DataFrame 显示了客户以及回归模型估算的参数:

图 6.10:最终斜率和标准差
-
现在我们的信息已经整齐地结构化,我们可以创建一个 seaborn 热力图,更直观地展示结果数据:
f, ax = plt.subplots(figsize=(12, 12))sns.heatmap(results_df, annot=True)
结果为以下输出:

图 6.11:斜率和偏差热力图
从数据中我们可以看到,一些客户的月度购买量明显下降,而有些客户则逐渐增加了购买量。查看标准差也很有帮助,可以找到这些客户购买模式的波动性。
现在我们了解了每个客户的表现,可以通过为他们提供量身定制的推荐,针对销售下降的客户采取行动。在下一部分中,我们将根据客户的购买模式训练推荐系统。
理解产品推荐系统
现在我们已经识别出消费下降的客户,我们可以为他们创建特定的产品推荐。你如何推荐产品?在大多数情况下,我们可以使用推荐系统,这是一种过滤系统,旨在预测并显示用户可能想购买的产品,从而形成产品建议。k 最近邻方法和潜在因子分析是协同过滤中使用的两种算法,潜在因子分析是一种统计方法,用于找出相关变量的群体。此外,使用协同过滤时,系统会学习两个或多个物品共同购买的可能性。推荐系统的目标是以用户喜欢的方式提供产品的用户友好推荐。协同过滤方法和基于内容的方法是实现这一目标的两大类技术。
向客户推荐相关产品的重要性至关重要,因为企业可以通过推荐系统个性化客户体验,推荐那些根据客户消费模式对他们最有意义的产品。为了提供相关的产品推荐,推荐引擎还使公司能够分析客户过去和现在的网页活动。
推荐系统有很多应用,其中一些最著名的包括视频和音频服务的播放列表生成器、在线商店的产品推荐器、社交媒体平台的内容推荐器以及开放网页内容推荐器。
总结来说,推荐引擎提供基于每个客户需求和兴趣的个性化直接推荐。机器学习正在被用来改进在线搜索,因为它基于用户的视觉偏好而非产品描述提供建议。
创建推荐系统
我们训练推荐系统的第一步是捕捉客户的消费模式。在接下来的示例中,我们将关注客户在不同时间段内购买的产品:
dfs = df[['client_name','product']].groupby(['client_name','product']).size().reset_index(name='counts')
dfs = dfs.sort_values(['counts'],ascending=False)
dfs.head()
这会产生以下输出:

图 6.12:客户购买的产品
我们将使用 0 到 1 之间的评分标准来训练推荐系统,因此需要对这些值进行缩放。现在我们可以看到一些客户始终购买某些产品,因此我们将使用sklearn的最小-最大缩放器来调整比例。
在机器学习中,我们通过生成新值来规范化数据,保持整体分布,并调整数据中的比例;规范化可以防止使用原始数据以及解决众多数据集问题。运用多种方法和算法还可以提高机器学习模型的效率和准确性。
来自 scikit-learn 的MinMaxScaler可以用于将变量缩放到一个范围内。需要注意的是,变量的分布应该是正态分布。MinMaxScaler保留了原始分布的形状,确保原始数据中的信息没有实质性改变。请记住,MinMaxScaler不会降低异常值的重要性,且最终生成的特征默认范围为 0 到 1。
哪个缩放器——MinMaxScaler还是StandardScaler——更优?对于符合正态分布的特征,StandardScaler很有帮助。当上下边界从领域知识中得到了很好的定义时,可以使用MinMaxScaler(例如 RGB 颜色范围内从 0 到 255 的像素强度)。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
dfs['count_sc'] = scaler.fit_transform(dfs[['counts']])
dfs = dfs.drop(['counts'],axis=1)
现在我们已经标准化了值,可以开始构建推荐系统了。在这里,我们将使用 SVDpp 算法,它是 SVD 的扩展,考虑了隐式评分。SVD 作为协同过滤机制被应用于推荐系统。矩阵中的每一行代表一个用户,每一列代表一件商品。用户为物品提供的评分构成了矩阵的元素。
SVD 的通用公式是:
M=UΣV**ᵗ
其中:
-
M 是我们想要分解的原始矩阵,它是用户和他们购买的产品的密集矩阵
-
U 是左奇异矩阵(列是左奇异向量)
-
Σ是一个对角矩阵,包含奇异特征值
-
V 是右奇异矩阵(列是右奇异向量)

图 6.13:协同过滤矩阵分解
scikit-surprise包高效地实现了 SVD 算法。我们无需重新发明轮子,就可以使用简单易用的 Python 模块SurpriseSVD快速构建基于评分的推荐系统。当使用像 SVD 这样的模型时,SurpriseSVD还提供了对矩阵因子的访问,使我们能够直观地看到数据集中对象之间的关系:
-
我们将从导入库开始:
from surprise import SVDppfrom surprise.model_selection import cross_validatefrom surprise import Reader, Dataset -
现在,我们将初始化阅读器,并将其范围设置为 0 到 1:
reader = Reader(rating_scale=(0,1)) -
然后,我们可以使用
Dataset方法从包含标准化产品值计数的数据框加载数据:data = Dataset.load_from_df(dfs, reader) -
最后,我们可以实例化 SVD 算法并在数据上进行训练:
algo = SVDpp()algo.fit(data.build_full_trainset())
训练过程应该需要几分钟,具体取决于您的硬件规格,但一旦完成,我们就可以开始使用它来进行预测。
-
我们将从选择一个特定的用户开始,并筛选出他们尚未购买的所有产品,以便为他们提供更推荐的产品:
usr = 'LA TROYA'# Filter the products that the client is already buyinguser_prods = dfs[dfs.client_name==usr]['product'].unique().tolist()prods = dfs[dfs.client_name!=usr]['product'].unique().tolist()prods = [p for p in prods if p not in user_prods] -
现在我们已经确定了用户未购买的产品,让我们看看算法如何为这个特定的用户评分:
my_recs = []for iid in prods:my_recs.append((iid, algo.predict(uid=usr,iid=iid).est)) -
上述代码将遍历以下数据中的产品,并创建一个包含推荐值最高产品的数据框:
dk = pd.DataFrame(my_recs)dk.columns = ['product', 'rating']dk = dk.sort_values('rating',ascending= False).reset_index(drop=True)dk.head()
下一个数据框展示了我们为客户推荐的产品:

图 6.14:客户推荐的产品
-
现在我们已经为单个用户确定了这一点,我们可以将其推断到其余的客户。我们将只保留前 20 个推荐,因为产品数量过于庞大:
dki_full = pd.DataFrame()# For top 25 clientsusers = kgs_by_period.client_name.unique()for usr in users:print(usr)user_prods = dfs[dfs.client_name==usr]['product'].unique().tolist()prods = dfs[dfs.client_name!=usr]['product'].unique().tolist()prods = [p for p in prods if p not in user_prods]my_recs = []for iid in prods:my_recs.append((iid, algo.predict(uid=usr,iid=iid).est))dk = pd.DataFrame(my_recs)dk.columns = ['product', 'rating']dk = dk.sort_values('rating',ascending= False).reset_index(drop=True)dk['client'] = usrdki_full = pd.concat([dki_full, dk.head(20)])
这个脚本将允许我们遍历我们的客户,并为每个客户生成一份推荐产品的清单。在这种情况下,我们正在进行特定的分析,但这可以实现为一个实时交付这些结果的管道。
现在我们有了数据,可以为每个客户提供量身定制的产品推荐,推荐他们尚未购买的产品。我们还可以推荐与他们已经购买的产品互补的产品,接下来我们将在下一部分做这些。
使用 Apriori 算法进行产品捆绑
目前,我们专注于那些减少购买的客户,为他们提供针对那些他们未购买的产品的特定优惠,但我们也可以改善已经是忠实客户的人的结果。我们可以通过进行市场购物篮分析,提供与他们消费模式相关的产品,从而提高他们购买的产品数量。为此,我们可以使用多种算法。
最受欢迎的关联规则学习方法之一是 Apriori 算法。它识别数据集合中的事物,并将它们扩展为越来越大的项目组合。Apriori 被用于数据集中的关联规则挖掘,寻找多个经常出现的事物集合。它扩展了项集之间的联系和关联。这就是你经常在推荐网站上看到的“You may also like”建议的实现,它们正是该算法的结果。
Apriori 是一种用于关联规则学习和频繁项集挖掘的算法,应用于关系数据库。只要这些项集在数据库中出现得足够频繁,它就会通过检测频繁的单个项目并将其扩展为越来越大的项集来向前推进。Apriori 算法通常用于事务型数据库,这些数据库通过 Apriori 方法挖掘频繁项集和关联规则。“支持度”、“提升度”和“置信度”作为参数被使用,其中支持度是某项物品出现的可能性,而置信度则是条件概率。项集由事务中的物品组成。该算法使用“连接”和“剪枝”两个步骤来减少搜索空间。它是发现最频繁项集的迭代方法。在关联规则学习中,数据集中的项被识别,数据集会被扩展为包括越来越大的物品组合。
Apriori 方法是市场购物篮分析中常用的算法,它是一种广泛使用的关联规则算法。该方法有助于发现交易中频繁出现的项集,并揭示这些项之间的关联规律。
使用 Apriori 进行市场购物篮分析
对于此分析,我们将使用 UCI ML 库中找到的独立数据(archive.ics.uci.edu/ml/datasets/Online+Retail):
-
我们通过导入包并加载数据开始分析。记得在运行这段代码之前安装
mlxtend模块,否则会遇到Module Not Found错误:from mlxtend.frequent_patterns import apriori, association_rulesdata = pd.read_csv('/content/Online Retail.csv',encoding='iso-8859-1')data.head()

图 6.15: 在线零售数据
这份国际数据集包含了 2010 年 12 月 1 日至 2011 年 12 月 9 日间,一家英国注册的非店面互联网零售商所进行的每一笔交易。该公司主要提供各种场合的一次性礼品,拥有大量批发商客户。
-
我们开始探索数据的各列:
data.columns>>> Index(['InvoiceNo', 'StockCode', 'Description', 'Quantity', 'InvoiceDate','UnitPrice', 'CustomerID', 'Country'],dtype='object')
数据包含了包含代码和日期信息的交易销售数据,但我们现在不需要这些信息。相反,我们将重点关注描述、数量和价格。
-
我们可以查看数据的统计汇总:
data.describe()

图 6.16: 描述性统计汇总
-
为了评估分类变量,我们使用聚焦于 DataFrame 对象列的
describe方法:data.describe(include='O')

图 6.17: 描述性分类汇总
这些信息展示了每个对象列的一些计数,并表明最常见的国家是英国,正如预期的那样。
-
我们还将探索不同地区的交易,以更好地理解数据:
data['Country'].value_counts().head(10).plot(kind='bar',figsize=(12,6))

图 6.18: 数据中的市场
我们可以确认绝大多数交易发生在英国,其次是德国和法国。
-
为了提高可读性,我们将去除描述中的多余空格:
data['Description'] = data['Description'].str.strip() -
现在,我们将删除发票号中包含 NaN 的行,并将它们转换为字符串以进行分类处理:
data = data[~data['InvoiceNo'].isna()]data['InvoiceNo'] = data['InvoiceNo'].astype('str') -
目前,我们将专注于非信用交易,因此我们将删除所有信用交易:
data = data[~data['InvoiceNo'].str.contains('C')] -
我们将通过查看英国的关联规则开始分析:
data_uk = data[data['Country'] =="United Kingdom"]basket_uk = data_uk.groupby(['InvoiceNo', 'Description'])['Quantity'].sum()basket_uk = basket_uk.unstack().reset_index().fillna(0)basket_uk = basket_uk.set_index('InvoiceNo')basket_uk.head()

图 6.19: 英国市场购物篮
我们可以看到每张发票上购买的产品的密集矩阵。
-
我们将对法国的交易进行相同的操作:
basket_fr = data[data['Country'] =="France"]basket_fr = basket_fr.groupby(['InvoiceNo', 'Description'])['Quantity'].sum()basket_fr = basket_fr.unstack().reset_index().fillna(0)basket_fr = basket_fr.set_index('InvoiceNo') -
最后,我们将对德国的数据进行相同的操作:
basket_de = data[data['Country'] =="Germany"]basket_de = basket_de.groupby(['InvoiceNo', 'Description'])['Quantity'].sum()basket_de = basket_de.unstack().reset_index().fillna(0)basket_de = basket_de.set_index('InvoiceNo') -
现在,我们将定义热编码函数,使数据适应相关库的要求,因为它们需要离散的值(0 或 1):
basket_uk = (basket_uk>0).astype(int)basket_fr = (basket_fr>0).astype(int)basket_de = (basket_de>0).astype(int) -
一旦我们将结果编码为一热编码器,我们就可以开始为每个市场建立模型:
frq_items_uk = apriori(basket_uk, min_support = 0.01, use_colnames = True) -
一旦模型建立完成,我们就可以查看英国市场发现的关联规则:
# Collecting the inferred rules in a dataframerules_uk = association_rules(frq_items_uk, metric ="lift", min_threshold = 1)# rules_uk = rules_uk.sort_values(['confidence', 'lift'], ascending =[False, False])rules_uk.head()

图 6.20:英国关联规则
如果更详细地检查英国交易的规则,就会发现英国人通常购买各种颜色的茶盘。这可能是因为英国人非常喜欢喝茶,并且经常为各种场合收集不同颜色的茶具。
-
现在我们将对法国的数据进行相同的操作:
frq_items_fr = apriori(basket_fr, min_support = 0.05, use_colnames = True)# Collecting the inferred rules in a dataframerules_fr = association_rules(frq_items_fr, metric ="lift", min_threshold = 1)rules_fr = rules_fr.sort_values(['confidence', 'lift'], ascending =[False, False])rules_fr.head()

图 6.21:法国关联规则
从这些数据中可以明显看出,纸盘、杯子和餐巾在法国经常一起购买。这是因为法国人每周至少会与朋友和家人聚会一次。此外,由于法国政府已经禁止在国内使用塑料,公民必须购买纸质替代品。
-
最后,我们将为德国数据建立模型:
frq_items_de = apriori(basket_de, min_support = 0.05, use_colnames = True)# Collecting the inferred rules in a dataframerules_de = association_rules(frq_items_de, metric ="lift", min_threshold = 1)rules_de = rules_de.sort_values(['confidence', 'lift'], ascending =[False, False])rules_de.head()

图 6.22:德国关联规则
先前的数据向我们展示,大多数项目与交付成本相关,因此这可能表明德国的交易大多由单一商品构成。
总结
在本章中,我们学习了如何识别销售量逐渐减少的客户,以便根据他们的消费模式为他们提供特定的产品推荐。我们通过观察给定周期内的历史销售斜率来识别销售下降,并使用 SVD 协同过滤算法为客户未购买的产品创建个性化推荐。
作为下一步,为了提高现有客户的忠诚度,我们探索了使用 Apriori 算法进行市场篮子分析,并能够根据购买的特定产品提供产品推荐。
在下一章中,我们将深入探讨如何识别流失客户的共同特征,以便通过更深入理解我们的客户流失来补充这些方法。
第三部分:运营和定价优化
本书的最后部分将介绍如何优化业务运营。我们将从理解市场和客户转向,深入探讨如何调整运营以提高利润率。这将通过改善定价策略、优化促销使用以及最终提升数字营销策略以接触更多潜在客户来实现。
本部分涵盖以下章节:
-
第七章,预测客户流失
-
第八章,通过客户细分分组用户
-
第九章,利用历史 Markdown 数据预测销售
-
第十章,网站分析优化
-
第十一章,在商业中创建数据驱动的文化
第七章:预测客户流失
流失率是一个用来衡量在一定时间框架内有多少客户或员工离开公司的指标。它也可以指因客户流失而损失的资金总额。公司流失率的变化可能会提供关于公司有价值的信息。通过客户流失分析,可以了解不再购买更多商品或服务的消费者数量或比例。
在本章中,我们将了解流失(churn)的概念以及它在商业中的重要性。接着,我们将准备数据进行进一步分析,并创建分析模型来确定理解流失模式时需要考虑的最重要因素。最后,我们将学习如何创建机器学习模型,以预测可能流失的客户。
本章包含以下内容:
-
理解客户流失
-
探索客户数据
-
探索变量关系
-
预测可能流失的用户
技术要求
为了能够按照本章中的步骤进行操作,您需要满足以下要求:
-
运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果你有 Google Drive 账户,也可以使用 Google Colab 笔记本来运行这些步骤。
-
了解基础的数学和统计概念。
理解客户流失
在商业中,未能成为回头客的付费客户的数量被称为客户流失,也称为客户流失率。这里的流失是指在预定时间段内发生的可衡量的变化率。
分析流失的原因、与客户互动、教育客户、了解哪些客户处于风险中、识别最有价值的客户、提供激励措施、选择正确的目标受众以及提供更好的服务是减少客户流失的一些策略。
降低流失率至关重要,因为它会增加客户获取成本(CAC)并降低收入。实际上,维护和改善现有客户关系的成本远低于吸引新客户的成本。你失去的客户越多,你就需要花费更多的资金来吸引新客户,以弥补失去的收入。你可以使用以下公式来计算 CAC:CAC 通过将销售和营销成本除以新客户的数量来计算。客户回归率是指返回你公司并继续购买的消费者的比例。

这与流失率不同,后者衡量的是在一定时间内失去的客户数量。默认情况下,流失率高的公司会有较低的客户回归率。
现在我们已经大致了解了通过识别导致客户流失的模式所获得的商业价值,在接下来的部分中,我们将开始探索数据及其变量。
探索客户数据
我们的目标是创建一个模型,用于估算客户流失的可能性,数据来自电信客户。这是为了回答消费者停止使用该服务的可能性有多大这个问题。
最初,数据会进行探索性分析。了解每一列的数据类型是过程中的第一步,之后将对变量进行必要的调整。
为了探索数据,我们将绘制流失变量与其他构成数据集的重要因素之间的关系。在提出模型之前,进行这项工作是为了初步了解变量之间的潜在关系。
在进行描述性统计时,采用了细致的方法,主要关注客户基于一项或多项属性的差异。现在,主要关注的变量是流失,因此为此生成了一组新的有趣图表。
为了检查变量,我们必须处理非结构化数据并调整数据类型;第一步是探索数据。本质上,我们将了解数据分布并为聚类分析整理数据。
在接下来的示例中,我们将使用以下 Python 模块:
-
Pandas:用于数据分析和数据操作的 Python 包。
-
NumPy:这是一个为大型、多维数组和矩阵提供支持的库,还包含了广泛的高阶数学函数,用于处理这些数组。
-
statsmodels:一个 Python 包,它为统计计算提供了 SciPy 的补充,包括描述性统计和统计模型的估计与推断。它提供了用于估计多种统计模型的类和函数。
-
Seaborn、mpl_toolkits 和 Matplotlib:用于高效数据可视化的 Python 包。
现在,我们将按照以下步骤开始分析:
-
第一部分的代码块中,我们将加载刚才提到的所有必需包,包括我们将要使用的函数,如
LabelEncoder、StandardScaler和KMeans:import numpy as npimport pandas as pdimport seaborn as snsimport matplotlib.pyplot as pltimport os -
为了提高可读性,我们将限制最大行数为
20,将最大列数设置为50,并显示保留2位小数的浮动数据:pd.options.display.max_rows = 20pd.options.display.max_columns = 50pd.options.display.precision = 2path = 'customer_churn.csv'data = pd.read_csv(path)data.head()
这段代码将加载数据并展示数据的前几行:

图 7.1:客户数据
-
现在,我们可以查看 DataFrame 中的列:
data.columns
为了获取每一列的类型和缺失值数量,我们可以使用info方法:
data.info()

图 7.2:Pandas 列数据类型
-
我们可以看到,尽管我们没有
null值需要填补,但大多数变量是分类变量——这意味着我们需要将它们转换为boolean数值列,才能使用机器学习模型或聚类方法。第一步是将TotalCharges转换为数值数据类型:data.TotalCharges = pd.to_numeric(data.TotalCharges, errors='coerce')
上述代码将变量转换为数值型变量,强制任何错误值,而不是失败。
我们可以通过再次使用info方法查看转换结果:
data.info()

图 7.3: 修正后的数据类型
结果转换成功,但生成了 10 个null值,我们可以稍后删除这些值。
-
现在,我们将确定需要转换为虚拟变量以便更好分析的所有分类列:
object_cols = [c for c in data.drop(['customerID'],axis=1).columns if data[c].dtype=='O']object_cols -
有几列可以通过 1 和 0 来轻松表示,因为这些列的值是
Yes或No;我们将确定哪些变量只有这两种选项,然后将这些值映射到它们的数值对应值:yn_cols = []# Iterate over the column namesFor c in object_cols:# count the unique values by accessing the columnval_counts = data[c].value_counts()# If the count of unique values is equal to two, we assume that it's a Yes/No columnif len(val_counts.index)==2 and all(val_counts.index.isin(['No', 'Yes'])):print(c)print(data[c].value_counts().to_string())yn_cols.append(c) -
上述代码将生成一组
Yes/No的分类列,我们可以将这些数据映射为 1 和 0:# Iterate over the yes/no column namesfor c in yn_cols:# Normalize the column values by lowering them and mapping them to new values.data[c] = data[c].str.lower().map({'yes': 1, 'no': 0})
我们现在可以重新评估这些替换的结果:
data.head()

图 7.4: 列转换为规范化布尔列
我们现在可以使用describe方法查看数值变量的分布,以更好地理解数据:
data.describe()

图 7.5: 数据的统计描述
有趣的是,在这里,有 27%的客户流失,这个比例非常大。在其他情况下,这些值往往会低得多,这使得数据集高度不平衡,并且需要调整分析方法以应对这些不平衡。幸运的是,这里并非如此,因为客户流失的发生频率足够具有代表性。然而,不平衡的数据集要求我们更深入地审视评估模型时使用的度量标准。如果我们仅仅关注准确率,在我们的案例中,模型如果只是输出最常见的变量(客户不流失),其准确率将是 73%。这就是为什么我们需要添加更多的性能指标,比如精确率、召回率,以及两者的结合,如 F1 得分,特别是查看混淆矩阵,以找出每个类别正确预测案例的比例。
-
我们现在可以可视化一些分类变量的分布,考虑到用户流失的情况。我们可以使用 Seaborn 的
countplot来完成这个操作:import seaborn as snsimport matplotlib.pyplot as pltf, ax = plt.subplots(figsize=(10, 6))pl = sns.countplot(x=data["InternetService"],hue=data["Churn"])pl.set_title("InternetService vs Churn")pl.set_xlabel("InternetService")pl.set_ylabel("Count")

图 7.6: 客户互联网合同与流失
我们可以看到,选择光纤服务的用户与没有互联网服务或仅有 DSL 服务的用户在流失率上存在很大的差异。
-
这些信息可以用于深入分析原因,或制定新的促销活动以应对这一情况:
f, ax = plt.subplots(figsize=(10, 6))pl = sns.countplot(x=data["MultipleLines"],hue=data["Churn"])pl.set_title("MultipleLines vs Churn")pl.set_xlabel("MultipleLines")pl.set_ylabel("Count")

图 7.7: 客户电话合同与流失率
我们可以看到,选择多个线路的客户与不选择多个线路的客户在流失率上存在一定差异。没有多个线路的客户似乎流失更多。这些差异需要通过 t 检验或其他假设检验方法来验证,以确定各组均值之间的实际差异。
-
下一步是可视化合同与流失率之间的关系:
f, ax = plt.subplots(figsize=(10, 6))pl = sns.countplot(x=data["Contract"],hue=data["Churn"])pl.set_title("Contract vs Churn")pl.set_xlabel("Contract")pl.set_ylabel("Count")
这里的代码将展示合同类型与流失率之间的条形图:

图 7.8: 客户合同类型与流失率
在这里,相比于拥有 1 年或 2 年合同的客户,按月合同的客户流失率极高。这些信息可以用来制定营销策略,尝试将按月合同转化为 1 年或 2 年的合同。
-
最后,我们对最后一个变量的探索聚焦于支付方式类型。下一个图表将展示流失率与使用的支付方式类型之间的关系:
f, ax = plt.subplots(figsize=(10, 6))pl = sns.countplot(x=data["PaymentMethod"],hue=data["Churn"])pl.set_title("PaymentMethod vs Churn")pl.set_xlabel("PaymentMethod")pl.set_ylabel("Count")
这里的代码将展示支付方式与流失率之间的条形图:

图 7.9: 客户支付方式与流失率
在这里,使用电子支票而非其他支付方式的客户之间的差异是显而易见的。
接下来,我们将使用 Seaborn 的pairplot以及相关分析来探索不同变量之间的关系。
探索变量关系
探索变量如何共同变化可以帮助我们确定隐藏的模式,这些模式支配着客户的行为:
-
我们的第一步将使用 Seaborn 方法绘制一些关系图,主要是数值连续变量之间的关系,如服务年限、每月费用和总费用,使用流失率作为
hue参数:g = sns.pairplot(data[['tenure','MonthlyCharges', 'TotalCharges','Churn']], hue= "Churn",palette= (["red","blue"]),height=6)

图 7.10: 连续变量关系
从分布情况来看,流失的客户通常具有较低的服务年限,通常每月费用较低,且总费用较低。
-
现在,我们可以最终转换并确定将要转化为虚拟变量的对象列:
object_cols = [c for c in data.drop(['customerID'],axis=1).columns if data[c].dtype=='O']object_cols -
一旦确定了这些列,我们可以使用
get_dummies函数,并创建一个仅包含数值型变量的新 DataFrame:df_dummies = pd.get_dummies(data[object_cols])data = pd.concat([data.drop(object_cols+['Churn'],axis=1),df_dummies,data[['Churn']]],axis=1)data.head()
上述代码将展示重新结构化后的数据:

图 7.11:重构后的数据
在这里,数据能够有效地描述每个客户的描述性水平,从而使信息以数值形式而非分类形式表示。这些因素包括任期、订阅类型、费用、通话历史和人口统计等。之所以需要以数值形式表示维度,是因为大多数机器学习算法要求数据以这种方式表示。
-
下一步,我们将研究变量之间的关系,以确定它们之间最重要的相关性:
import numpy as npfrom matplotlib import colorsdf_corr = data.corr()mask = np.triu(np.ones_like(df_corr, dtype=bool))df_corr = df_corr.mask(mask).round(2)fig, ax = plt.subplots(figsize=(25,25))sns.heatmap(df_corr, annot=True,ax=ax)
代码确定这些相关性并构建一个三角形数据集,我们可以将其绘制为热力图,以清晰地可视化变量之间的关系。

图 7.12:变量相关性
在这里,我们可以可视化整个变量相关性的集合,但我们可能只关注与目标变量相关的部分。我们可以看到,一些依赖其他变量的变量与此变量的相关性为 1——例如,在internet contract = no的情况下,它与streaming service = no的相关性为 1。这是因为如果没有互联网合同,很明显就无法使用需要互联网合同的流媒体服务。
-
我们可以通过查看与此变量相关的相关性来实现这一点:
churn_corr = data.corr()['Churn'].sort_values(ascending = False)churn_corr.plot(kind='bar',figsize=(20,8))

图 7.13:与流失最相关的变量
这些信息非常有用,因为它不仅确定了与较高流失率相关的变量,还指出了能够降低流失率的变量,例如任期时间和拥有 2 年合同。建议在这里删除流失变量,因为该变量与自身的相关性为 1,且会扭曲图形。
完成这一步 EDA 后,我们将开发一些预测模型并进行比较。
预测将流失的用户
在这个例子中,我们将训练逻辑回归、随机森林和支持向量机(SVM)机器学习模型,预测基于观察到的变量将会流失的用户。我们需要首先对变量进行缩放,使用 sklearn 的MinMaxScaler功能来实现:
-
我们将从逻辑回归开始,并将所有变量缩放到 0 到 1 的范围内:
from sklearn.preprocessing import MinMaxScalery = data['Churn'].valuesx = data.drop(columns = ['customerID','Churn']).fillna(0)scaler = MinMaxScaler(feature_range = (0,1))x_scaled = scaler.fit_transform(x)x_scaled = pd.DataFrame(x_scaled,columns=x.columns)x_scaled.head()
前面的代码将创建x和y变量,其中我们只需要对x进行缩放。

图 7.14:模型输入特征
在逻辑回归中,缩放变量非常重要,以确保所有变量都在 0 到 1 的范围内。
-
接下来,我们可以通过分割数据来训练逻辑回归模型,首先获得一个验证集:
from sklearn.model_selection import train_test_splitfrom sklearn.linear_model import LogisticRegressionfrom sklearn import metricsx_train, x_test, y_train, y_test = train_test_split(x_scaled, y, test_size=0.3, random_state=101)model = LogisticRegression()result = model.fit(x_train, y_train)preds_lr = model.predict(x_test)
最后,我们可以打印出预测准确性:
print(metrics.accuracy_score(y_test, preds_lr))

图 7.15:逻辑回归模型准确性
我们在模型中获得了不错的准确性。
-
我们还可以获得所有变量的权重,以衡量它们在预测模型中的重要性:
weights = pd.Series(model.coef_[0],index=x_scaled.columns)pd.concat([weights.head(10),weights.tail(10)]).sort_values(ascending = False).plot(kind='bar',figsize=(16,6))
上述代码将创建一个数据框,显示 10 个权重最正向和 10 个权重最负向的变量。

图 7.16:模型特征重要性
有趣的是,看到总费用和服务年限,尤其是后者,在回归模型中的重要性。通过查看变量之间的相关性,这些变量的重要性得到了验证。一个重要的下一步将是深入分析这些变量与流失变量之间的关系,以理解这种关系背后的机制。
-
我们可以创建混淆矩阵,以可视化每个类别预测的性能:
from sklearn.metrics import classification_report, confusion_matrixprint(confusion_matrix(y_test,preds_lr))

图 7.17:模型混淆矩阵
在混淆矩阵中,列中显示的是不同的类别(无流失和流失),而行中则是按相同顺序预测的结果(无流失,流失)。对角线上的值表示真正的正例,预测为该类别且实际也是该类别。对角线外的值则表示预测错误的值。在我们的案例中,我们正确分类了 1401 个无流失的案例,错误地将 265 个无流失分类为流失,将 145 个流失预测为无流失,正确分类了 302 个流失的案例。从混淆矩阵中可以看出,模型在预测最常见的情况(即无流失)方面表现良好,但几乎三分之一的类别预测错误,这对我们预测非常重要。
- 我们的下一步是创建一个由多个决策树组成的分类系统,称为随机森林。它通过使用自助法(bagging)和特征随机性在生成每个单独的树时,尝试产生一个相互独立的决策树森林,这比单棵树更准确。
在接下来的代码中,我们将使用来自 sklearn 的 RandomForestClassifier 类,并在数据上进行训练:
from sklearn.ensemble import RandomForestClassifier
model_rf = RandomForestClassifier(n_estimators=750 , oob_score = True, random_state =50, max_features = "auto",max_leaf_nodes = 15)
model_rf.fit(x_train, y_train)
-
最后,我们训练了模型,并可以进行预测:
preds_rfc = model_rf.predict(x_test)print(metrics.accuracy_score(y_test, preds_rfc))

图 7.18:随机森林模型准确度
我们得到了一个与回归模型非常相似的准确度。
-
让我们来看一下模型的变量重要性,作为下一步:
importances = model_rf.feature_importances_weights_rf = pd.Series(importances,index=x_scaled.columns)pd.concat([weights_rf.head(10),weights.tail(10)]).sort_values(ascending = False).plot(kind='bar',figsize=(16,6))
上述代码将显示模型中 10 个最正向和 10 个最负向的权重。

图 7.19:模型特征重要性
这个模型有不同的排序,在极端情况下,月度合同和 2 年合同排在前列:
print(confusion_matrix(y_test,preds_rfc))

图 7.20:模型混淆矩阵
该模型更好地预测了我们的目标变量,这使得它更适合我们的需求。
-
接下来,我们将训练一种监督式机器学习技术,称为支持向量分类器(SVC),它常用于分类问题。SVC 通过将数据点映射到高维空间,然后定位最佳超平面,将数据分为两类:
from sklearn.svm import SVCmodel_svm = SVC(kernel='linear')model_svm.fit(x_train,y_train)preds_svm = model_svm.predict(x_test)metrics.accuracy_score(y_test, preds_svm)
这里的代码将模型拟合到数据中,并打印出准确率得分。

图 7.21:模型准确率得分
-
准确率得分仍然在其他模型的相同范围内,因此让我们来看看模型的绝对权重重要性:
pd.Series(abs(model_svm.coef_[0]), index=x_scaled.columns).nlargest(10).plot(kind='barh',figsize=(10,8))
这里的代码将提示模型的 10 个最重要的变量。

图 7.22:模型特征重要性
我们可以看到,任期和总费用是模型中重要的变量,这也是我们在其他模型中看到的。这种可视化的缺点是我们无法看到这些重要性的方向性。
-
让我们看看预测目标变量的表现:
print(confusion_matrix(y_test,preds_svm))
下一步是混淆矩阵,这将帮助我们更准确地确定哪些标签我们正确地预测了。

图 7.23:模型混淆矩阵
该模型在预测客户是否流失方面的准确性低于随机森林。
将混淆矩阵的不同视角结合起来总是很有帮助的,因为如果数据集不平衡,仅凭准确率无法作为性能度量。
总结
在本章中,我们分析了一个非常常见的商业案例:客户流失。理解其原因,以及能够采取预防措施避免流失,可以为公司创造大量收入。
在我们分析的示例中,我们看到了如何清洗数据集中的变量,正确表示它们并为机器学习做准备。将变量与我们分析的目标变量进行关系可视化,有助于我们更好地理解问题。最后,我们训练了几个机器学习模型,随后分析了它们在预测目标变量时的表现。
在下一章中,我们将更加关注如何理解变量如何影响数据段,并将具有相似特征的用户分组,以便更好地理解他们。
第八章:使用客户细分对用户进行分组
为了更好地理解消费者需求,我们需要了解我们的客户具有不同的消费模式。每一类特定产品或服务的消费者群体可以根据年龄、婚姻状况、购买力等因素划分为不同的细分群体。在本章中,我们将对来自一家超市的消费者数据进行探索性分析,然后应用聚类技术将其分成具有相似消费模式的群体。这些知识将帮助我们更好地理解他们的需求,创造独特的优惠,并更有效地进行精准营销。在本章中,我们将学习以下内容:
-
了解客户细分
-
探索有关客户数据库的数据
-
应用特征工程来标准化变量
-
使用 K-means 聚类创建用户细分
-
描述这些群体的共同特征
让我们看看理解步骤并跟随本章的要求。
技术要求
为了能够跟随本章的步骤,您需要满足以下要求:
-
运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果您有 Google Drive 账户,也可以使用 Google Colab notebook 运行步骤。
-
了解基本的数学和统计学概念。
-
一个 Kaggle 账户—您必须同意我们将获取数据的比赛条款和条件,您可以在此处找到:
www.kaggle.com/datasets/imakash3011/customer-personality-analysis。
了解客户细分
客户细分是将客户根据共同特征分类的实践,以便企业能够有效且恰当地向每个群体进行市场营销。在企业对企业(B2B)市场营销中,一家公司可能会根据多种标准将其客户群划分为几个组别,如地理位置、行业、员工人数以及公司商品的历史购买记录。
企业通常根据人口统计学特征(如年龄、性别、婚姻状况、地理位置(城市、郊区或农村)和生活阶段(单身、已婚、离婚、空巢、退休))将客户群体划分为不同的细分市场。客户细分要求企业收集关于其客户的数据,评估这些数据,并寻找可用来建立细分市场的趋势。
职位名称、地点和购买的产品——例如——是从购买数据中可以获取的帮助企业了解其客户的一些细节。这些信息可能通过查看客户的系统记录发现。使用选择加入邮件列表的在线营销人员可能会根据吸引客户的选择加入优惠将营销通讯划分为不同类别。例如,其他数据——例如消费者的人口统计信息,如年龄和婚姻状况——则需要通过不同的方法收集。
在消费品领域,其他典型的信息收集方法包括:
-
与客户的面对面访谈
-
在线调查
-
在线营销和网站流量信息
-
焦点小组
所有组织,无论大小、行业,还是在线销售或面对面销售,都可以使用客户细分。这一过程从获取和评估数据开始,最终通过采取适当有效的行动来利用所获得的信息。
在本章中,我们将对一家杂货店数据库中的客户记录执行无监督的聚类分析。为了最大化每位客户对公司的价值,我们将细分客户群体,根据特定需求和消费者行为调整产品。满足不同客户需求的能力也会使公司受益。
探索数据
理解客户细分的第一步是了解我们将使用的数据。第一阶段是探索数据,检查我们必须处理的变量,处理非结构化数据并调整数据类型。我们将为聚类分析构建数据,并了解数据的分布情况。
在下一个示例中我们将使用以下 Python 模块进行分析:
-
Pandas:用于数据分析和数据处理的 Python 包。
-
NumPy:这是一个为大规模、多维数组和矩阵提供支持的库,并附带大量的高阶数学函数,用于操作这些数组。
-
scipy用于统计计算,包括描述性统计和统计模型的估算与推断。它提供用于估算多种统计模型的类和函数。 -
Yellowbrick:一个用于视觉分析和诊断工具的 Python 包,旨在通过 scikit-learn 促进 机器学习(ML)。
-
Seaborn, mpl_toolkits 和 Matplotlib:用于有效数据可视化的 Python 包。
现在我们将开始分析,采用以下步骤:
-
以下代码块将加载之前提到的所有必要包,包括我们将使用的函数,例如
LabelEncoder、StandardScaler和Kmeans:import numpy as npimport pandas as pdimport datetimeimport seaborn as snsimport matplotlib.pyplot as pltfrom matplotlib import colorsfrom matplotlib.colors import ListedColormapfrom sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCAfrom yellowbrick.cluster import KElbowVisualizerfrom sklearn.cluster import KMeansfrom mpl_toolkits.mplot3d import Axes3Dfrom sklearn.cluster import AgglomerativeClustering -
为了提高可读性,我们将限制显示的最大行数为 20,设置最大列数为 50,并将浮动数值显示为保留两位小数:
pd.options.display.max_rows = 20pd.options.display.max_columns = 50pd.options.display.precision = 2 -
接下来,我们将加载数据,这些数据存储在本地数据文件夹中。文件采用 CSV 格式,使用制表符作为分隔符。我们将数据读取到一个 Pandas DataFrame 中,并打印数据的形状,以及显示前几行:
path = "data/marketing_campaign.csv"data = pd.read_csv(path, sep="\t")print("Data Shape", data.shape)data.head()
这会生成以下输出:

图 8.1:用户数据
-
为了全面了解我们将采取的清洗数据集的步骤,让我们查看使用
describe方法生成的数据的统计摘要:data.describe()
这会生成以下输出:

图 8.2:描述性统计摘要
-
为了获取更多关于特征的信息,我们可以使用
info方法显示null值的数量和数据类型:data.info()
这会生成以下输出:

图 8.3:列数据类型和缺失值
从之前使用 Pandas DataFrame 的describe和info方法显示的输出中,我们可以看到以下内容:
-
Income列中有 26 个缺失值 -
名为
Dt_Customer的日期变量,表示客户加入数据库的日期,并未被解析为DateTime -
我们的 DataFrame 中有一些类别特征,其
dtype为对象类型,我们稍后需要将它们编码为数值特征,以便能够应用聚类方法。
-
为了处理缺失值,我们将删除缺失收入值的行,因为收入是描述客户的重要变量:
data = data.dropna()print("Data Shape", data.shape) -
我们将使用
pd.to_datetimePandas 方法解析日期列。需要注意的是,该方法会自动推断日期格式,但如果需要,我们也可以指定格式:data["Dt_Customer"] = pd.to_datetime(data["Dt_Customer"]) -
在解析日期之后,我们可以查看最新和最早注册客户的值:
str(data["Dt_Customer"].min()),str(data["Dt_Customer"].max())>>>> ('2012-01-08 00:00:00', '2014-12-06 00:00:00') -
在下一步中,我们将创建一个基于
Dt_Customer的特征,表示客户注册公司数据库的天数,相对于数据库中第一个注册的用户,尽管我们也可以使用今天的日期。我们这样做是因为我们分析的是历史记录,而不是最新数据。Customer_For特征是客户注册日期减去日期列中的最小值,可以解释为客户开始在商店购物以来的天数,相对于最后记录的日期:data["Customer_For"] = data["Dt_Customer"]-data["Dt_Customer"].min()data["Customer_For"] = data["Customer_For"].dt.days -
现在,我们将探索类别特征中的唯一值,以更清楚地了解数据:
data["Marital_Status"].value_counts().plot.bar(figsize=(12,6),title = 'Categories in the feature Marital_Status:')

图 8.4:婚姻状况
在这里,我们可以看到有几种婚姻状况,这可能是由于数据采集时自由文本输入造成的。我们将需要标准化这些值。
-
接下来,我们将使用
value_counts方法查看Education特征中的值,并使用 Pandas 的plot方法绘制条形图:data["Education"].value_counts().plot.bar(figsize=(12,6),title = 'Categories in the feature Education:')

图 8.5:教育水平值
再次,我们可以看到自由文本输入的影响,因为有几个值具有相同的基本含义;因此,我们也需要对它们进行标准化处理。
在接下来的部分,我们将应用特征工程来结构化数据,以便更好地理解和处理数据。
特征工程
为了能够正确分析数据以及建模集群,我们需要清理和结构化数据——这个步骤通常被称为特征工程——因为我们需要根据分析计划重新结构化一些变量。
在这一部分,我们将执行下一步操作,清理和结构化一些数据集特征,目标是简化现有变量,并创建更易于理解的特征,以正确描述数据:
-
使用
Year_Birth特征创建一个Age变量,表示每位客户的出生年份。 -
创建一个
Living_With特征,以简化婚姻状况,描述夫妻的居住情况。 -
创建一个
Children特征,表示家庭中孩子的总数——即儿童和青少年。 -
按照产品类型汇总支出,以更好地捕捉消费者行为。
-
使用一个名为
Is_Parent的特征表示父母身份。
所以,让我们应用这里提到的步骤来结构化数据:
-
首先,让我们从客户当前的年龄开始,使用
pd.to_datetime方法获取当前年份和客户的出生年份:data["Age"] = pd.to_datetime('today').year -data["Year_Birth"] -
现在,我们将使用
sum方法对选定列进行求和,并沿列轴汇总支出数据:prod_cols = ["MntWines","MntFruits","MntMeatProducts","MntFishProducts","MntSweetProducts","MntGoldProds"]data["Spent"] = data[prod_cols].sum(axis=1) -
作为下一步,我们将婚姻状况的值映射到不同的编码中,以简化含义相近的术语。为此,我们定义一个映射字典,并用它来替换
marital_status列中的值,创建一个新特征:marital_status_dict= {"Married":"Partner","Together":"Partner","Absurd":"Alone","Widow":"Alone","YOLO":"Alone","Divorced":"Alone","Single":"Alone",}data["Living_With"] = data["Marital_Status"].replace(marital_status_dict) -
接下来,我们通过将家庭中孩子和青少年的总数相加,创建一个
Children特征:data["Children"]=data["Kidhome"]+data["Teenhome"] -
现在,我们使用关系和儿童数据建模家庭成员总数:
data["Family_Size"] = data["Living_With"].replace({"Alone": 1, "Partner":2})+ data["Children"] -
最后,我们在一个新变量中捕捉父母身份:
data["Is_Parent"] = (data.Children> 0).astype(int) -
现在,我们将教育水平分为三个组以便简化:
edu_dict = {"Basic":"Undergraduate","2n Cycle":"Undergraduate", "Graduation":"Graduate", "Master":"Postgraduate", "PhD":"Postgraduate"}data["Ed_level"]=data["Education"].replace(edu_dict) -
现在,为了简化,我们使用映射字典将列重命名为更易于理解的术语:
col_rename_dict = {"MntWines": "Wines","MntFruits":"Fruits","MntMeatProducts":"Meat","MntFishProducts":"Fish","MntSweetProducts":"Sweets","MntGoldProds":"Gold"}data = data.rename(columns=col_rename_dict) -
现在,我们将删除一些冗余特征,专注于最清晰的特征,包括我们刚刚创建的特征。最后,我们将使用
describe方法查看统计描述分析:to_drop = ["Marital_Status", "Dt_Customer","Z_CostContact", "Z_Revenue", "Year_Birth", "ID"]data = data.drop(to_drop, axis=1)data.describe() -
统计数据显示,
Income和Age特征中存在一些不一致的情况,我们将通过可视化来更好地理解这些不一致之处。我们从Age的直方图开始:data["Age"].plot.hist(figsize=(12,6))

Figure 8.6: Age data
我们可以看到有一些异常值,年龄超过 120 岁,因此我们将移除这些异常值。
-
接下来,我们来看一下收入分布:
data["Income"].plot.hist(figsize=(12,6))

图 8.7:收入数据
同样,我们可以看到大多数收入低于 20,000,因此我们将限制消费水平。
-
接下来,我们通过设置
年龄的上限来剔除异常值,以避免数据不真实,并将收入限定为包含 99%的案例:prev_len = len(data)data = data[(data["Age"]<99)]data = data[(data["Income"]<150000)]new_len = prev_len - len(data)print('Removed outliers:',new_len)
上述代码将输出以下内容:
>>> Removed outliers: 11
-
现在,我们可以回过头来查看
年龄和消费数据分布,以更好地了解我们的客户。我们首先通过创建年龄特征的直方图来开始:data["Age"].plot.hist(figsize=(12,6))

图 8.8:无异常值的年龄分布
年龄集中在 50 岁左右,右偏,意味着我们的客户的平均年龄超过 45 岁。
data["Income"].plot.hist(figsize=(12,6))

图 8.9:无异常值的收入分布
查看消费分布,它呈正态分布,集中在 4,000 左右,略微向左偏斜。
-
接下来,我们将创建一个 Seaborn 成对图,以根据父母身份的颜色标签显示不同变量之间的关系:
sns.pairplot(data[["Income", "Recency", "Customer_For", "Age", "Spent", "Is_Parent"]], hue= "Is_Parent",palette= (["red","blue"]))

图 8.10:关系图
这些图形使我们能够快速观察不同变量之间的关系以及它们的分布。最明显的关系之一是消费与收入之间的关系,我们可以看到收入越高,支出越高,同时还观察到单亲家庭的支出大于非单亲家庭。我们还可以看到,最近一次消费行为较频繁的是父母,而单身消费者的消费频率较低。接下来,让我们来看一下特征之间的相关性(此时排除类别属性)。
-
我们将使用
corr方法创建相关矩阵,并通过numpy掩码仅显示数据的下三角部分。最后,我们将使用 Seaborn 方法来显示这些值:df_corr = data.corr()mask = np.triu(np.ones_like(df_corr, dtype=bool))df_corr = df_corr.mask(mask).round(3)fig, ax = plt.subplots(figsize=(16,16))cmap = colors.ListedColormap(["#682F2F", "#9E726F", "#D6B2B1", "#B9C0C9", "#9F8A78", "#F3AB60"])sns.heatmap(df_corr, cmap=cmap,annot=True,ax=ax)

图 8.11:变量相关性
这些相关性使我们能够更详细地探索变量之间的关系。我们可以看到,在平均值上,子女数量与消费支出呈负相关,而子女数量与最近消费行为呈正相关。这些相关性有助于我们更好地理解消费模式。
在接下来的章节中,我们将使用聚类概念将客户分成具有共同特征的群体。
创建客户细分
市场营销人员可以通过细分目标受众,更好地针对不同的受众子群体进行营销。产品开发和传播可能是这些努力的一部分。细分为企业带来如下好处:
-
为每个客户或用户群体在合适的沟通渠道上创建有针对性的营销传播
-
对正确的客户应用正确的定价选项
-
集中精力在最有利可图的客户身上
-
提供更好的客户服务
-
推广和交叉推广其他商品和服务
在本节中,我们将对数据进行预处理,以便应用聚类方法进行客户细分。我们将在这里列出用于预处理数据的步骤:
-
使用标签编码器对分类变量进行编码,将它们转换为数值列
-
使用标准缩放器对特征进行缩放,以规范化值
-
应用主成分分析(PCA)进行降维
所以,让我们按照这里的步骤进行操作:
-
首先,我们需要列出分类变量。在这里,我们将使用列名并检查列的
dtype,以仅获取对象列:object_cols = [c for c in data.columns if data[c].dtypes == 'object']print("Categorical variables in the dataset:", object_cols) -
接下来,我们将使用
sklearn的LabelEncoder函数对dtypes对象进行编码:LE = LabelEncoder()for i in object_cols:data[i]=data[[i]].apply(LE.fit_transform) -
我们对数据进行子集选择,并通过删除“已接受交易”和“促销”特征,对数值变量进行缩放:
scaled_ds = data.copy()cols_del = ['AcceptedCmp3', 'AcceptedCmp4', 'AcceptedCmp5', 'AcceptedCmp1','AcceptedCmp2', 'Complain', 'Response']scaled_ds = scaled_ds.drop(cols_del, axis=1) -
最后,我们可以应用缩放:
scaler = StandardScaler()scaler.fit(scaled_ds)scaled_ds = pd.DataFrame(scaler.transform(scaled_ds),columns= scaled_ds.columns )
这个数据集中有许多属性描述了数据。特征越多,在商业环境中正确分析它们就越困难。这些特征中的许多是冗余的,因为它们是相互关联的。因此,在将特征输入分类器之前,我们将对选定的特征进行降维处理。
降维是减少考虑的随机变量数量的过程。为了降低大型数据集的维度,通常使用一种名为 PCA 的技术。PCA 通过将大量变量压缩成一个较小的集合,同时保留较大集合中的大部分数据来工作。
随着数据集变量的减少,准确性自然会下降,但降维的答案是牺牲一些准确性换取简单性,因为机器学习算法可以通过更小的数据集更快速、更容易地分析数据,因为需要处理的无关因素更少。总之,PCA 的基本原则是尽可能保留信息,同时减少所收集数据中的变量数量。
本节中我们将应用的步骤如下:
-
使用 PCA 进行降维
-
在三维图中绘制降维后的 DataFrame
-
使用 PCA 进行降维,再一次
这将使我们能够以三维的方式可视化投影的细分。在理想的设置中,我们将使用每个成分的权重来理解每个成分所代表的意义,并更好地理解我们正在可视化的信息。为了简化起见,我们将专注于成分的可视化。以下是步骤:
-
首先,我们将启动 PCA,将维度或特征减少到三个,以简化复杂性:
pca = PCA(n_components=3)PCA_ds = pca.fit_transform(scaled_ds)PCA_ds = pd.DataFrame(PCA_ds, columns=(["component_one","component_two", "component_three"])) -
数据集中每个主成分(特征向量)所解释的变异度被统计为“解释的方差”。这只是指数据集中每个唯一主成分可以解释的数据变异度。
print(pca.explained_variance_ratio_)>>>>[0.35092717 0.12336458 0.06470715]
对于这个项目,我们将把维度减少到三维,这能够解释观察变量的 54%的总方差:
print('Total explained variance',sum(pca.explained_variance_ratio_))
>>>> Total explained variance 0.5389989029179605
-
现在我们可以将数据投影到 3D 图中,以查看点的分布:
x,y,z=PCA_ds["component_one"],PCA_ds["component_two"],PCA_ds["component_three"]fig = plt.figure(figsize=(10,8))ax = fig.add_subplot(111, projection="3d")ax.scatter(x,y,z, c="maroon", marker="o" )ax.set_title("A 3D Projection Of Data In The Reduced Dimension")plt.show()
上述代码将展示我们在三维中投影的维度:

图 8.12:PCA 变量的三维表现
由于现在的属性仅有三维,聚合聚类将用于执行聚类。凝聚聚类是一种层次聚类技术。在达到适当数量的簇之前,样本将会被合并。
聚类的过程涉及将数据点分组,使得每个组内的数据点比其他组中的数据点更相似。简而言之,目标是将具有相似特征的任何组分成簇。聚类的目标是在数据集中找到独特的组,或称为“簇”。该工具使用机器学习算法来构建组,组内的成员通常会共享相似的特征。
机器学习中使用的两种模式识别方法是分类和聚类。虽然这两个过程之间有一些相似之处,但聚类发现事物之间的相似性,并根据那些将它们与其他对象组区分开来的特征将其分组,而分类则使用预定的类别将对象分配到这些类别中。“簇”是这些集合的名称。
聚类涉及的步骤如下:
-
肘部法用于确定要形成的簇的数量
-
通过凝聚聚类进行聚类
-
通过散点图检查形成的簇
在 K 均值聚类中,理想的簇数是通过肘部法来确定的。通过肘部法绘制不同成本函数值下形成的簇的数量或 K:
-
肘部法是一种启发式方法,用于聚类分析,以估计数据集中存在的簇的数量。将解释的变异度作为簇的数量的函数进行绘图,该过程包括选择曲线的肘部作为适当的簇数,如以下代码片段所示:
fig = plt.figure(figsize=(12,8))elbow = KElbowVisualizer(KMeans(), k=(2,12), metric='distortion') # distortion: mean sum of squared distances to centerselbow.fit(PCA_ds)elbow.show()
这段代码将绘制肘部图,这是对所需簇数的一个良好估算:

图 8.13:肘部法
-
根据前面的单元,四个簇将是这个数据集的最佳选择。为了获得最终的簇,我们将拟合凝聚聚类模型,如下所示:
AC = AgglomerativeClustering(n_clusters=4)# fit model and predict clustersyhat_AC = AC.fit_predict(PCA_ds)PCA_ds["Clusters"] = yhat_AC -
最后,我们将向原始 DataFrame 中添加一个
Clusters特征,以便进行可视化:data["Clusters"]= yhat_AC -
现在,我们可以使用每个集群的颜色代码,在三维空间中可视化集群:
classes = [0,1,2,3]values = PCA_ds["Clusters"]colors = ListedColormap(['red','blue','green','orange'])fig = plt.figure(figsize=(10,8))ax = plt.subplot(projection='3d')scatter = ax.scatter(x, y,z, c=values, cmap=colors)plt.legend(handles=scatter.legend_elements()[0], labels=classes)
上述代码将显示一个三维可视化的 PCA 组件,颜色根据集群进行区分:

图 8.14:PCA 变量与集群标注
从中我们可以看到,每个集群在可视化中占据了一个特定的空间。现在我们将深入描述每个集群,以便更好地理解这些群体。
将集群理解为客户群体
为了严格评估获得的输出结果,我们需要评估所描述的集群。这是因为集群分析是一种无监督的方法,提取的模式应该始终反映现实,否则,我们可能只是在分析噪声。
消费者群体之间的共同特征可以帮助企业选择哪些商品或服务应该向哪些群体广告宣传,以及如何向每个群体进行市场营销。
为了做到这一点,我们将使用探索性数据分析(EDA)在集群的上下文中查看数据并做出判断。以下是步骤:
-
让我们首先检查集群组分布:
cluster_count = PCA_ds["Clusters"].value_counts().reset_index()cluster_count.columns = ['cluster','count']f, ax = plt.subplots(figsize=(10, 6))fig = sns.barplot(x="cluster", y="count", palette=['red','blue','green','orange'],data=cluster_count)

图 8.15:集群数量
各集群分布相对均衡,集群 0 占主导地位。可以清晰地看到,集群 1 是我们最大的客户群体,其次是集群 0。
-
我们可以使用以下代码来探索每个集群在目标营销策略上的消费:
f, ax = plt.subplots(figsize=(12, 8))pl = sns.scatterplot(data = data,x=data["Spent"], y=data["Income"],hue=data["Clusters"], palette= colors)pl.set_title("Cluster vs Income And Spending")plt.legend()plt.show()

图 8.16:收入与消费关系
在收入与消费关系图中,我们可以看到下一个集群的模式:
-
集群 0 是高消费和中等收入的群体
-
集群 1 是高消费和高收入的群体
-
集群 2 是低消费和低收入的群体
-
集群 3 是高消费和低收入的群体
-
接下来,我们将查看数据中每个产品的消费集群的详细分布。即,我们将探索消费模式。以下是代码:
f, ax = plt.subplots(figsize=(12,6))sample = data.sample(750)pl = sns.swarmplot(x=sample["Clusters"], y=sample["Spent"], color= "red", alpha=0.8 ,size=3)

图 8.17:每个集群的消费分布
从图 8.17中可以看到,集群 0 的消费分布较为均匀,集群 1 的消费集中在高消费上,而集群 2 和 3 则集中在低消费上。
-
接下来,我们将使用 Seaborn 创建集群的 Boxen 图,以找出每个集群的消费分布:
f, ax = plt.subplots(figsize=(12, 6))pl = sns.boxenplot(x=data["Clusters"], y=data["Spent"], palette=['red','blue','green','orange'])

图 8.18:每个集群的消费分布(Boxen 图)
我们可以使用不同的方式来可视化这些模式,使用 Boxen 图。
-
接下来,我们将创建一个特征来获取接受的促销活动总和,以便我们能够建模它们与不同集群之间的关系:
data["TotalProm"] = data["AcceptedCmp1"]+ data["AcceptedCmp2"]+ data["AcceptedCmp3"]+ data["AcceptedCmp4"]+ data["AcceptedCmp5"] -
现在,我们将绘制接受的总营销活动数量与集群之间的关系图:
f, ax = plt.subplots(figsize=(10, 6))pl = sns.countplot(x=data["TotalProm "],hue=data["Clusters"], palette= ['red','blue','green','orange'])pl.set_title("Total Promotions vs Cluster")pl.set_xlabel("Cluster")pl.set_ylabel("Count")

图 8.19:每个群体应用的促销活动
我们可以看到,尽管每个群体的促销活动没有显著的特征模式,但我们可以看到群体 0 和群体 2 是应用促销活动数量最多的两个群体。
-
现在,我们可以将每种群体类型的购买交易数量可视化:
f, ax = plt.subplots(figsize=(12, 6))pl = sns.boxenplot(y=data["NumDealsPurchases"],x=data["Clusters"], palette= ['red','blue','green','orange'])pl.set_title("Purchased Deals")

图 8.20:每个群体购买的交易数量
促销活动未能广泛传播,但交易却非常成功。群体 0 和群体 2 的结果是最好的。群体 1 是我们的顶级客户之一,但他们对促销活动不感兴趣。没有什么能强烈吸引群体 1。
现在,群体已经创建并且其购买模式已经被分析,我们来看看这些群体中的每个成员。为了确定谁是我们的明星客户,谁需要零售店的营销团队进一步关注,我们将对已开发的群体进行分析。
考虑到群体特征,我们将绘制一些代表客户个人特征的元素。我们将根据结果得出结论。
-
我们将使用 Seaborn 联合图来可视化不同变量之间的关系和分布:
sns.jointplot(x=data['Education'], y=data["Spent"], hue =data["Clusters"], kind="kde", palette=['red','blue','green','orange'],height=10)

图 8.21:每个群体的消费与教育水平分布
群体 0 主要集中在中等教育水平,但在高学历方面也有一个峰值。群体 2 在教育方面是最低的。
-
接下来,我们将看看家庭规模:
sns.jointplot(x=data['Family_Size'], y=data["Spent"], hue =data["Clusters"], kind="kde", palette=['red','blue','green','orange'],height=10)

图 8.22:每个群体的消费与家庭规模分布
群体 1 代表小家庭规模,群体 0 代表夫妻和家庭。群体 2 和群体 3 的分布较为均匀。
-
现在我们来看看消费与客户群体的关系:
sns.jointplot(x=data['Customer_For'], y=data["Spent"], hue =data["Clusters"], kind="kde", palette=['red','blue','green','orange'],height=10)

图 8.23:每个群体的消费与客户分布
群体 3 是由年长客户组成的群体。尽管群体 0 的消费最高,但值得注意的是,它在“自客户成为用户以来的天数”这一维度上有一个向左偏移的趋势。
sns.jointplot(x=data['Age'], y=data["Spent"], hue =data["Clusters"], kind="kde", palette=['red','blue','green','orange'],height=10)

图 8.24:每个群体的消费与年龄分布
群体 0 是拥有较年长客户的群体,而拥有最年轻客户的是群体 2。
总结
本章我们进行了无监督聚类。经过降维后,使用了凝聚层次聚类方法。为了更好地基于客户的家庭结构、收入和消费习惯对群体进行分析,我们将用户分为四个群体。这一方法可用于制定更有效的营销计划。
在接下来的章节中,我们将深入研究使用时间序列数据预测销售,以便能够根据一组历史销售确定收入预期,并理解它们与其他变量的关系。
第九章:使用历史降价数据预测销售
许多零售商别无选择,只能跟随行业趋势,增加折扣和特别促销活动,以维持在当今过度饱和市场中的竞争优势。这是因为大多数新客户更加敏感价格,会在多个网站上比价,寻找特定商品的最佳优惠。
然而,折扣也有其弊端。虽然促销活动能加速销售,但企业在没有进行充分研究的情况下提供折扣,也面临着进一步利润损失的风险。零售数据建模的一大挑战是需要根据有限的历史数据做出决策。如果圣诞节一年只有一次,那么看到战略决策对底线的影响的机会也只有一次。
本章我们将深入研究行业领先者最新的降价和折扣营销策略,并展示如何在保持利润率的同时实施这些策略。我们将使用来自 45 家超市的历史销售信息,这些超市分布在多个区域。虽然普遍认为这些降价对销售有影响,但很难预见到哪些部门会受到影响,以及影响的程度。因此,我们的目标是预测每个商店中各个部门的销售情况,这些商店有多个部门。选定的假期降价事件也被纳入数据集,以增加难度。
本章中,我们将学习以下内容:
-
考虑所有影响销售的因素,确定销售表现
-
探索销售数据,确定影响销售的因素
-
研究促销降价在假期期间对销售的影响
-
在特定降价应用下预测销售
我们将使用反映实际真实数据的数据,尽量接近现实情况。
技术要求
为了能够跟随本章中的步骤,你需要满足以下要求:
-
一个运行 Python 3.7 及以上版本的 Jupyter notebook 实例。如果你有 Google Drive 账户,你也可以使用 Google Colab notebook 来运行这些步骤。
-
理解基本的数学和统计概念
创建有效的降价策略
降价是指产品因未能以原定售价售出而进行的降价处理。一个折扣的例子是,如果你有一件商品,经过一个月的销售低迷后,你决定降价 20%。即使你只是失去了收入,这也会吸引更多人以更低的价格购买它。由于该商品原本销售不佳,提供一个合适的折扣价格通常会导致原本不会发生的销售。然而,你会发现,通常需要根据销售低迷来调整你的降价策略,可能会将折扣从 20% 调整到 30%、40% 等等。时机尤其重要,因为你希望在商品仍然符合季节、趋势等背景时卖出它。
Markdown 与折扣需要区分。在零售中,折扣是根据购买者的客户类型对商品或交易价格的降低。例如员工折扣、老年人折扣和常客折扣就是几种例子。由于获得折扣,许多零售商认为这使得顾客更有可能回到他们的商店,而不是去竞争对手的店。
在本章中,我们将使用位于美国不同地点的商店的历史销售数据。每个商店有多个部门,我们将预测整个部门的销售情况。此外,公司全年举办多次促销 markdown 销售活动,其中超级碗、劳动节、感恩节和圣诞节是四个最大节假日。
此外,沃尔玛全年会举行几次促销性 markdown 活动。这些 markdown 活动通常会在重要节假日前进行,其中四个最大的节假日分别是超级碗、劳动节、感恩节和圣诞节。包括这些假期的周数在评估中比非假期周数的权重高五倍。此竞赛面临的挑战之一是,在没有完整/理想的历史数据的情况下,如何建模假期周数中 markdown 对销售的影响。
在接下来的部分,我们将分析数据以理解变量及其分布,并将其结构化以便于可视化和用于机器学习。
数据分析
我们提供了 3 个文件,分别包含关于 45 家商店的匿名信息,指出商店的类型和规模、涵盖 2010-02-05 至 2012-11-01 的历史训练数据,以及一个包含与给定日期的商店、部门和区域活动相关的附加数据的文件。
在下一个示例中,使用了以下 Python 模块:
-
pandas:一个用于数据分析和数据处理的 Python 包
-
NumPy:这是一个库,支持大规模的多维数组和矩阵,并提供丰富的高阶数学函数用于对这些数组进行操作。
-
Seaborn 和 Matplotlib:用于有效数据可视化的 Python 包
-
我们将通过导入库开始分析,并通过读取所有文件并使用
read_csvpandas 函数的parse_date选项解析Date列来开始分析数据:import pandas as pd # data processingimport matplotlib.pyplot as plt # visualizationimport seaborn as sns # visualizationtrain=pd.read_csv("/content/train.csv",parse_dates=["Date"])features=pd.read_csv("/content/features.csv",parse_dates=["Date"])stores=pd.read_csv("/content/stores.csv") -
现在所有文件已加载完毕,我们可以通过查看训练文件的前几行开始:
train.head()

图 9.1:商店、日期和部门的每周销售训练文件
该文件包含每个商店和部门的每周销售数据,并标明该日是否为假期。
我们还将引入一些日期变量,以便使分析更为简便:
train['Year']=train['Date'].dt.year
train['Month']=train['Date'].dt.month
train['Week']=train['Date'].dt.week
train['Day']=train['Date'].dt.day
-
我们将要查看的下一个文件包含关于每家商店类型和规模的信息。我们可以稍后使用这些信息来评估降价的效果是否会根据商店特征的不同而有所变化:
stores.head()

图 9.2:商店类型和规模数据
-
我们可以通过使用 Seaborn 的箱形图,开始可视化销售类型和规模的分布。在接下来的代码块中,我们将创建一个数据子集,将商店数据的类型和规模列进行拼接。然后,我们使用 Matplotlib 创建一个 8x6 英寸的图形,图形中将包含 Seaborn 图:
data = pd.concat([stores['Type'], stores['Size']], axis=1)f, ax = plt.subplots(figsize=(8, 6))fig = sns.boxplot(x='Type', y='Size', data=data)
代码将展示商店规模的分布情况。

图 9.3:商店类型和规模数据
在规模方面,我们可以看到有三种类型的商店,其中 A 类型是最大的。看到分位数之间的变化以及异常值是很有趣的。
-
最后,接下来的文件包含更多关于每一天和商店的上下文数据。这些信息包括温度、燃料价格、应用的不同降价类型,分为五种类型,还有消费者物价指数、失业率,以及关于该天是否是节假日的信息:
features.head()

图 9.4:按商店分类的上下文数据
我们将在 train 的 pandas DataFrame 中使用 merge 方法,来附加关于日期、商店规模和类型的信息,这些数据来源于商店的 DataFrame:
train=train.merge(stores, on='Store', how='left')
train.head()
之前的代码将合并数据并显示前几行。

图 9.5:带有节假日、商店类型和规模信息的每周销售数据
我们现在可以通过商店类型的分布来可视化每周销售数据。我们将创建一个包含类型和每周销售列的数据子集,创建一个 8x6 英寸的 Matplotlib 图形,然后填充一个 Seaborn 箱形图:
data = pd.concat([train['Type'], train['Weekly_Sales']], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x='Type', y='Weekly_Sales', data=data, showfliers=False)
这将根据商店类型生成箱形图的可视化。

图 9.6:按类型的每周销售数据箱形图
关于商店的类型,我们可以看到,尽管 A 类型是最小的,但它的销售中位数却是最高的。
-
我们现在将创建一个按商店 ID 分布的销售数据箱形图。我们从训练数据中提取商店和每周销售列,创建一个 25x8 英寸的 Matplotlib 图形,然后显示一个 Seaborn 箱形图,x 轴为商店 ID,按类型进行颜色区分:
data = pd.concat([train['Store'], train['Weekly_Sales'], train['Type']], axis=1)f, ax = plt.subplots(figsize=(25, 8))fig = sns.boxplot(x='Store', y='Weekly_Sales', data=data, showfliers=False, hue="Type")
该代码将生成每周按商店分布的箱形图。

图 9.7:按商店 ID 和类型的每周销售数据
从箱线图中,我们可以看出,门店可能是提供销售信息的变量,因为在所有类型中,销售的上边界有一致的高值模式。这表明在某些时刻,需求在短时间内增加。此外,数据中没有出现需求在中位数以下大幅下降的情况。
门店数据中包含了许多关于类型、规模和部门的内在信息。因此,我们将探讨节假日如何影响销售。
-
我们重复相同的步骤:创建数据子集、绘制 Matplotlib 图形和 seaborn 箱线图,但现在的 hue 变量设置为IsHoliday列:
data = pd.concat([train['Store'], train['Weekly_Sales'], train['IsHoliday']], axis=1)f, ax = plt.subplots(figsize=(25, 8))fig = sns.boxplot(x='Store', y='Weekly_Sales', data=data, showfliers=False, hue="IsHoliday")
这段代码将展示更多层次的销售信息分布。

图 9.8:按门店 ID 划分的每周销售数据,包括节假日与不包括节假日
从数据中可以看出,Holiday 和 Store 之间没有显著的关系,除了在节假日当天销售略有上升。这可能意味着大部分销售是在节假日前几天完成的。
-
现在,让我们深入探讨按部门类型划分的销售差异。为此,我们将创建一个数据子集,作为每个部门每周销售的分组,然后计算均值并重置索引。我们使用条形图展示数据,因为如果使用箱线图,难以一眼看出部门之间的差异,原因是部门数量太多:
data= train[['Dept','Weekly_Sales']].groupby('Dept').mean().reset_index()f, ax = plt.subplots(figsize=(25, 8))fig = sns.barplot(x='Dept', y='Weekly_Sales', data=data)
这项可视化将为我们提供按部门划分的销售视角。

图 9.9:按部门划分的每周中位销售额
我们可以看到不同部门的销售存在差异。某些部门的收入高度集中,这可能促使我们考虑排除那些销售较小的部门。但这可能并不合适,因为这些部门可能吸引那些只需要特定商品的顾客前来,并最终购买其他商品。你可以使用如 Apriori 算法等方法进一步研究这一点。
-
接下来,我们将可视化按月份划分的每周销售,以窥探数据的季节性。我们创建包含相关列的数据子集,并使用箱线图可视化数据:
data = pd.concat([train['Month'], train['Weekly_Sales']], axis=1)f, ax = plt.subplots(figsize=(8, 6))fig = sns.boxplot(x='Month', y="Weekly_Sales", data=data, showfliers=False)
以下是月度销售的分布情况。

图 9.10:按月份划分的每周销售数据
我们可以通过可视化节假日在月度销售中的表现,进一步探索月度销售数据。我们通过将箱线图的 hue 值设置为IsHoliday来实现这一点:
data = pd.concat([train['Month'], train[
'Weekly_Sales'],train['IsHoliday']], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x='Month', y="Weekly_Sales", data=data, showfliers=False, hue='IsHoliday')
以下图表展示了按月份和节假日划分的销售分布。

图 9.11:按月份区分节假日的每周销售数据
对于大多数节假日,假期当天的销售稍高于常规日,圣诞节除外,圣诞节是在 12 月。
现在,我们可以创建一个 seaborn 的 relplot 来按周可视化数据:
data = pd.concat([train['Week'], train['Weekly_Sales'],train['Type']], axis=1)
sns.relplot(x='Week', y="Weekly_Sales", data=data, hue='Type',kind='line',height=8, aspect=2.2)
这个图表显示了中位数和置信区间。

图 9.12: 随时间变化的每周销售
通过查看 relplot,我们可以看到第 40 周到第 50 周之间销售的急剧增长。
-
我们可以使用箱线图来可视化每周销售数据,将
Week放在 x 轴,将Weekly_Sales列放在 y 轴:data = pd.concat([train['Week'], train['Weekly_Sales']], axis=1)f, ax = plt.subplots(figsize=(20, 6))fig = sns.boxplot(x='Week', y="Weekly_Sales", data=data, showfliers=False)
该图表显示了每年按周的中位数和置信区间。

图 9.13: 每年按周的销售
我们现在可以看到,年初的假期带来了小幅增长,而年末的假期则带来了最大的销售增长,包括随后的一个星期的急剧下降。
-
我们现在可以使用 seaborn 的 relplot 绘制数据,使用日期和每周销售数据,并通过 hue 参数按商店类型进行区分:
data = pd.concat([train['Date'], train['Weekly_Sales'],train['Type']], axis=1)sns.relplot(x='Date', y="Weekly_Sales", data=data, hue='Type',kind='line',height=8, aspect=2.2)
上述代码绘制了带有置信带的数据。

图 9.14: 随时间变化的每周销售
我们可以观察到,两个年份中的数据呈现出相同的模式,这可以通过进一步分析数据的季节性来解释。季节性是指在某些时间段内反复出现的模式。购买模式的周期在每年末重复,并在圣诞节前几周出现急剧增长。
-
我们的下一步将是分析每周销售的均值,按周和应用的降价进行区分。为此,首先我们将每周销售按周进行分组并计算均值,然后打印形状进行合理性测试:
data_store = train[['Week','Weekly_Sales']].groupby(['Week']).mean().reset_index()data_store.shape>>>(52, 2)
然后,我们可以从特征数据框中获取有关该特定周应用的降价信息,并计算均值:
data_features = features[['Week','MarkDown1','MarkDown2',
'MarkDown3','MarkDown4','MarkDown5']].groupby(['Week']).mean().reset_index()
data_features.shape
>>>(52, 6)
-
现在,我们可以合并两个数据框,并获得每周的中位销售信息以及所应用的降价类型:
data_store = data_store.merge(data_features,on=['Week'],how='left').fillna(.0)data_store.index = data_store['Week']data_store = data_store.drop(['Week'],axis=1)data_store.head()

图 9.15: 每周中位数销售和每周降价
使用这些信息,我们现在可以绘制数据,查看折扣与每周销售之间的影响:
data_store.plot(figsize=(20,8))

图 9.16: 每周中位数销售和每周降价
从数据中我们可以看到,许多降价是在圣诞节前立即实施的,然后在接下来的几周内继续进行。其原因可能是,这些降价是为了减少在假期期间未售出的库存。
-
现在,我们将查看销售与描述每一天和每个商店条件的特征之间的关系。为此,我们按周对销售数据进行分组并计算均值。然后,我们对特征数据执行相同操作,最后将两个 DataFrame 合并:
data_features = features.drop(['Store','Date','MarkDown1','MarkDown2','MarkDown3','MarkDown4','MarkDown5'],axis=1).groupby(['Week']).mean().reset_index()data_store = train[['Week','Weekly_Sales']].groupby(['Week']).mean().reset_index()data_store = data_store.merge(data_features,on=['Week'],how='left').fillna(.0)data_store.head()
前面的代码将展示我们合并后的数据。

图 9.17:每周中位数上下文数据
-
我们现在可以利用这些信息构建一个 seaborn 配对图来可视化任何关系。我们排除了
Week和IsHoliday变量,后者是因为类别变量难以通过这种类型的图进行可视化:sns.pairplot(data_store.drop(['Week','IsHoliday'],axis=1))
代码将创建一个显示数值变量之间关系的图表。

图 9.18:特征配对图
我们可以看到,尽管我们能够更好地理解这些关系,但大部分每周销售的均值每周相似,这使得与 CPI、失业率或油价之间的有意义关系难以区分。我们应该不对数据进行分组,重新进行此操作,这样会得到更多信息,但需要更多的处理时间。
- 为了预测销售,首先,我们需要处理数据并做出一些决策。第一个决策是我们希望在哪个时间层级进行预测。在我们的案例中,我们将使用
predict进行每周销售的预测。我们会将所有数据合并到一个 DataFrame 中,在该 DataFrame 中,每个数据点都附加了关于商店以及那天条件的信息。
我们还将 Type 列转换为一-hot 向量表示,使用 pd.get_dummies 函数:
data = train.drop(['Year','Month','Week'],axis=1).merge(features.drop(['IsHoliday','Week'],axis=1),on=['Store','Date'])
data = pd.concat([data.drop(['Type'],axis=1),
pd.get_dummies(data['Type'])],axis=1).fillna(0)
data['IsHoliday'] = data['IsHoliday'].astype(int)
data.head()
我们可以看到 DataFrame 中创建的列。

图 9.19:没有编码变量的数据集
这种数据配置使我们能够扩展探索,分析变量之间的相关性,进而为使用数据预测销售奠定基础。
下一段代码将使用 pandas DataFrame 的 corr() 方法分析变量之间的相关性,我们将使用 numpy 包来屏蔽冗余的值,最后使用 seaborn 的 heatmap 可视化工具绘制数据:
import numpy as np
df_corr = data.drop(['Date'],axis=1).corr()
mask = np.triu(np.ones_like(df_corr, dtype=bool))
df_corr = df_corr.mask(mask).round(3)
fig, ax = plt.subplots(figsize=(16,16))
sns.heatmap(df_corr, annot=True,ax=ax)
上述代码将创建变量相关性的可视化图。

图 9.20:变量相关性
我们可以看到一些有趣的相关性,例如,折扣 2 和 3 的大小与商店大小之间的正相关性,以及折扣 1 和 4 的大小之间的正相关性。
-
最后,我们可以展示变量与每周销售之间的相关性程度:
f, ax = plt.subplots(figsize=(20, 6))data = df_corr['Weekly_Sales'].dropna().sort_values(ascending=False)sns.barplot(x=data.index,y=data)
以下条形图将展示我们最相关的变量。

图 9.21:排序后的变量相关性
我们可以看到每周销售受商店大小的正向影响最大,其次是 markdowns 5 和 1。它与失业率和消费者价格指数(CPI)呈负相关,这也是合理的。
现在我们对数据有了更好的理解,我们将开始进行每周销售预测的过程。
使用 Prophet 预测销售
预测时间序列可能是一个具有挑战性的任务,因为你可以使用许多不同的方法,并且每种方法都有许多不同的超参数。Prophet 库是一个开源库,旨在为单变量时间序列数据集进行预测。它易于使用,并设计为自动找到一组良好的超参数,使模型能够为具有标准趋势和季节性结构的数据做出有竞争力的预测。我们将学习如何使用 Facebook Prophet 包来预测每周销售时间序列:
-
首先,我们将导入库并创建一个包含所有描述为连续变量或独热表示的特征的数据集:
from fbprophet import Prophetdata = train.drop(['Year','Month','Week'],axis=1).merge(features.drop(['IsHoliday','Week'],axis=1),on=['Store','Date'])data = pd.concat([data.drop(['Type'],axis=1),pd.get_dummies(data['Type'])],axis=1).fillna(0)data['IsHoliday'] = data['IsHoliday'].astype(int) -
过滤 Store 1 的结果,因为我们将训练一个能够学习每个特定商店行为的模型:
data = data[data.Store==1] -
对于 Prophet 包,我们需要准备一些具有特定数据类型的预期列名。在这种情况下,我们需要传递
ds和y变量,第一个是datetime类型的列:data['ds']= pd.to_datetime(data['Date'])data = data.sort_values(['ds'],ascending='True')data['y']=data['Weekly_Sales']
现在我们已经创建了具有正确数据类型的列,我们可以删除冗余列并显示前几行:
data = data.drop(['Date','Weekly_Sales'],axis=1).reset_index(drop=True)
data.head()

图 9.22:用于模型训练的数据
现在我们可以将数据分为训练数据集和测试数据集。
x_train = data[:-1000]
x_test = data[-1000:]
-
最后,我们可以定义将使用的模型。在这种情况下,我们需要建立线性增长和乘法季节性。在这种情况下,我们还包括了每周季节性:
model = Prophet(changepoint_prior_scale=0.05,interval_width=0.95,growth = 'linear',seasonality_mode = 'multiplicative',weekly_seasonality=True,changepoint_range=0.9)
在训练预测模型之前,我们可以添加使用附加变量的回归器。
这里,add_regressor 参数是训练数据框中附加变量的列名:
for c in ['Dept','IsHoliday',
'CPI','Fuel_Price','MarkDown1',
'MarkDown2','MarkDown3','MarkDown4','MarkDown5', 'Unemployment']:
print(c)
model.add_regressor(name=c, prior_scale=None, standardize='auto', mode='additive')
请注意,这里我们正在将附加变量作为回归器添加到模型中。这些变量应具有未来(测试)数据的值。一个关键因素是,我们对所有变量使用相同的参数,而建议的做法是对每个变量单独进行调优,尽管根据每个变量的具体情况来选择这些参数超出了本分析的范围。
最后,我们可以拟合模型:
model.fit(data[['ds','y','Dept','IsHoliday','CPI',
'Fuel_Price','MarkDown1','MarkDown2','MarkDown3',
'MarkDown4','MarkDown5', 'Unemployment']])
-
模型训练完成后,我们可以使用它对测试数据进行预测,以评估预测的表现:
forecast = model.predict(x_test[['ds','Dept','IsHoliday','CPI','Fuel_Price','MarkDown1','MarkDown2','MarkDown3','MarkDown4','MarkDown5', 'Unemployment']])
我们可以访问结果预测,展示每个日期的预测值,以及上下边界:
print(forecast[['ds', 'yhat', 'yhat_lower',
'yhat_upper']].head())

图 9.23:预测数据
-
现在,我们可以将这些结果与测试数据中 y 的实际值连接起来,以可视化结果。为便于可视化,生成的曲线采用七天滞后的滚动平均进行平滑处理:
forecast_data = pd.concat([forecast[['yhat','yhat_lower', 'yhat_upper']],x_test['y'].reset_index(drop=True)],axis=1)forecast_data.rolling(window=7).mean().plot(figsize=(20,8))
上述代码将生成一个可视化图,我们可以看到实际销售与实际结果的对比。

图 9.24:预测数据与实际值对比
我们可以看到,除了个别情况外,大多数情况下,预测值与实际值接近,极少出现超出上限的情况,但从未低于下限。
总结
本章我们已深入探讨了销售分析和降价应用的细节。如前所述,降低商品价格以增加销售和减少库存相关成本之间存在一定的权衡,而这种降价会导致一定的收入损失。在零售的情况下,这些结果受到多个因素的影响,其中包括某个店铺的位置、环境和经济条件,以及季节性因素,正如我们通过不同节假日销售分析所看到的那样。
销售和降价的分析与预测可以帮助微调应用的降价策略,从而维持盈利性和销售之间的平衡,并深入理解涉及的变量及其相对影响,这有助于设计更好的降价策略。
在下一章,我们将深入探讨电子商务零售商如何学习消费者行为、理解消费者细分、预测消费者生命周期价值,以及预测哪些用户最终会购买。
第十章:网页分析优化。
数据驱动的营销优化是一种重视能够通过可信和可验证数据支持的决策的营销分析方法。它高度重视那些可以通过实证证据证明的选择,无论是流量来源、页面浏览量,还是每次会话的时间。数据收集、处理和解释的有效性对最大化营销结果是数据驱动方法成功的关键因素。
在本章中,我们将学习以下内容:
-
理解网页分析是什么。
-
如何使用网页分析数据来改善业务运营。
-
基于网页分析数据计算用户的客户生命周期价值(CLV)。
-
基于这些历史数据预测用户的 CLV。
让我们确定了解这些步骤并跟随本章所需的要求。
本章涵盖以下主题:
-
理解网页分析。
-
使用网页分析来改善商业决策。
-
探索数据。
-
计算 CLV。
-
预测客户收入。
技术要求。
为了能够跟随本章中的步骤,你需要满足以下要求:
-
一个运行 Python 3.7 及以上版本的 Jupyter Notebook 实例。如果你有 Google Drive 账户,也可以使用 Google Colab 笔记本来运行这些步骤。
-
理解基本的数学和统计学概念。
理解网页分析。
对于电子商务,理解用户行为至关重要。网页分析的数据通常显示在可以根据用户画像或时间段等因素调整的仪表板上。这些仪表板随后用于做出产品和市场决策,因此这些数据的准确性至关重要。数据可以分为多个组别,包括以下内容:
-
通过检查访问次数、新访客与回访者的比例、访客来源,以及他们使用的浏览器或设备(桌面与移动设备),可以了解受众数据。
-
常见的登录页面、常访页面、常退出页面、每次访问所花费的时间、每次访问的页面数量以及跳出率,都可以用于研究受众行为。
-
活动数据用于了解哪些活动带来了最多的流量,哪些网站作为推荐来源表现最佳,哪些关键词搜索导致了访问,以及活动媒介的细分,如电子邮件与社交媒体的对比。
然后,销售、产品和营销团队可以使用这些信息了解用户如何与产品互动,如何定制信息,以及如何改进产品。
用于跟踪和分析网站流量的最佳和最有效的工具之一是 Google Analytics。您可以从中了解到关于您网站访问者的大量信息,包括他们是谁、他们在寻找什么以及他们是如何找到您的。每家希望开发和改善其在线存在的公司都应该使用 Google Analytics。
Google Analytics 将为您提供的最重要信息列在此处:
-
您的访问者来自哪里——如果您针对特定受众,这一点非常重要。
-
人们如何找到您的网站是了解您的努力是否成功的关键信息。它表明用户是直接访问您的网站,通过另一个网站的链接(如 Twitter 或 Facebook),还是通过搜索引擎到达的。
-
访问者使用的浏览器——通过了解他们使用的浏览器,您可以决定应该集中注意力的浏览器。
-
人们用来找到您的网站的搜索引擎术语是 SEO 的重要信息。了解人们如何通过搜索引擎找到您的网站的搜索术语有助于评估您的进展。
在接下来的部分,我们将深入探讨如何利用 Google Analytics 数据来了解我们的客户群体。
使用网络分析来改善业务运营
通过利用数字分析和用户输入的见解,我们可以通过转化率优化增加我们的网站和应用程序的性能。通过利用当前的流量并最大化它,以期提高销售、线索或任何其他目标。
借助数字分析仪表板和分析,我们可以监控用户活动,包括他们的来源、他们在我们的网站或应用程序上浏览的页面,以及他们如何在各个页面之间移动。通过内容和用户行为分析,我们可以确定哪里有最大的潜力,以及需要进行哪些变更来实现指定的目标和目标。
通过定义和执行测量计划,企业网站或应用的标签具有上下文意义。这使得公司可以进行优势、劣势、机会和威胁(SWOT)分析,从而明确目标和目标,并指示必须通过特定内容和消息来定位内外网站或应用的用户细分。
当识别出机会或威胁时,也可以进行 A/B 或多元测试。通过这种方式,我们可以向不同用户群体展示网站功能的两个(或多个)不同迭代版本,然后评估哪个表现更好。我们可以使用这种方法做出基于数据的决策,而忽略季节性影响等因素。
现在我们已经了解了应用程序的背景,请从查看数据集并了解我们的需求、我们的目标以及分析的局限性开始。
探索数据
在下一个示例中,我们将使用以下 Python 模块:
-
pandas:用于数据分析和数据操作的 Python 包。
-
NumPy: 这是一个支持大规模多维数组和矩阵的库,并提供了一大批高层次的数学函数来操作这些数组。
-
Statsmodels: 一个 Python 包,提供了对 SciPy 的补充,用于统计计算,包括描述性统计和统计模型的估计与推断。它提供了许多不同统计模型的估计类和函数。 -
Seaborn 和 Matplotlib: 用于有效数据可视化的 Python 包。
我们将按以下步骤开始:
-
以下代码块将加载所有必需的包,加载数据并显示前五行。为了提高可读性,我们将限制显示的最大行数为
20,将最大列数限制为50,并将浮动数字显示为2位小数:import pandas as pdpd.options.display.max_rows = 20pd.options.display.max_columns = 50pd.options.display.precision = 2df = pd.read_csv('google_analytics_clean_data.csv')df["date"] = pd.to_datetime(df["date"], format="%Y%m%d") # seting the column as pandas datetimedf.head()
上面的代码将加载文件,将日期列转换为正确的数据类型,并提示我们查看前几行。

图 10.1:Google Analytics 示例数据
我们可以看到,这是一个可以从 Google Analytics 获得的数据演示,因为某些列不可用。
-
我们将探索这组列:
df.columns
这行代码将显示文件中所有列的名称。

图 10.2:列名
根据我们从 Kaggle 竞赛中获得的有关列和数据的信息,我们可以描述数据集中的列:
-
fullVisitorId: 每个用户的标识符。 -
channelGrouping: 客户被重定向的渠道。 -
date: 访问的日期。 -
Device: 使用的设备类型。 -
geoNetwork: 客户的位置。 -
socialEngagementType: 客户是否在社交媒体上参与互动? -
trafficSource: 该字段显示流量来源。 -
visitId: 特定访问的标识符。 -
visitNumber: 特定客户的会话计数。
-
现在我们已经有了有关列的信息,可以绘制收入列以查看它们的分布:
df['totals.transactionRevenue'].plot(figsize=(12,4))
这行代码将使用 pandas 的 plot 方法显示列的分布。

图 10.3:收入分布
许多公司发现 80/20 法则是成立的:只有一小部分客户创造了大部分收入,通过查看数据我们可以验证这一点,少数客户创造了大部分收入。营销团队面临的问题是如何将适当的资金分配到促销活动中。在这种情况下,比例明显更低。
- 数据点之间的统计关联通过关系图展示。数据可视化对于发现趋势和模式至关重要。这张图为用户提供了额外的轴级函数,利用语义子集映射,可以展示两个变量之间的关系。以长格式模式传递整个数据集将对重复值(每年)进行汇总,以展示均值和 95%的置信区间。
在这里,我们使用seaborn包创建一个带有 95%置信区间区域的关系图,展示收入列:
import seaborn as sns
sns.relplot(x='date', y='totals.transactionRevenue', data=df,kind='line',height=8, aspect=2.2)
这展示了带有置信区间的交易分布,如下所示:

图 10.4:带有置信区间的收入分布
这里我们看到的一个问题是,由于数值差异,数据难以观察,因此我们将实现对数尺度。对于广泛的数值范围,通常会使用一种非线性尺度,称为对数尺度。每个区间的增量是以对数的底数为因子的,而不是等量增加。常用的对数尺度有以 10 为底和以 e 为底的尺度。
有时,显示的数据比其余数据大得多或小得多——当值之间的百分比变化显著时,对数尺度也可能很有帮助。如果可视化中的数据范围非常广泛,我们可以使用对数尺度。
另一个好处是,当显示较小的价格上涨或下跌时,对数定价尺度比线性定价尺度表现得更好。它们可以帮助你确定价格必须上涨或下跌多少,才能达到买入或卖出的目标。然而,如果价格接近,对数价格尺度可能会变得拥挤且难以读取。使用对数尺度时,当值之间的百分比变化相同时,尺度上的价格之间的垂直距离是相等的。
-
在这里,我们将使用
numpy库在收入列上实现对数尺度:import numpy as npdf['totals.transactionRevenue'] = np.log(df['totals.transactionRevenue'])df['totals.transactionRevenue'].plot(figsize=(12,4))
在这里,我们可以看到在对数尺度上的交易。

图 10.5:对数尺度收入
-
我们现在可以使用关系图更好地可视化带有置信区间的对数交易值:
sns.relplot(x='date', y='totals.transactionRevenue', data=df,kind='line',height=8, aspect=2.2)
我们可以通过relplot获得更好的可视性,它将绘制均值数据为一条线,并显示 95%的数据所在的置信区间。

图 10.6:带有置信区间的对数尺度收入
-
另一种可视化方法是使用散点图,这对于识别离群值非常有用:
import matplotlib.pyplot as plt # visualizationdata = df.groupby("fullVisitorId")["totals.transactionRevenue"].sum().reset_index()f, ax = plt.subplots(figsize=(12, 6))fig =sns.scatterplot(x='fullVisitorId', y='totals.transactionRevenue',size=4,alpha=.8,color='red', data=data)
散点图向我们展示了有一些离群值。

图 10.7:作为散点图的交易
在这里,我们可以更清楚地看到,只有少数几个用户通过他们的订单创造了极高的收入。
-
现在,我们可以看看前 50 名客户的支出模式:
top_50_customers = data.sort_values(['totals.transactionRevenue'],ascending=False).head(50)top_50_customers['totals.transactionRevenue'].plot.bar(figsize=(15,6))
接下来是一个关于前几大客户的条形图。

图 10.8:最高收入的用户
从这张图中我们可以确认,用户26437是我们最大的客户。
-
使用 Google Analytics 查找商品和服务的潜在市场是该工具的另一个精彩应用。你可以查看按国家分列的访问量和转化率,选择重点关注的地区,并决定在哪些地方扩展,尤其是在你的业务面向全球或考虑全球化时。在这里,我们可以分析用户群体中的主要国家:
global_countries = df['geoNetwork.country'].value_counts().head(20)global_countries.plot.bar(figsize=(16,6))
上面的代码将展示用户来自的国家。

图 10.9:所有国家
我们可以看到,绝大多数用户集中在美国。
-
现在,让我们关注一下我们的顶级客户,并查看他们来自哪些地方。我们可以通过遮罩在前 50 名用户名单中的用户,然后复制前面的图形来实现:
top_50_data = df[df.fullVisitorId.isin(top_50_customers.fullVisitorId)]top_50_countries = top_50_data['geoNetwork.country'].value_counts()top_50_countries.plot.bar(figsize=(12,6))
上面的代码将以条形图的形式展示前几大国家。

图 10.10:最大客户的国家
再次,我们可以确认最大的客户来自美国,其次是肯尼亚和日本。
-
现在,我们将分析有多少访客实际上进行了转化,也就是他们实际购买了某些商品:
zero_revenue_users = (data["totals.transactionRevenue"]==0).sum()print("Number of unique customers with non-zero revenue: ", len(data)-zero_revenue_users, "and the ratio is: ", zero_revenue_users / len(data))>>> Number of unique customers with non-zero revenue: 6876 and the ratio is: 0.9264536003080478
比例上,约有 8% 的用户实际在网站上进行了购买,这个比例不错。
-
现在,我们将开始清理数据的过程,首先查找只有一个值的分类列。这些列没有提供有效数据,我们将将其删除:
const_cols = [c for c in df.columns if df[c].nunique(dropna=False)==1 ]Const_cols>>> ['socialEngagementType', 'totals.visits']df = df.drop(const_cols, axis=1) -
现在,我们将通过删除一些不使用的列来简化数据:
drop_cols = ['sessionId','visitId','visitStartTime','geoNetwork.continent','geoNetwork.subContinent','geoNetwork.region','geoNetwork.metro','geoNetwork.city','geoNetwork.networkDomain']df = df.drop(drop_cols, axis=1)df.columns
以下截图展示了我们当前拥有的列:

图 10.11:最终列集合
-
我们的列现在已经精简为我们实际需要的列。接下来,让我们探索
campaign列,看看哪一项活动更成功:df['trafficSource.campaign'].value_counts().plot.bar(figsize=(10,6),rot=30)

图 10.12:活动数据
从活动数据中我们可以看出,大部分流量并非来自活动,而且一些活动表现不佳。这些信息可以帮助营销团队优化这些活动并节省费用。
计算 CLV
客户生命周期价值(CLV)是一项指标,用于描述公司在客户保持关系期间,可以从一个典型客户身上获得的总收入。CLV 是公司从典型客户身上赚取的总金额,它在营销中被用来预测客户未来整个关系过程中将产生的净利润。
了解客户的 CLV 至关重要,因为它能帮助我们决定花多少钱来吸引新客户以及保持现有客户。计算 CLV 最简单的方法是将每次购买的平均值乘以客户每年购买的次数,再乘以客户关系的平均持续时间(按年或月计算)。
通过计算不同客户的 CLV(客户生命周期价值),可以获得许多好处,其中最关键的是商业决策。了解你的 CLV 能帮助你发现如下内容:
-
你可以花费多少资金,依然能与类似的客户保持盈利的关系
-
高 CLV 客户所青睐的产品种类
-
哪些产品的盈利性最高
-
你的最盈利的客户类型是谁
将资源花费在现有客户群体的销售上是关键,因为现有客户的购买概率为 60%到 70%,而新客户的购买概率只有 5%到 20%。有几种策略能增加消费者再次购买的可能性,其中一些方法如下:
-
让客户更容易退还他们购买的商品,因为如果让顾客退货变得困难或昂贵,将大幅降低他们再次购买的可能性。
-
设定交货日期的预期目标,并通过建立安全边际超越这些预期。例如,承诺在 5 月 20 日交货,然后在 5 月 1 日提前交货,而不是反过来。
-
创建一个有实际可得且有吸引力的激励措施的计划,鼓励用户进行重复购买。
-
提供激励措施以鼓励客户继续选择你的品牌。
-
与长期客户保持联系,确保他们知道你依然在关心他们,同时提供简单的方式让他们联系到你。
-
专注于获取并保持回头客,这些顾客将推动你的品牌发展,以及长期客户。
更具体地说,计算 CLV 的步骤如下:
-
将数据分割为每三个月一组。
-
汇总每位客户在过去 3 个月的收入。
-
生成类似“自上次购买以来的天数”、“购买间隔的平均天数”等列。
我们将通过以下步骤来进行:
-
为了应用这一方法,我们将定义一些辅助函数,并与
pandas的聚合方法一起使用,以确定我们用户的 CLV:def groupby_mean(x):return x.mean()def groupby_count(x):return x.count()def purchase_duration(x):return (x.max() - x.min()).daysdef avg_frequency(x):return (x.max() - x.min()).days/x.count() -
我们希望将分析的时间范围设定为 3 个月,因此我们将创建一个变量来确定这一点:
clv_freq = '3M' -
需要注意的是,我们将使用
__name__属性来确定 Python 中函数的名称,并保持列名整洁。要访问__name__属性,只需在函数名后不加括号并使用__name__属性即可。它将返回函数名的字符串:groupby_mean.__name__ = 'avg'groupby_count.__name__ = 'count'purchase_duration.__name__ = 'purchase_duration'avg_frequency.__name__ = 'purchase_frequency' -
现在,我们可以通过应用
groupby方法并使用之前定义的函数来聚合值,从而创建我们的汇总数据框:summary_df = df.reset_index().groupby('fullVisitorId').agg({'totals.transactionRevenue': [min, max, sum, groupby_mean, groupby_count],'date': [min, max, purchase_duration, avg_frequency]}) -
最后,我们将对列名进行一些调整,以提高可读性:
summary_df.columns = ['_'.join(col).lower() for col in summary_df.columns]
我们可以检查数据框的最终大小:
summary_df.shape
>>> (93492, 9)
我们还可以通过describe方法检查值的分布:
summary_df.describe()
在这里,我们调用了pandas的统计摘要:

图 10.13:计算的用户 CLV
-
现在,让我们通过查看购买日期来筛选出那些实际购买了商品的用户:
summary_df = summary_df.loc[summary_df['date_purchase_duration'] > 0]
之后,我们大大减少了数据集中的用户数量:
summary_df.shape
>>> (66168, 9)
-
我们可以通过绘制分组后的结果,使用交易计数来可视化这些结果:
import matplotlib.pyplot as pltax = summary_df.groupby('totals.transactionrevenue_count').count()['totals.transactionrevenue_avg'][:20].plot(kind='bar',color='blue',figsize=(12,7),grid=True)ax.set_ylabel('count')plt.show()

图 10.14:交易收入计数
- 现在,最常见的交易次数是2,呈抛物线方式减少。这为我们提供了足够的信息,以便在客户第二次交易后向他们提供激励措施,鼓励他们继续购买。
现在,让我们来看看交易之间的天数:
ax = summary_df['date_purchase_frequency'].hist(
bins=20,
color='blue',
rwidth=0.7,
figsize=(12,7)
)
ax.set_xlabel('avg. number of days between purchases')
ax.set_ylabel('count')
plt.show()

图 10.15:购买之间的时间
这些信息告诉我们,客户在 25 天后再次购买的情况很少,因此我们可以利用这些信息在交易间隔超过某个阈值时保持用户活跃。这帮助我们减少客户流失并提高忠诚度。
现在,我们已经确定了如何计算 CLV,这将帮助我们制定更好的营销策略,精确了解我们可以为获取每个客户花费多少。
预测客户收入
通过利用我们公司历史上的交易数据,我们试图预测在特定时间点从客户那里获得的未来收入。当你能够准确预测收入时,规划如何实现收入目标会更简单,在许多情况下,营销团队会被分配收入目标,特别是在初创行业的融资轮之后。
B2B 营销聚焦于目标,这时历史预测(通过历史数据预测我们的收入)一直非常成功。因为精准的历史收入和管道数据为你以前的收入创造提供了宝贵的洞察。你可以利用这些洞察预测为实现收入目标所需的条件。为营销团队提供更好的信息的因素可以总结为四个指标,在你开始计算预期收入之前:
-
你需要多长时间才能产生收入
-
每个管道阶段的交易平均停留时间
-
以往交易的数量
-
在这个时间段内产生的收入
这些指标通常是你预测收入的基础,并且能够帮助创建更加明确的营销计划。
像“用户需要多长时间才能开始赚钱”这样的问题要求你收集这些指标。了解用户的收入时间(从创建账户到用户完成购买所需的时间)是第一步。这是因为收入时间决定了你通常需要多长时间才能创造收入,并收回为获得这个客户所做的投资。如果忽略了这些指标,你的收入周期和估算将失去同步,没有这个参数,可能会导致你错失目标并错误分配资金。事实上,你必须了解你的收入时间。
同样,唯一准确衡量这一点的方法是从一个匿名用户首次与你互动的那一刻开始,直到该账户转化为客户。如果没有识别首次接触,你的测量将不准确,并且再次低估了你实际上需要多长时间才能获得收入:
-
我们将通过导入将要使用的包开始分析,包括
LightGBM分类。需要注意的是,我们会将交易中的 NaN 值替换为零:from sklearn import model_selection, preprocessing, metricsimport lightgbm as lgb# Impute 0 for missing target valuesdf["totals.transactionRevenue"].fillna(0, inplace=True)y = df["totals.transactionRevenue"].valuesid = df["fullVisitorId"].values
为了使分类数据能供各种模型使用,必须通过编码过程将分类数据转换为整数表示。在数据科学领域,数据准备是建模之前的必要步骤——那么,如何在数据科学中处理分类数据呢?常用的方法如下:
-
使用 Python 的
category_encoding库进行独热编码 -
Scikit-learn 预处理
-
get_dummies在 pandas 中的使用 -
二进制编码
-
频率编码
-
标签编码
-
序数编码
当数据无法以其现有格式跨系统或应用程序传输时,这种方法通常会被采用,以确保数据的完整性和可用性。数据保护和安全性并不使用编码,因为解码很简单。
将类别特征的水平转换为数值的一个非常有效的方法是使用标签,这些标签的值介于 0 和 n 类 - 1 之间,其中 n 是不同标签的数量。在这里,我们使用LabelEncoder对变量进行编码。重复的标签会分配与第一次相同的值。
-
我们将列出需要编码的类别列。这里,列表是硬编码的,但我们本可以使用
pandas的数据类型来确定对象列:cat_cols = ['channelGrouping','device.browser','device.deviceCategory','device.operatingSystem','geoNetwork.country','trafficSource.campaign','trafficSource.keyword','trafficSource.medium','trafficSource.referralPath','trafficSource.source','trafficSource.isTrueDirect'] -
现在,我们将遍历它们并使用
LabelEncoder进行编码:for col in cat_cols:print(col)lbl = preprocessing.LabelEncoder()lbl.fit(list(df[col].values.astype('str')))df[col] = lbl.transform(list(df[col].values.astype('str'))) -
现在,类别列已经转换完成,我们将继续将数值列转换为浮动类型,以满足 LightGBM 的要求。
接下来是我们将要处理的列:
num_cols = ['totals.hits',
'totals.pageviews',
'visitNumber',
'totals.bounces',
'totals.newVisits']
-
下一步,我们使用
astypepandas 方法将这些数据类型转换为浮动类型:for col in num_cols:print(col)df[col] = df[col].astype(float) -
现在,我们可以根据时间将训练数据集划分为开发集(
dev)和验证集(val):import datetimedev_df = df[df['date']<='2017-05-31']val_df = df[df['date']>'2017-05-31'] -
对收入变量应用对数变换:
dev_y = np.log1p(dev_df["totals.transactionRevenue"].values)val_y = np.log1p(val_df["totals.transactionRevenue"].values) -
接下来,我们将类别列和数值列进行连接:
dev_X = dev_df[cat_cols + num_cols]val_X = val_df[cat_cols + num_cols]
开发数据框的最终形状如下所示:
dev_df.shape
>>> (237158, 20)
验证数据框的最终形状如下所示:
val_df.shape
>>> (45820, 20)
为了预测每个用户的 CLV,我们将使用之前指定的 LightGBM 回归模型。该算法是表现最好的算法之一,且它是一种决策树算法。
决策树是一种监督学习工具,可以根据过去对查询的回答来分类或预测数据。监督学习的一个例子是决策树模型,它在具有所需类别的数据集上进行训练和测试。决策树用于分类和回归应用,是一种非参数方法。它的结构是层次化的,包含根节点、分支、内部节点和叶节点。
LightGBM 是一种基于决策树的梯度提升算法,它在提高模型性能的同时,使用更少的内存。LightGBM是一个 Python 中的开源梯度提升实现,旨在与现有的实现一样有效,甚至更高效。这个软件库、机器学习方法和开源项目统称为 LightGBM。
LightGBM 的优点包括更快的训练速度和更高的效率:LightGBM 采用基于直方图的方法,通过将连续特征值分到离散的区间中,从而加速训练过程。这种技术还将连续值转化为离散区间,减少了内存的使用。
-
为了简化训练流程,我们将实现一个自定义函数来运行 LightGBM 模型。此函数具有预定义的参数,我们可以根据获得的性能进行调整。这些参数作为字典传递,文档中可以进一步介绍它们:
def run_lgb(train_X, train_y, val_X, val_y):params = {"objective" : "regression","metric" : "rmse","num_leaves" : 50,"learning_rate" : 0.1,"bagging_fraction" : 0.7,"feature_fraction" : 0.5,"bagging_frequency" : 5,"verbosity" : -1}lg_train = lgb.Dataset(train_X, label=train_y)lg_val = lgb.Dataset(val_X, label=val_y)model = lgb.train(params, lg_train , 1000,valid_sets=[lg_val ], early_stopping_rounds=100,verbose_eval=100)pred_val_y = model.predict(val_X,num_iteration=model.best_iteration)return model, pred_val_y
在这里,函数使用Dataset方法加载训练和开发数据集,并在 1,000 步中使用指定的参数训练模型。开发数据集用于验证,因为它能提供有关模型整体表现的信息。
-
现在,我们可以开始训练模型:
model, pred_val = run_lgb(dev_X, dev_y, val_X, val_y)

图 10.16:每用户收入的 RMSE 值
结果显示,模型的表现可以得到提升,这需要我们微调参数,直到达到一个在我们置信区间内的表现水平。
总结
网络分析使我们能够优化在线销售的产品和服务的表现。通过深入了解客户及其消费模式,我们能够改进与客户的沟通方式。 在本章中,我们深入了解了这些数据,并探讨了如何使用这些数据来确定客户的客户生命周期价值(CLV)、了解其特征以及识别关键指标,从而制定成功的数字营销计划。
下一章将探讨几位行业专家在如何将数据、机器学习和商业智能(BI)应用于现实商业场景以改善运营方面的考虑。
第十一章:在商业中创建数据驱动文化
将组织转型为数据驱动型不仅仅需要可以实施的解决方案来解决特定问题。从那些已经走过这条路径、并通过使用数据使组织更高效、更成功的人身上,我们可以学到很多。在本章中,我们将深入了解数据领导者的经验,并学习以下内容:
-
他们是如何开始自己的数据职业生涯的
-
成为成功的数据领导者所需掌握的技能
-
技术和数据的意义以及它们在商业环境中的应用
-
如何在组织中实施数据驱动文化
希望他们的经验能够指导你成为数据领导者的转型,同时推动组织转型,成为数据驱动型企业。
将分享他们观点的受邀领导者包括:
- 帕特里克·克林克:ONBRDNG 的总经理和创始人,这是一家帮助公司进行数字化转型的荷兰公司,他是一位经验丰富的数据领导者和先驱。帕特里克的经验使他成为荷兰市场和国际市场上最受欢迎的数字转型和增长专家之一,因为他帮助公司从传统企业转型为全面数字化公司:

在 ONBRDNG 之前,帕特里克曾是 RTL 的产品和技术总监,并且是广播、内容和数字领域的国际领导者,涉及 60 个电视频道。他还曾为多个公司提供支持,担任产品/数据/技术主管或 CTO。帕特里克还担任多个规模化企业的监事会成员,并为这些公司提供风险投资。
- 迈克尔·库里:他是一位总经理、首席产品官、战略领导者以及品牌和产品讲述者。凭借 30 年的企业间商业软件各个方面的经验,他是技术产品专家,拥有产品管理、市场营销、开发、实施和销售方面的技能和经验。此外,他还是金融服务和医疗保健行业的专家,并且是一位成功的演讲者,曾在哈佛大学和宾夕法尼亚州立大学担任客座讲师:

迈克尔在 IBM 工作了 17 年,曾担任 IBM Watson Health 的副总裁。他领导了 Watson Health 业务向 Francisco Partners 的出售,帮助将 30 多个产品简化为六个主要产品领域,并在单一开发领导者下重组开发,减少了开发运行成本和 SaaS 运营成本,幅度达到两位数。他还领导了向公共云的转型,将 EBITDA 提高了三位数,同时改善了 NP,并领导了 Watson Health 业务的出售。
- 弗洛里安·普雷姆:拥有超过 20 年的数据和分析经验,弗洛里安是瑞士苏黎世德勤技术的首席数据官(CDO):

Florian 是德勤技术领导团队的一员,担任首任 CDO。德勤技术是德勤咨询的部门,是一个全球一体化的技术组织,覆盖 85 个以上的国家,拥有超过 10,000 名专业人员。
在成为全球 CDO 之前,Florian 曾担任德勤瑞士首席数据官(CDO),负责数据战略、治理、风险与安全、数据管理、数据分析与 AI、自动化、协作以及文档和记录管理平台。
- Micaela Kulesz:一位行为数据科学家,拥有 AI、实验经济学和消费者行为的经验。她是数据科学的负责人,是数据与创新领域的领导者,也是机器学习工程师(在 AI 团队中)。目前,她是数据与创新领域的首席数据科学家,专注于零售技术,为零售行业开发解决方案:

她拥有布宜诺斯艾利斯大学的经济学背景,随后在不来梅大学获得了实验经济学、行为经济学、应用博弈论和实验计量学的博士学位。此后,她曾在瑞典农业科学大学担任研究员,之后转型为数据科学家。
她选择与诚实、直接和善良的人合作,并且将数据科学视为一种伪艺术,涉及与同事以及所有人进行创意对话。
- Jack Godau:数据领袖,目前是德国柏林 Doctorly 公司的首席技术官(CTO)。他喜欢谈论创业公司、医疗保健、招聘、未来的工作以及数字化转型:

Jack 是一位富有同理心、合作精神和热情的领导者,他在作为技术专家的基础上,具备强大的销售技能,建立了坚实的职业生涯。他与各种类型和规模的组织合作,通过提供技术和战略领导、指导和培训,帮助他们管理数字化转型。Jack 在团队建设方面非常熟练,致力于培养包容性文化,帮助本地和远程团队取得成功,且他在国际层面成功建立并发展了战略合作伙伴关系。
- Julio Rodriguez Martino:一位充满活力的领导者,拥有超过 15 年的经验,致力于建立、发展和领导高绩效的全球多元化团队。Julio 是一名分析师/数据科学家/机器学习/人工智能专家,具备团队管理技能和经验,同时也是一位作家和导师:

Julio 凭借扎实的物理学教育背景解决了过于复杂的问题,从而开发出创新解决方案。他在各种功能领域,包括分析、人工智能、机器学习、数据科学和工程方面都是团队领导者。Julio 是一个引人入胜的沟通者,精通与关键利益相关者互动,将业务需求转化为技术需求,并作为技术专家为高级管理层提供建议。他具备推动创新和跨功能内部部门知识共享的强大能力,Julio 是一个终身学习者,对研究和开发充满激情,并且始终保持对人工智能和机器学习领域最新发展的关注。
- Agustina Garcia Hernandez:安海斯-布希英博集团的数据与战略总监。她是一位经济学家和分析先锋,具备出色的战略思维模式,将商业和技术数据紧密结合。她专注于赋权来自不同文化和背景的团队,以实现共同富裕的影响结果。她在跨国公司中开发了深厚的管理和指导专业知识,领导本地及国际项目,形成了扎实的项目管理专长,为她的管理职业生涯增添了不少价值。

Agustina 利用她将商业知识和数据结合的关键优势;她在带领团队、领导项目和获得综合结果方面有丰富经验。她专注于在不失大局的情况下创造机会,富有同理心,理解需求并将其转化为解决方案。她通过富有吸引力的口头和图形展示桥接团队和利益相关者,激发行动和结果。她的流程将直觉性的商业知识与基于数据的洞察力对齐,以改进决策过程,同时连接不同领域专家的各个方面,将复杂性转化为简单且可执行的答案。Agustina 利用不同的方法论,如焦点小组、民意调查、分割和机器学习/人工智能,提出优化使用多种学科的业务解决方案。
- Bob Wuisman:拥有人力资源和商业背景的 Bob,利用他在业务流程方面的经验,成为了荷兰乌得勒支 Ebiquity 数字创新中心的数据运营总监。

建立在他作为业务流程顾问多年经验的基础上,Bob 成功在不同企业建立了商业智能环境。凭借全局视野和流程驱动的思维模式,Bob 擅长团队建设和可持续发展的数据驱动运营,具备专业的人力资源、流程和项目管理技能和经验。
- 温·范·德·梅尔:温专注于可持续发展和水资源管理,他是荷兰阿珀尔多恩瓦特谢普·法莱与费卢韦的首席数据官(CDO):

温曾是瓦特谢普·法莱与费卢韦的数字化转型项目经理,负责开发数据驱动的网络组织。作为项目领导者,温监督了《环境法》许可的实施、监督和执行工作,并曾担任水务检查员。他的背景涵盖水资源管理、法律和敏捷组织。
让我们从这些领袖们的路径开始,看看他们是如何开始从事数据工作的。
开始与数据打交道
开始成为数据领袖的旅程需要提升你的技能,涵盖一些能力,这些能力不仅让你理解描述性可视化、基础统计概念,以及技术和数据概念,还能培养出领导团队和理解业务需求的能力。
本节内容是我们询问数据领袖们关于成为成功的数据领袖需要哪些技能和能力时得到的答案。
胡里奥·罗德里格斯·马蒂诺
- 你是如何进入数据科学和工程领域的?
我拥有科学学位,并将我的科研生涯专注于实验物理学。拥有数据分析、统计学和问题解决经验,使得在转向行业时,数据科学成为了一个自然的选择。
- 为了达到这个目标,你在哪些领域需要付出最多的努力?
机器学习和 Python。我在这两者上的经验都很少。
- 如果你重新开始成为数据领袖的旅程,你会如何应对?
我不会改变任何一件事。
迈克尔·库里
- 你能告诉我们为什么你对数据工作产生了兴趣吗?
在我职业生涯初期,我在一家数据集成公司担任产品角色。在这个角色中,我亲眼见证了数据战略对企业的巨大影响。那些在数据集成、整理和分析方面表现卓越的公司,往往是市场表现最好的公司。它们在服务客户、销售、产品开发和投资管理方面都表现得更好。从这段经历中,我意识到数据将成为我职业生涯中每一步的关键工具。
- 你为了成为数据领袖,必须发展哪些技能?
数据目录整理和策划、数据分析、数据可视化和数据治理。
- 商业决策者和分析师如何为使用数据科学和分析做好准备?
企业决策者需要定义那些能带来最大数据回报的问题领域。哪些特定的见解能最大程度地影响业务?是帮助你的销售团队更好地定位新客户,还是优化产品开发流程?有哪些数据盲点可以消除,从而帮助改善决策和运营?这些都是企业决策者需要回答的问题,只有这样,他们才能定义他们希望达成的成果。
米凯拉·库莱兹
- 你是如何进入数据科学和工程领域的?
我的背景是应用定量方法,拥有经济学博士学位。进入数据科学领域,再到数据工程领域,推动创新是一个自然的步骤。
- 能告诉我们为什么你对从事数据工作产生兴趣吗?
数据是事实。松散的解释是可行的,但不可扩展的。“事实是什么?”是我们首先要问自己的问题。只有在此之后,才是我们从事实中读取到的信息。这是推动繁荣社会的普遍态度。
数据中藏有文字,你可以挖掘它们,只要时间允许。数据就是魔法。
- 成为数据领导者,你需要发展哪些技能?
投入和自律是成为专家的关键。然而,要成为一名领导者,我还必须发展耐心和专注力。数据团队需要目标,就像任何团队一样。然而,随着数据量的增加,很容易迷失其中:领导者需要确保在每个时刻都保持专注。
- 你在哪些领域最需要努力,才能到达今天的位置?
我会说我的“多用途”编程技能。我能很好地使用 R、STATA 和 bash(sh,zsh)编程,但为了在数据科学领域成长,多用途语言有着巨大的帮助。它有助于与其他团队和背景的人沟通,但最重要的是,它让我们的想法随着这个领域一起成长。
- 企业决策者和分析师如何准备好使用数据科学和分析?
一旦讨论开始接触数据——即事实——创新的想法便会随之而来,变化也将一点一点地开始。这是最好的准备:讨论和倾听。
- 如果你重新开始你的数据领导者之旅,你会如何应对?
我会更多地参与聚会和黑客马拉松。
鲍勃·维斯曼
- 你是如何进入数据科学和工程领域的?
我擅长 Excel,作为一名业务分析师,我参与了一个新的 CRM 系统(Microsoft Dynamics AX)实施项目。没有人想到要构建跟踪操作的报告。我举起了手,指出了这个程序中的缺失部分,并接手了这个项目。一个月后,我意识到我不知道自己报名参与的是什么,但我喜欢它,并开始在 Google 上查找 SQL 服务器是什么。
- 你在哪些领域最需要努力,才能到达今天的位置?
学习 SQL Server、QlikView、数据仓库概念、ETL 以及数据的一般知识。一切与技术和数据相关的内容。
- 如果你重新开始成为数据领袖的旅程,你会如何应对?
从领域的角度来看——如果你的角色是从数据中提取业务价值。
- 能告诉我们为什么你开始对数据工作产生兴趣吗?
从数据中获得的洞察让我能够清晰地了解那些过于复杂的课题,例如从第一杯咖啡到发票的端到端供应链。
- 成为数据领袖,你需要培养哪些技能?
主要是技术技能和概念,因为我已经在业务流程、文化、架构等方面有了强大的组织背景。我学习了 SQL 来提取数据、转化数据,并将其加载到数据库中,还能进行查询和数据合并。数据建模帮助我学习了如何将数据进行关联,并为我提供了数据可视化工具,如 QlikView 和 PowerBI。Python 的基础知识,现如今主要使用的语言,以及理解每个决策和代码对下游步骤的全面影响也有所帮助。
- 企业决策者和分析师如何为使用数据科学和分析做好准备?
它本身不是一个目标。首先要弄清楚你为什么要使用数据科学和分析,以及它将为你带来什么。拥有数据科学并不是成功的捷径。
Wim Van Der
- 能告诉我们为什么你开始对数据工作产生兴趣吗?
作为一个在水域领域的政府组织,我们在气候变化和水质问题上面临巨大挑战,特别是在荷兰这样的国家,我们生活在海平面以下三分之一的地方。我们需要数据和技术来帮助我们理解这些复杂性,并为未来的解决方案建模。
- 成为数据领袖,你需要培养哪些技能?
基本的技术知识。变更管理。伦理学。该做和不该做的事。
- 企业决策者和分析师如何为使用数据科学和分析做好准备?
开始培训那些了解你业务的人,并让他们意识到数据和技术的可能性。从那里开始扩展,例如,建立 DevOps 团队。
Florian Prem
- 能告诉我们为什么你开始对数据工作产生兴趣吗?
这是解决业务问题、获得洞察和找到根本原因的最佳方法,在我的职业工作中尤其如此。
- 你是如何进入数据科学和工程领域的?
通过应用跨领域的实践知识(例如,在金融领域 20 多年前就开始做分析),结合法学、信息技术和管理的合规/治理背景,以及完成一系列数字化转型、人工智能/机器学习、数据管理和 IT 现代化项目。
- 为了达到这个目标,你在哪些领域需要投入最多精力?
我必须不断努力,保持技术和法规方面的最新知识。
- 成为数据领袖,你需要培养哪些技能?
数据治理、合规性、法规知识;数据管理和分析/人工智能与机器学习/技术技能;项目、产品和变革管理技能,以及软技能。
- 企业决策者和分析师如何为使用数据科学和分析做好准备?
变得更加精通技术,了解数据/在运营、决策、产品和流程中使用数据的重要性。
今天的数据领导者大多数都是从不同的职位转型而来,这些职位不仅要求他们创建收集和展示数据的系统,还需要管理高度专业化的数据团队。从他们的回答中,我们可以看到每位领导者在成为领导者的过程中,如何重塑自己并学会所需的技能,以在这些领导职位上蓬勃发展。
在接下来的部分,我们将听取他们对当前组织中如何利用数据以及他们关注的重点的看法。
在组织中使用数据
每位受访的领导者目前都担任着需要将其组织转型为数据驱动型公司或提升现有数据价值提取方式的职位。
让我们来看看他们对公司和组织中当前数据状态的看法。
弗洛里安·普雷姆
- 公司如何利用数据来改善运营?
在他们的业务和运营模式中,围绕客户、运营和员工的端到端关注,但大多数仍然只是孤立的。
- 对公司而言,成为数据驱动型公司有多重要?
今天很重要;明天,它将成为生死存亡的话题。
- 企业通过存储大量数据能获得什么好处?
存储大量数据本身没有直接的好处。价值只来自于能够转化为资产的洞察和背景。
- 根据你的经验,企业是否意识到并愿意探索数据的新用途?
有些已经开始了,大多数则刚刚起步——这些领导者已将其商业和运营模式围绕数据构建。
- 公司需要什么基础设施?
除了这一个答案:不要从基础设施/技术入手——技术是由商业需求来定义的,别无他法。
- 哪些业务领域更需要处理大量数据?
这取决于你的行业——运营、财务以及客户/产品。
- 这个数据需要多频繁更新?
这取决于数据和你想要实现的洞察/使用场景——从实时到每天/每周/每月/每年,或者按具体案例(例如地址变更)。
米凯拉·库雷兹
- 企业通过存储大量数据能获得什么好处?
到目前为止,这仅仅是一个当前的成本和潜在的收益。不幸的是,数据科学资源非常稀缺,而且该领域的进展主要由非结构化数据驱动,这反过来又吸引了那里仅有的少数资源。
- 根据你的经验,企业是否意识到并愿意探索数据的新用途?
还不足以推动持续的变革。
- 企业如何应对建立数据团队和维护复杂基础设施的挑战?
这取决于公司的规模和关注点。
- 企业需要什么基础设施?
这取决于规模和行业。
- 哪些业务领域更需要处理大量数据?
他们都需要这样做;始终如此。
- 这些数据需要更新的频率是怎样的?
这取决于他们如何使用它。
- 这些公司中的哪些领域可以从使用数据科学和工程中受益最多?
创新和发现。这个领域可以与公司的其他部分并行运作。一个蓝绿开发,专注于提升数据驱动的文化。
- 在经济收缩的背景下,如何利用数据推动收入增长?
提高盈利能力的利润率。同时,不需要太多努力来维持甚至增加利润。在危机时期,我们需要在限制范围内工作。在这里,数据是关键。
- 在供应链成本上升的背景下,如何利用数据来降低运营成本?
我希望提高当前模型的性能。但要构建新的模型,我认为数据量并不是必须的。
- 企业如何利用数据来改善运营?
他们开始尝试一些小型产品,以改善用户体验。
- 对企业来说,成为数据驱动型企业有多重要?
关键。我认为这将决定他们在未来 5 年中的市场地位。
- 企业如何利用数据来了解客户的行为模式?
A/B 测试非常普及,是接近行为的主要工具。然而,正如我之前提到的,结构化数据仍有很大的改进空间。
Wim Van Der
- 企业如何利用数据来改善运营?
在这个阶段,主要是提高流程效率、减少能源使用、获取更好的输出、开发新软件并创建气候情景模型。
- 对企业来说,成为数据驱动型企业有多重要?
不是。保持以人为本很重要,但人类需要学习如何利用数据来帮助自己。
Julio Rodriguez Martino
- 企业存储大量数据能带来什么好处?
质量比数量更重要。大量无用的数据不会帮助解决任何问题。
- 根据你的经验,企业是否意识到并愿意探索数据的新用途?
他们是有意识的并且愿意尝试,但并非总能做到。企业通常需要帮助才能开始。
- 企业如何应对建立数据团队和维护复杂基础设施的挑战?
我见过各种各样处理数据团队的方法。许多年前,企业雇佣数据科学家时并不知道他们的价值。后来,他们意识到雇佣第三方团队做具体项目更容易。当明确知道创建的知识没有被保留在公司时,转向了混合模式:公司内部的小团队得到外部专业人士的帮助。
- 公司需要什么样的基础设施?
拥有云计算的访问权限,我认为在大多数情况下不需要特定的基础设施。
- 哪些业务领域通常需要处理大量数据?
任何处理事务性数据的业务单元。其他领域通常管理较少的数据。
- 这些数据需要多频繁地更新?
这取决于分析的类型。在某些情况下,更新频率会很低;在其他情况下,可能需要几乎实时的更新频率。
Michael Curry
- 公司如何利用数据改善运营?
我见过数据在各个方面的运营管理中得到了有效应用。在我的职业生涯中,我通过数据推动了转型,找出了最需要改进的领域。我还通过数据发现了新市场或现有客户群体中的新机会。我还使用数据预测了哪些产品在未来可能会看到需求的增长。
- 公司变得数据驱动有多重要?
一个组织越是数据驱动,它通常就越成功。这是经过一项又一项研究验证的直线关系。与其依赖假设和直觉,不如依赖数据,因为这样通常会导致时间和金钱的浪费。今天,凭借机器学习的强大能力,我们能够比以往更好地利用数据。那些始终走在投资前沿的公司,将超越其同行。
- 在商业中创建数据驱动文化的主要挑战是什么?
数据的获取具有挑战性,且通常存在质量或时效性问题。此外,人们常常难以理解他们在数据中看到的内容。因此,基于假设和直觉做决策往往更加容易。例如,与客户的最后一次对话,往往会过度影响许多决策。创建一种文化,使得决策时默认依赖于深入分析数据,需要着重确保必要的数据可用、易于理解且值得信赖。这些都需要巨大的投资,而许多公司并不愿意做出这样的投资。
- 数字化公司从一开始就将数据分析和机器学习嵌入到其业务核心中。更传统的公司在这些技术在日常运营中的应用上是否滞后?
当然,有些公司是这样的。然而,即便是传统公司也在数据、分析和机器学习方面进行了大量投资。纯数字化公司在这一点上有优势,因为它们的整个供应链通常都是数字化的,这使得获取决策所需的数据变得更容易,但传统公司通常拥有更多直接相关的数据、更大的现有客户基础和更多的历史数据,因此,在数据分析和人工智能方面的有效投资有时能带来更好的结果。问题在于,传统公司是否能够克服文化惯性,转型为数据驱动型公司。
- 这些公司在哪些领域可以最大程度地受益于数据科学和工程的应用?
没有任何领域是无法从数据科学和工程中受益的。然而,最显著的进展发生在企业的市场营销和销售方面。利用分析理解客户行为并更好地为客户提供精准的优惠已经变得非常普遍。近期的许多数据科学投资更多集中在企业的生产领域,这可能带来更大的回报——帮助制药公司加速药物发现,帮助农业公司提高作物产量,帮助化工公司更快速地生产新型化合物。利用数据减少长时间、昂贵的投资并提高产量,具有显著提升各行各业生产力的潜力。
Bob Wuisman
- 企业通过存储大量数据能获得什么好处?
没有。它只会花费资金,关键在于公司的质量和价值。
- 根据你的经验,企业是否意识到并愿意探索数据的新用途?
是的,但当你通过数据分析将实际结果透明化,并指出某人过度美化了结果时,往往会出现政治上的复杂问题。这种情况无论内部还是外部都会发生。
- 公司需要什么样的基础设施?
精英和高效团队都使用云端或多个云平台;可以查看谷歌进行的 Dora 研究。
- 哪些业务领域通常需要处理大量数据?
所有具有数据流组件的行业。
- 这个数据需要多频繁更新一次?
这一点没有普遍规则可适用。有时是每纳秒更新一次;有时是一年更新一次。
Jack Godau
- 企业通过存储大量数据能获得什么好处?
我的思路与你稍微不同。它始于愿景、组织目标以及在这些方面的强大共识。只有当这些存在时,才应该开始努力确定做出决策所需的信息,之后再去发现数据源并提取所需的信息。
我会用采矿来做类比(不是数据,而是挖掘地下的东西)。我们并不是简单地砍伐森林和挖掘整个山脉去“看看能找到什么”。我们会专门提取最低成本和最少努力下需要的资源——这是“数据团队”经常忽视的,所以就会投入大量精力去“让我们保留一切”,“让我们做一些分析,看看它告诉我们什么。”从商业角度看,这样是没有用的,也不可行。
只有有目的地使用数据,才能获得益处。囤积数据——特别是那些没有价值的数据——可能是一个代价高昂的错误。
数据不应与信息混淆。
你需要真正清楚你在组织中追求的目标,然后确定你需要的信息;接着,你可以确定这些信息的数据来源。当然,这比“聘请一个数据团队让他们自己解决”要难得多。
现在我们已经了解了团队在公司内实施成功的数据驱动策略的工作方式,在接下来的部分,我们将重点讨论为什么这些公司认为实施成功的数据策略具有重要意义。
数据驱动的好处
将组织转型为数据驱动型组织并非易事。这需要明确我们想要实现的目标,因为这种转型需要大量资源,并且在改变现有流程以利用数据做出明智决策时,会带来很多不便。
Wim Van Der
- 这些公司中,哪些领域最能从数据科学和工程的应用中受益?
这取决于公司及其身份;我认为没有一种适用于所有公司的解决方案。
- 数据如何在经济收缩的背景下推动收入增长?
经济收入不再应该是公司发展的核心驱动力。更广泛的价值才是。所以,不要只盯着巨额的经济收入;也要看一下生态收入。
- 数据如何在供应链成本上升的背景下帮助减少运营成本?
从一个 DevOps 团队开始,分析你的业务流程,并给他们一个开放的任务,提升效率。
- 公司如何利用数据来理解客户的行为模式?
流程挖掘仍然是成功制定客户旅程的有效工具。
迈克尔·库里
- 数据如何在经济收缩的背景下推动收入增长?
即便在市场放缓的情况下,企业、政府和个人依然在花费资金。数据是揭示这些资金流向和原因的秘密,这样企业可以更有效地争取这些资金。例如,了解在经济形势变化时,优先级如何变化,是数据可以揭示的内容。数据还可以用来帮助调整定价和包装,以优化买方需求的变化。
- 数据如何在供应链成本上升的背景下帮助减少运营成本?
优化供应链一直是数据分析的重点。更好的需求预测和更合理的资源分配可以在不牺牲收入的情况下帮助最小化成本。
- 企业如何利用数据来理解客户的行为模式?
客户行为分析是过去十年中公司进行的最大投资之一。这一进程因人们在数字环境中留下的自然数据足迹而加速。这些数据足迹能比纯粹的模拟环境中获得的洞察力更深刻地理解个体的行为和动机。因此,数字追踪变得无处不在(同时也带来了反对声)。例如,家庭保险公司希望知道人们何时可能在新的邮政编码区寻找房屋,以便将相关优惠推送给他们。
- 更传统的公司习惯将市场研究外包给咨询公司。社交媒体、网站分析和搜索引擎数据的使用是否正在取代这些方法?
在很大程度上,这正变得越来越真实。曾经只有少数非常熟练的数据分析师才能使用的工具(因此集中在咨询公司手中)现在已经更加普及,更多的人能够使用。此外,数据分析已成为一种更为普遍的技能,越来越多的商业人士正在学习,而用于丰富内部数据的外部数据如今对于甚至是普通的商业分析师也变得更加可访问。因此,对昂贵外部顾问的依赖已被减少。
杰克·戈道
- 根据您的经验,企业是否意识到并愿意探索数据的新用途?
不。
- 企业成为数据驱动型公司有多重要?
零百分比——我们需要数据来做决策,但企业必须主导愿景和战略。单纯依靠数据并“自下而上”来做决策并不可行。的确,亚马逊利用其数据帮助销售更多产品——但它的愿景是成为最好的在线商店。数据使这一目标成为可能,但单靠数据并不足以实现这一目标。战略和愿景必须先行,并且必须得到清晰的阐述。
鲍勃·维斯曼
- 企业如何应对创建数据团队和维护复杂基础设施的复杂性?
这在很大程度上取决于 C 级领导的数字化素养和公司文化。企业向数据驱动型组织转型所做的尝试次数也非常重要。在第一次尝试时,企业拥有比后续尝试更多的自由度和责任委托。
弗洛里安·普雷姆
- 这些公司在哪些领域最能从数据科学和工程的应用中受益?
客户、员工和产品——每个公司都有不同的痛点和需求。
- 在经济收缩的背景下,数据如何推动收入增长?
超越竞争对手,了解客户,实施成本节约。
- 在供应链成本上升的背景下,如何利用数据减少运营成本?
这取决于你的行业和供应链——没有单一的答案。
- 公司如何利用数据来了解客户的行为模式?
我认为最好的方式是数据驱动的端到端客户旅程。
现在我们有了一个视角,了解这些领导者认为组织如何通过实施数据驱动的策略受益。在接下来的部分,我们将深入探讨这些转型所面临的挑战。
数据驱动战略的挑战
我们没有现成的策略可以实施来推动公司进行数据驱动的转型,因此这些策略需要根据组织的需求量身定制。
我们询问了数据领导者,在将这些组织转型为快速发展的数据驱动公司时,他们通常面临哪些挑战。
Bob Wuisman
- 创建数据驱动文化在商业中面临的主要挑战是什么?
70%的时间,这样的尝试都会失败。文化和政治,部门之间的隔阂,不愿意合作的部门文化,权力导向的文化,技术的不足,数据质量得不到保障,技术过于集中而非组织绩效导向。
项目由 CIO、CTO 或 CFO 领导。CDO 或类似职位应该领导并向 CEO 或 COO 汇报。
- 创建数据驱动文化在商业中面临的主要技术挑战是什么?
成为稳定、可靠和值得信赖的。兑现你的承诺,并指出必要投资的优势。
Florian Prem
- 公司如何应对创建数据团队和维护复杂基础设施的复杂性?
做得不好——他们最好雇佣并授权一位懂技术和商业的 CDO,并在高层支持下启动数字化转型项目。
商业决策者如何与高度专业化的数据团队合作,以满足公司的需求?
将数据团队整合成 CDO/公司数据办公室,让 CDO 及其团队领导处理——这需要比数据团队通常所需的不同技能。
- 创建数据驱动文化在商业中面临的主要挑战是什么?
政治、人员、流程和解决方案,以及缺乏高层支持/推动。
- 创建数据驱动文化在商业中面临的主要技术挑战是什么?
人员,但也包括现有的解决方案/技术负债/孤岛。
- 数字化公司从一开始就将数据分析和机器学习作为其业务的核心。传统公司在日常运营中采纳这些技术是否滞后?
是的,数据必须融入其运营和商业模式中。
- 传统公司习惯于将市场调研外包给咨询公司。社交媒体、网页分析和搜索引擎数据是否正在取代这些方法?
不过,端到端的分析平台可能很快会使这一过程变得更容易——但你仍然需要具备理解数据和市场洞察等方面的能力与工具。
温·范·德尔
- 在企业中建立数据驱动文化的主要挑战是什么?
从底层开始,创造小的成功。让人们讲述他们的成功故事。不要从最新的技术开始;人们首先需要学习并调整心态。
- 数字化公司从一开始就将数据分析和机器学习嵌入其业务核心。更传统的公司在日常运营中是否落后于这些技术的应用?
问题依然存在:你的企业价值观是什么,你所追求的身份是什么?然后,从技术入手。那些现在看似领先的公司,可能很快就会落后——不是在技术层面,而是在伦理层面。
杰克·戈道
- 在企业中建立数据驱动文化的主要技术挑战是什么?
这个问题是错的——现实世界中没有人真正想要一个数据驱动的文化。这里正是数据与商业之间存在巨大不对称的地方。以医疗保健为例——数据挖掘正确的信息可以为病人的治疗带来巨大的好处。但在现实世界中,每个人都明白,组织的焦点应该是治疗病人,而不是拥有数据。使数据中的信息更容易获取和呈现以便于治疗是件好事,但这并不是数据驱动的文化。
米凯拉·库莱斯
- 在企业中建立数据驱动文化的主要技术挑战是什么?
大型公司需要流程,而流程需要时间来在规模上落地。小型公司更灵活,但也缺乏预算来投资创新技术。确实是个挑战!但总的来说,答案是“人”。
- 在企业中建立数据驱动文化的主要挑战是什么?
员工害怕失去工作,自动化威胁着人权,算法公平性挑战了人类权益,公司则担心失去对数据使用的控制权。恐惧无处不在。让公司更积极地采取数据驱动行动将有助于克服这种恐惧。
- 数字化公司从一开始就将数据分析和机器学习嵌入其业务核心。更传统的公司在日常运营中是否落后于这些技术的应用?
较大的非数字化公司是最滞后的。 一方面,它们的规模使得变革本身就复杂。另一方面,直到现在,数据有着特定的用途和目标,这些目标和意义正开始受到挑战。
- 更传统的公司习惯将市场研究外包给咨询公司。社交媒体、网页分析和搜索引擎数据是否正在取代这些方法?
我不这么认为:它们增加了竞争格局,迫使公司进行创新。
胡里奥·罗德里格斯·马蒂诺
- 企业需要什么样的基础设施?
获取云计算资源。大多数情况下,我认为没有必要为此提供特定的基础设施。
- 创建以数据为驱动的商业文化的主要技术挑战是什么?
我认为挑战不在于技术。最重要的挑战是改变整个组织对数据的看法。极其重要的是向每个人展示数据的价值,让他们明白生成高质量数据并确保其安全是多么重要。
对我们在推动数据倡议时可能面临的挑战有一个清晰的愿景,帮助我们制定能够促进数据战略采纳和持续执行的策略。
在接下来的部分,我们的问题将集中在如何创建能够支持组织转型的数据团队。
创建高效的数据团队
数据倡议需要实施具备专业技能的数据团队,并且需要采取跨学科的方法,因为支持数据战略的技术位于商业战略之上。
我们向数据领导者询问了他们对数据团队的看法;以下是他们的回答。
Florian Prem
- 一个优秀的数据团队应该是什么样的?
这取决于首席数据官(CDO)的职责范围。我通常会组织为治理、数据分析和数据价值创造/产品三个部分。然后,我会为敏捷产品交付组建跨领域的团队(IT、业务和数据)。
- 领导数据团队时需要考虑哪些最重要的议题?
了解你的团队,了解你的业务,了解你的技术。
- 商业决策者与高度专业化的数据团队合作的最佳实践是什么?
请参阅我之前所说的,雇佣一位既懂技术又懂业务的 CDO,并建立跨企业的/多个相互关联的数据办公室。
Michael Curry
- 商业决策者与高度专业化的数据团队合作的最佳实践是什么?
最重要的最佳实践是建立一个数据组织。这个组织的目标应该是定义并执行整个业务的数据整理流程,将数据与业务需求对齐,并管理数据的使用方式。
Micaela Kulesz
- 一个优秀的数据团队应该是什么样的?
三到四名成员:一位数据科学家负责人、一位初级/中级数据科学家、一位来自 IT 背景的数据工程师,以及如果可能的话,加入一位产品经理。这样的团队组成非常有活力,能够解决许多——如果不是大多数——业务问题。我认为需要强调的是,至少有一位成员必须来自 IT 背景,另一个来自业务背景。
- 领导数据团队时需要考虑哪些最重要的议题?
-
确保团队内部的活力。
-
理解是一种特权。团队大约 15%的时间用于研究最新的发展,且大多数由初级/中级人员执行。
-
高级人员负责为项目提供明确的背景,他们必须具备编码能力。
-
花时间讨论并明确待解决的问题,不要偏离它。
- 商业决策者与高度专业化的数据团队合作,服务公司需求的最佳方式是什么?
商业决策者有一个明显明确的关注点和优化功能。在这里,数据团队可以为业务提供数据服务,帮助他们增加关注点,或者没有。如果有,他们就会成为公司的一项操作性服务,失去独立性。然而,由于数据团队还比较新,因此也在公司中寻找自身的定位,把自己定位为“数据操作员”或“数据提供者”并不是他们所期望的。如果他们接受自己是“数据服务员”,我认为最理想的方式是,商业方花时间将问题和痛点讲清楚,直到彻底明白,而数据团队则必须为这些痛点带来具体的解决方案。既不多也不少。
Bob Wuisman
- 一个好的数据团队是什么样子的?
一个好的数据团队是一个对公司战略有清晰视野,并知道如何快速做出贡献的团队。他们所做的每一件事都有明确的商业目标,即便是在基础设施层面。采用 DevOps 方法论是关键。
- 领导数据团队时最需要考虑的最重要的议题是什么?
拥有一个透明且有组织的待办事项清单,得到高级经理和高层执行人员的支持、认可和重视。确保工程师在工作中没有任何约束。理想情况下,他们应该能够在没有外部支持的情况下独立工作。需要庆祝成果,并将“错误”视为学习的机会。
- 商业决策者与高度专业化的数据团队合作的最佳实践是什么?
采用 DevOps 方法,并拥有清晰的战略、目标、关键绩效指标(KPI)、流程和 IT 系统,以捕捉正确的数据。熟悉数据概念,并深入了解商业管理。不要过度推销或提出过高要求。
Jack Godau
- 商业决策者与高度专业化的数据团队合作,服务公司需求的最佳方式是什么?
拥有清晰的愿景和需求,并将其传达给团队,确保他们理解组织目标。定期反馈,和团队一起了解他们的成果,并将这些成果与实际案例进行验证。之后,执行所有标准工作——经典的团队赋能、避免微观管理、提供良好的工具等。
Julio Rodriguez Martino
- 一个好的数据团队是什么样子的?
一个好的团队由来自不同背景的多种角色组成。团队成员应该渴望学习,并通过互相帮助来教学。
- 领导数据团队时最需要考虑的最重要的议题是什么?
以身作则。领导者必须亲自了解每个成员面临的挑战:学习新主题并与具有不同背景和经验的同事合作。领导者还应了解每个项目的细节,至少要掌握该主题的基本知识。
- 商业决策者与高度专业化的数据团队合作以满足公司需求的最佳方式是什么?
帮助识别可以通过数据解决的业务问题,并且能带来价值。此外,要准备好与数据团队合作,理解并解释分析结果。
温·范·德
- 商业决策者与高度专业化的数据团队合作的最佳实践是什么?
扩展现实、区块链和数字双胞胎,但这取决于具体的业务。然而,DevOps 团队在恰当的位置上是已知会取得成功的。
创建和支持高效的数据团队,以制定成功的数据战略,对组织至关重要,并且需要的不仅仅是硬技能。
在下一部分,我们将请数据领域的领导者分享他们对这些组织未来数据发展方向的看法。
未来数据的可视化
数据已经成为那些希望发掘业务价值潜力的公司的核心支柱,这也是现代技术将塑造组织未来的原因。
我们询问了数据领域的领导者他们对未来技术的看法。
迈克尔·库里
- 你认为哪些未来的技术将塑造在商业中使用数据的未来?
数据集成、数据质量和数据治理依然昂贵且过于依赖手工操作,然而它们仍然是公司无法充分利用数据的最大障碍。我预计在这些领域会有显著的进展,利用新的基于人工智能的方法来减少人工工作量。
弗洛里安·普雷姆
- 你认为哪些未来的技术将塑造在商业中使用数据的未来?
可解释的人工智能、设备对设备通信/物联网 -> 预测分析、自动化、端到端分析平台,以及新的自然语言处理和图像/视频处理模型——一旦这些技术作为产品和平台问世,它们将被更广泛地使用。
鲍勃·维斯曼
- 你认为哪些技术将塑造数据科学和工程的未来?
下一步将是量子计算。在那之前,已有更多、更快速的云平台出现,并提供更为细分的服务。
米凯拉·库勒兹
- 你认为哪些技术将塑造数据科学和工程的未来?
在数据科学中,这些技术与增加对大规模结构化数据的理解和建模密切相关。
在工程领域,这涉及到改进算法;我们已经知道如何执行快速而简单的任务。
胡里奥·罗德里格斯·马尔蒂诺
- 你认为哪些技术将塑造数据科学和工程的未来?
我猜无代码和 AutoML 技术将变得越来越流行。未来只有复杂的分析才需要编程。
塑造组织中数据的未来需要灵活应对新挑战和新技术的变化。希望我们数据领导者的视角能为你提供一些关于哪些技术将塑造数据未来的思路。
下一节将专门探讨那些在多个组织中致力于推动数字化和数据转型的领导者的经验。
实施数据驱动文化
数据已经在公司领导和经理的讨论桌上占据了核心地位。通过实施数据战略,提升运营绩效、以优化的方式与客户互动并寻找新的收入来源,这些都是值得追求的目标,但这些转型并非没有挑战。
在本节中,我们采访了数据领域的领导者 Patrick Flink,他的公司专注于将组织转型为数字化组织,以及 Agustina Hernandez,她曾为世界上一些最大型的公司从零开始建立全球数据团队。
Agustina Hernandez
Agustina 有计量经济学背景,曾担任计量经济模型师,并决定在职业生涯初期追随她的热情,即使这意味着薪水减少。她的其他兴趣是辅导和摄影。她曾参与多个项目,为顶级快速消费品公司创造洞察,并领导团队在品牌价值、媒体投资回报、产品组合等主题上创造洞察。她的主要关注点之一是深入理解客户需求,以实现业务收入和 EBITA 的目标。
在为多个客户账户工作后,她加入了一家顶级化妆品公司,担任全球数据分析负责人,并负责为该公司建立数据团队,提供数据驱动的解决方案,并在组织中创造一种更加专注于数据的文化变革。
她的领导策略可以看作是将所有不同领域和背景的数据团队利益相关者纳入讨论桌中。她促进团队内部的沟通,而不是强加某种特定的观点,更多的是通过了解客户需求来集中精力完成任务,确保团队拥有继续前进所需的所有资源,并专注于改善沟通。
她对数据团队的愿景是,它是一个负责改变现实的团队,企业可以通过理解创建数据驱动文化的必要性来提升运营,从而提高技能,并采取自上而下的方法。大多数情况下,这需要一个能够融入当前组织并通过大量工作推动这些变化的团队,重点是改变行为。对阿古斯蒂娜而言,数据在企业中的爆发可以看作是一项推动任何变革的技术,神奇地解决和改善所有指标。数据战略建立在现有战略之上,以便能够扩大我们可以用来做出更好决策的知识范围。
- 在组织中有效实施数据驱动转型的方式是什么?
实现有效转型有多种方式。创建灯塔小组,这些是有抱负的团队,代表他们希望实现的转型示范,来引导转型是不够的,因为之后,这些团队会遇到与他们工作条件不同的现实。这是因为这些团队拥有比其他团队更多的资源,而这与公司其他部分的实际情况有所不同。
灯塔模式与并行组织模式相对立,后者负责转型公司的流程。通过设立转型目标和明确的激励措施(如中期的可变奖金)来强力实施。这会产生内部冲突,但会持续在新文化的生命期内。这也意味着需要资源,并且这项转型就像是组织的青春期一样,会经历阵痛。
一些组织拥有较大的利润空间,可以通过为每个部门创建转型团队来重复配置资源,这些团队随后加入那些寻求改进的团队和部门。这个问题在于,虽然资源得以重复,但这些资源中的所有团队并没有能力推动这项转型。
- 管理层对这些转型的看法是什么?
中层管理者最难说服,因为采用新战略意味着他们需要同时做两件事。虽然一些中层管理者对变革感到兴奋,但一切都是依赖于双手和资源来定义的,执行转型需要转移变革流程并从运营中转移资源,这会影响这些中层管理者被评判的绩效指标。当那些应该使用数据的人没有改变他们的流程,以将这些指标或洞察力融入到他们的决策和日常运营中时,实施有效的数据战略就会与现实发生冲突。这些人通常是接近运营的中层管理者,他们没有时间或技能来领导转型或推动数据的使用。
- 这些组织从数据驱动转型中可以期待什么样的好处?
如果流程没有改变,数据就毫无意义;如果不考虑设备和流程,收集到的数据将没有用途或影响。数据驱动并不是意味着减少资源。这是来自 IT 领域的一种误解,IT 领域总是寻求降低运营成本。数据驱动战略应关注公司中创造收入的部分。依赖于 IT 或运营的首席数据官(CDO)旨在节省成本,但这行不通。数据既不代表较少的成本,也不意味着较少的时间——它涉及发现价值机会。举个例子,促销或降价,如果没有足够的客户和产品数据,往往会给那些已经准备购买的消费者提供补贴。如果我们利用数据,我们可以计算促销弹性,并根据每个买家群体的需求进行定制,促使他们购买更多,但不会产生贸易支出的节省。随着组织的成熟和对数据战略真正角色的理解,这一数据战略将成为销售部门、战略部门或总经理的责任。或者,您需要做到 C 级管理。
- 数据故事讲述如何融入数据驱动的转型中?
这项数据战略必须具备支持战略的故事性,并清晰传达组织中每个参与者的角色。这一战略必须从上至下实施,因为如果恰恰相反,战略从每个团队或部门开始,便会产生碎片化,因此需要进行有效的对齐,逐步展示并传达获得的好处。
- 成功的数据驱动战略的要素有哪些?
数据战略的要素包括明确的目标——即切实可行的目标、一个将每个人聚集在这些目标背后的文化,以及执行这种转型所需的资源和毅力。
- 人们如何融入数据驱动的战略中?
在人员方面,重要的是考虑每个人的技能和能力。有些人擅长分析,有些人擅长将战略付诸实践,还有一些人两者都做得很好。通常,数据团队具备战略开发能力和领域知识来执行数据战略。这是因为单纯的硬性知识是不够的,数据是补充那些需要了解的现实的,因此必须具备领域知识,以补充数据的使用,从而做出有效决策。这些领域知识存在于这些靠近操作的中层,他们必须向数据科学家学习,数据科学家是负责团队中与首席数据官(CDO)协调并推进这些开发的人员。如果仅有一个集中化的部门为整个组织提供数据,这种方式是行不通的。
- 在这一转型过程中,技术的角色是什么?
在技术方面,技术是一个使战略得以执行的推动力,但这种技术是建立在数据战略之上的,而数据战略又是建立在商业战略之上的,绝不是反过来。解决数据问题以解决业务问题时,应始终寻求最低复杂度的解决方案。这是通过数据工程师、MLOps、DataOps 等团队来实现的,始终控制开发质量,确保信息的可靠性以及系统的规则和能力。
Patrick Flink
Patrick 是变革性战略实施的专家。他高度关注从数据中提取可操作的价值,并通过最小化的方法迅速实现价值。他的公司 ONBRDNG 是一家荷兰公司,专注于数字化转型,不仅强调数据驱动的战略,还注重执行力。
拥有商业和管理背景,他采用多学科的方式来学习必要的工具和技能,从数据中提取价值,并优先考虑可操作的成果。他曾担任首席技术官(CTO)和首席数据官(CDO),并与荷兰的媒体公司合作开发创新的产品和解决方案。
Patrick 既务实又富有创造力。他是一个结果导向的战略家,喜欢与组织各个层级进行沟通,并将公司从传统企业重建为全面数字化的公司。
- 你是如何准备自己具备成为数据领导者的技能的?
是的,我是一个终身学习者。当然,我自己做了一些事情,也花了很多时间和我们的开发人员及分析师一起工作。我花了很多时间就坐在他们旁边。因此,成为跨学科的人总是很好的;这样,你可以看到与来自不同领域的专业人士一起工作所带来的价值。这通常包括问很多问题,从不同的角度看问题等等。所以,是的,这就是我开始的地方。我们还做了很多关于媒体的工作,这个领域的好处是,媒体公司总是关注如何不断增加客户和消费者的参与度。最新的功能、特性和产品也会在这里展示,因为每个人都知道他们对新事物极其开放。他们会生成大量关于行为的数据,比如他们喜欢什么、不喜欢什么,等等。你可以利用这些信息来制定出一个合适的策略。
- 你们使用什么样的方法论来发掘数据中的价值?
我们有一种方法,正在朝着方法论的方向发展。但根据我的经验,大多数时候,每个人都使用相同的示例。而且他们没有考虑到在该领域成功所需的所有重要因素,常常忽视了要深入了解整个故事。因此,这不仅仅是收集各种数据然后通过它变得更好,而是要看到并理解他们如何做 —— 比什么做更重要,因为你知道那可能会有所不同。我们喜欢我们的方法,是因为我们把它做得小而简洁。我们利用数据来告诉我们发生了什么,然后我们看看能做些什么,大家都说数据驱动或数据决策之类的,但更多的是在人性和创造力方面。如果你看看那些最具数据驱动的公司,当然他们使用数据,但同样还是依赖创造力、直觉和赌注,同时保持非常诚实。我见过太多公司招聘大量的数据工程师、数据科学家等,并花费大量资金在基础设施上。我们利用数据来看我们的赌注是否正确,以及我们是否在推动改变。我们使用大量数据,且在没有客户的情况下,我们也会进行大量数据冲刺,以保持专注。
我们跟踪客户的整个旅程,从头到尾。所有这些都是可能的,但每个人都忙于许多事情,所以我们尽量简化。因此,我们在数据冲刺中做了很多工作,通常持续几周 —— 没有人告诉你,很多时候,为了利用数据并从中获得价值,你不需要太多人。
- 在与公司中的数据团队合作时,你遇到过哪些挑战?
其中一个挑战是公司雇佣了太多员工。我自己经历过这种情况;我问一个负责客户洞察的数据经理,我们需要一个客户流失预测模型,然后几周或几个月后,我们问进展如何。他们说,商业需求不明确,但我们可以做面部识别。然而,这不是我们要求的。我们可以构建推荐引擎,但我们已经有了推荐系统,所以现在不需要这个。所以,更多员工并不是我们最大的问题。有许多公司有太多人在一个话题上工作,但这并没有带来好处或可行的成果,而且我们也没有从中获得真正的价值。
- 你是如何进行成功数据项目的构建的?
我们所做的是尽量将它做得极其小。所以,从一个数字开始。假设,我们要做数据驱动的工作。好的,答案是什么?我们的第一个假设是什么?让我们看看我们在做什么,正在发现什么,正在经历什么——这 90%的工作是数据清洗。我们在整理数据,获取正确的标签、正确的数据和正确的定义。但没有人谈论这些。所以,我们可能会尝试将它简化,然后真正面对现实。这种数据冲刺的概念是我们工作领域的一种视角。有时它是非常碎片化的;例如,它是一个单体系统还是一个大范围的工作场景?然后,我们问,商业是什么?我们对客户承诺什么?我们是如何赚钱的?接下来,我们尝试了解客户的端到端旅程。从这里开始。我们的主要挑战是什么?关键问题是什么?是增长吗?是客户流失吗?是流程自动化吗?这也与决策相关。我们是否想要自动化各种事务?那么,我们必须从假设开始,并从那里出发。我们必须始终在 6 周内——仅仅 6 周——尽力从中获取一些价值。我们评估基础设施和商业模式,并观察客户旅程中的各个渠道。然后,我们必须根据这 6 周设定目标,组成一个小团队。这个团队应该是跨学科的,并且是公司的一部分。那是你的团队。当然,我们为客户做这些工作。当然,这总是一个混合型的跨学科团队。
- 你如何设定优先级,并与公司中的数据团队合作?
团队的重点当然取决于高层管理层。如果它关注的是数字化流程自动化,目标将会是某种类型。如果它是一个工厂,例如,或一个电子商务公司,它将专注于转化率。当然,当涉及到体验或者关注点时,这个问题会有所不同,这取决于公司。所以,有时候这会有所变化,但我们始终协助并与客户合作。我们使用主平台,同时也拥有我们的数据平台,因此我们的客户在五个遗留系统中遇到困难。然后,我们有我们的数据平台,在这里我们对某些系统中的数据进行景观分析。然后,我们可以说,“把这个系统中的元素提供给我们,这样我们就可以创建某种初步视图。”我们还使用全球技术栈和一些工具,如脚本。平台上也有专家。不过,我们的数据平台的使用确保了我们能够加速。
- 数据驱动转型的目标应该是什么?
所以,平均而言,我认为如果你谈论数据驱动,你应该更多地与客户交流。接着,你应该谈论你在数据中看到的内容,以及其他人看到的内容。然后,你应该谈论你的收入、成本和你希望获得的结果。最终,很多公司只是在谈论这些组成部分。如果你是数据驱动的,那么你可以预测你的结果,因为你已经在客户行为中看到了它。你会看到结果在未来的某个时刻,而不仅仅是下一个季度。我认为,最终你总会承诺实现你的承诺,而这是对客户非常重要的。
将公司转变为数据驱动型组织需要深刻的文化变革,这一变革需要通过自上而下的方法来实施,由能够创造叙事来支持数据战略并适应组织需求和目标的领导者来主导。这一转型不仅仅关乎技术,更关乎人和流程。我感激 Agustina 和 Patrick 的观点,并希望他们的经验能够帮助你们创造有效的文化转型。
总结
在这一章中,我们探讨了构成数据转型的不同部分,这些部分帮助希望通过利用具有广泛知识和经验的数据领导者的观点和经验,成为数据驱动的公司。
这些方法不仅使公司能够转型,还使领导者和经理能够适应实现成功数据战略所需的新技能和新流程。通过这样做,他们可以明确了解公司将如何从这些努力中受益,如何创建和领导数据团队,以及如何考虑赋能数字化和数据转型的影响。
总体而言,本书的目标是帮助商业领袖、经理和爱好者们实施现成的解决方案,探索关于消费者、他们的行为模式、他们如何看待价格、如何有效地提出推荐以及如何将管理经济学应用于 Python 来补充商业战略等方面的见解。
我借鉴了程序员、经理和专家们的知识,正是他们给了我灵感,让我写下了这本书。书中所有被修改和使用的代码与数据均属于各自的所有者和生产者,我对此表示感谢。最重要的是,我要感谢那些数据领域的领导者,尽管他们的日常工作异常繁忙,仍抽出时间回答了本章提出的问题。
我感谢你花时间阅读这本书,希望它能够激发你利用数据达成目标,同时在享受过程的同时也能收获乐趣。


:价格弹性
:需求商品数量的变化
:需求商品价格的变化
浙公网安备 33010602011771号