Django MTV 架构的工作结构
MTV 架构组件(模型、模板和视图)
这是 MVC 模式的一个变种,正如你在首字母缩略词中看到的,Template 关键字代替了 Controller。但是,模板并不完全起控制器的作用,并且具有一些与控制器不同的属性。
模型的定义仍然保持不变,即模型包含项目的逻辑文件结构,是数据库和视图之间的中间件&数据处理器。该模型定义了来自视图的数据格式如何存储在数据库中,反之亦然,即从数据库中检索的信息以可显示的格式传输到视图。
MTV 架构中的视图可能看起来像控制器,但它不是。这个 MTV 架构中的视图通过模型格式化数据。反过来,它与数据库通信,并将数据传输到模板进行查看。
如果 everything View 首先实现的是什么模板?
嗯,模板让前端开发人员的生活变得简单,这是肯定的。它还提供了比传统 MVC 架构更快的开发速度。
那么,它是怎么做到的呢?
模板的主要目标是保存浏览器呈现的所有内容。模型的数据来自服务器的不同部分,当用户与网站交互时,这些数据被集成在一起。这里,Django 中的模板层更类似于 MVC 模式中的视图层。这一层更加集中,有了 Django 框架,它为前端开发者提供了比 MVC 架构更多的可扩展性。
MTV 架构的运作
现在为了更好地理解 Django 是如何实现这个过程的,以及在这个 MTV 架构中真正重要的是什么。我们可以检查下面的 Django 架构图来理解它。
如上图所示,我们有一些组件和两个区域,即服务器端和客户端。这里您会注意到视图在服务器端,而模板在客户端。
现在,当我们请求网站时,我们用来通过浏览器进行请求的界面就是模板。然后,请求者将视图文件传输到管理服务器。
Django 实际上是请求和响应之间的游戏。因此,每当我们的模板更新时,它是我们从这里发送的输入(请求),它在服务器上被视图看到。然后,它传输到正确的 URL。这是 Django MTV 架构的重要组成部分之一。在那里,Django 中的 URL 映射实际上是在正则表达式中完成的。这些表达方式比 IP 地址更容易理解。这也有助于我们在 Django 特性教程中讨论的 SEO 任务。
现在,在向正确的 URL 发送请求后,应用程序逻辑,模型开始对给定的请求做出正确的响应。然后,特定的响应被发送回视图,视图再次检查响应,并将其作为 HTTP 响应或所需的用户格式进行传输。然后,它再次通过模板由浏览器呈现。
上述功能的一个更简单的实际工作是——
当你登录一个网站(基于 Django ,你打开登录页面。它再次发生而不需要模型。这是因为视图将处理请求并将其发送到登录页面的 URL。然后,它将是服务器的响应,从那里到浏览器。
之后,在给定的模板 HTML 表单中输入您的凭证。从那里,数据再次被发送到视图,这一次这个请求被纠正,模型被给予数据。然后,模型读取并验证用户在连接的数据库中提供的数据。
如果用户数据匹配,它将发送相关的用户数据,如个人资料图像,名称和(其他事情取决于网站的类型)的看法。然后,它将在期望的响应中格式化相同的内容,并将相同的内容传输给客户端。
否则,模型将向视图发送一个否定的结果。接下来,它会再次将其路由到登录页面,并显示一条错误消息。
这就是 Django MTV 架构的实际工作方式。
阅读更多关于流行 Django 应用列表
MVC 架构的一些缺点
一.两个组件控制视图
在 MVC 模式中,视图是网页的 UI/显示逻辑。但在这种 MTV 架构中,它由两个组件生成/控制,模型和控制器。这使得 View 的测试过程有点困难。为了充分测试这一点,我们实际上必须首先编写所需的模型和控制器。
二。模型组件负载过大
模型完成所有的工作,无论是从数据库传输数据还是向数据库传输数据,以及对数据库进行操作。还通过应用业务逻辑给出适当的结果。单一组件完成所有这些任务。
三。开发复杂性很高
如果我们在较小的项目上实现 MVC 架构的方法,那么这将是一个漫长的开发过程。我们总是需要理解项目结构,并且必须小心使用的约定。
这对较小的应用程序有相反的影响,因为它们可能不太具有可伸缩性。它可以像一些大学生的 ERP 网站,办公室私人服务器等。这种方法可能会使系统更慢而不是更快。
这些是 MVC 架构的一些主要缺点。尽管 MTV 架构解决了这些缺点中的一些。但是,为了使用这种技术进行开发,您仍然需要熟悉 Django 的项目/文件结构。
摘要
在本教程中,我们了解了 Django MTV 架构的基本组件是什么,这些组件创建了 Django 项目。本教程是 Django 的基础,如果你打算在更小的项目中使用 Django,你必须掌握这些知识。
使用 Flask、Flask-RESTPlus 和 Swagger UI 处理 API
Photo by andrew welch on Unsplash
在从事机器学习项目时,我决定开发完整的应用程序。这将需要开发 API,这样我们就可以发布值并获得预测的响应。这就是 Flask 和 Flask-RESTPlus 的用武之地。
Flask 支持将 Python 函数公开为 API。Flask-RESTPlus 是 Flask 的扩展,改进了它的功能。它不仅允许我们定义 REST APIs,还为所有 API 引入了 Swagger UI。
在本文中,我将解释如何用几个 API 和虚拟数据开发 Flask 应用程序。该项目可以作为一个 GitHub 库使用。
装置
我通过使用pipenv
创建一个虚拟环境开始了这个过程。你可以在我的关于虚拟环境比较的文章中读到更多。我安装了 Flask 和 Flask-RESTPlus。
pipenv install flask
pipenv install flask-restplus
然而,如果您不希望在一个pipenv
环境中工作,您可以简单地使用下面的命令。
pip install flask
pip install flask-restplus
基础
Photo by Aaron Burden on Unsplash
导入
我开始是从flask
进口Flask
。从flask_restplus
中,我导入了Api
来定义 app 和Resource
,后者作为参数被接收到项目中定义的各个类中。
定义应用程序
我使用方法Flask()
将应用程序定义为 flask 应用程序,该方法使用__name__
设置名称。接下来,我将使用Api
来初始化应用程序。
我在这里定义了一个namespace
。概念很简单。每当在给定的名称空间下定义 API 时,它们都会出现在 Swagger 中的给定标题下(我们将在本文后面探讨 Swagger)。在namespace
中,第一个变量定义路径,第二个变量定义该空间的描述。
在上面的例子中,名称空间的 url 是http://127.0.0.1:5000/main
,在 Swagger 中描述为主 API。
定义 API
最后,我定义了端点。由于我们的名称空间是name_space
,我将通过 route 定义 url 为@name_space.route("/")
。接下来,Flask-RESTPlus 要求我们定义一个类中给定路径下的所有端点。方法可以是get()
、put()
等等。
在上面的例子中,可以通过路径http://127.0.0.1:5000/main
访问 API。类名设置为MainClass
,有get()
和post()
两种方法。无论何时,当我进行 GET 调用时,我得到的回复是字段为status
的Got new data
,而对于 POST 调用,我得到的回复是Posted new data
。
运行应用程序
现在,一切都准备好了。文件保存为 basic.py 。我使用pipenv
通过以下命令运行应用程序。
pipenv shell
FLASK_APP=basic.py flask run
使用 pip,您可以使用以下命令。
FLASK_APP=basic.py flask run
Swagger UI
Flask-RESTPlus 最好的部分是它自动记录我们创建的 API,并且它们在 Swagger UI 中是可见的。转到http://127.0.0.1:5000/
可以看到所有的 API。
Swagger UI
这两个 API 都可以在名称空间main
下看到,描述为Main APIs
。我们可以通过点击Try it out
按钮来尝试任一 API 并检查其功能。
尝试 API
我使用 curl 从终端发出 GET 和 POST 请求。
Making API Requests
使用curl
命令时,首先使用单词curl
,然后在字符-X
后使用方法。最后,指定端点。看一下我们的 curl 响应,我们看到我收到了正确的GET
和POST
API 数据。
更上一层楼
Photo by Markus Spiske on Unsplash
Flask 和 Flask REST-Plus 还有更多功能。让我们更深入地探索它们,更好地理解它们。下面的代码在 GitHub 资源库中以[app.py](https://github.com/kb22/Understanding-Flask-and-Flask-RESTPlus/blob/master/app.py)
的名字提供。
我们可以使用 POST 请求来发送数据并保存它。然后,我们可以使用 GET 请求来获取数据。假设我们有一个管理个人姓名并存储它们的项目。我们创建一个 GET 端点来使用id
获取名称,创建一个 POST 端点来根据id
保存名称。
在这里,我已经创建了路径为http://127.0.0.1:5000/names/<int:id>
,在这里我们每次都会经过id
。为了存储名字,我创建了一个对象list_of_names
,用于获取和接收数据。
导入更多库
我们已经导入了 Flask、Api 和资源。我们也从flask
包中导入request
。这有助于我们获得请求对象,然后从中检索信息,比如 JSON 数据。我们还从flask_restplus
包中导入fields
来定义元素的类型,比如字符串。
添加申请信息
我们还可以在 Flask 应用程序中添加额外的信息。这些信息非常有用,显示在 Swagger UI 中。
我们可以定义应用程序的version
、title
和description
。我们只设置了一个名称空间,即names
。Swagger UI 标题现在看起来如下图所示。
Swagger UI Header
定义模型
每当我们想要接收或发送特定格式(JSON)的信息时,我们都要借助model
来完成。我们指定模型的名称。接下来,我们描述它期望的信息和每个期望值的属性。
我们将型号名称定义为Name Model
。它包括一个参数,即name
,为必填字段,定义其描述和帮助文本。将使用这个模型的 API 将期待一个带有关键字name
的 JSON。
为了跟踪所有的名字,我将它们存储在list_of_names
中。
定义 API
让我们将上面的代码片段分解成更小的部分,以便更好地理解它。我们将探索POST
端点。GET
的功能将非常相似。
定义路线和等级
我们使用name_space
命名空间来定义路由,即http://127.0.0.1:5000/main/<int:id>
。它期望 Id 以整数形式发送。out 类的名称是MainClass
,它有一个参数Resource
。
为 API 定义文档
使用doc
,我们可以在 Swagger 中定义 API 的文档。responses
键定义了各种可能的 HTTP 状态代码。对于每个状态代码,我们还定义了向用户描述它的文本。params
键定义预期的参数。API 期望 URL 中有id
,我们为用户指定一个帮助文本。Swagger UI 如下图所示。
POST API (Swagger UI)
参数在顶部定义。所有预期的响应及其描述出现在下半部分。
定义方法
我们现在可以定义我们的方法了。在我们的方法之前,我们添加了行expect(model)
,它定义了 API 期望的model
。我们将代码包装在一个try
块中,并捕捉所有可能发生的错误。request.json['name]
获取我们收到的名称,我们可以保存它,并在响应中将其发送回去。如果name
键丢失,我们得到KeyError
并发送状态代码 500。在其他情况下,我们发送状态代码 400。
尝试应用程序
让我们启动应用程序。
FLASK_APP=app.py flask run
POST
我们解析来自请求的响应,读取name
并将它存储在list_of_names
中的id
中。我们还返回新添加人员的状态和姓名。
POST Request with id = 1 and name = Karan Bhanot
Response of POST Request
发布请求中的错误
比方说,我们忘记了在数据对象中提供name
参数。在这种情况下,我们会得到一个错误。
POST without name key
500 Error
由于没有提供密钥name
,我们收到了状态代码为 500 的错误和消息Mapping key not found.
GET
我们只需传递我们想要获取其姓名的id
,如果可用的话,我们将获取该人的状态和姓名。
GET Request with id = 1
Response of GET Request
获取请求时出错
比方说,我们没有人反对 Id 2。如果我们试图检索这些信息,它会抛出一个错误。
GET to fetch data against Id 2
500 Error
由于没有找到那个特定的 Id,我们得到状态代码 500 和消息Mapping key not found.
结论
在本文中,我们探索了使用 Flask 和 Flask-RESTPlus 创建 API。这两个都是很好的开发库,可以用 Python 编写 API 文档,并使用 Swagger 与 API 进行交互。
请随时分享你的想法和想法。
在 Python 中使用 Google Cloud AutoML
拥有一个 CSV 文件并在各种 ML 模型中实现它是很容易的。但是,困难在于完全使用 Python 来实现 e2e 过程,即获取视频、提取图像、将它们上传到谷歌云存储,然后对它们执行 AutoML。现在,大多数公司都有自己的内置模型;如果没有,那么他们使用谷歌 ML 模型或其他。
我将与你分享这个过程:
- 从对象的视频中提取帧(使用 OpenCV)
- 用正确的文件夹结构将图片上传到谷歌云存储中。
- 准备一个带标签的 CSV 数据集并上传到 Google 云存储。
- 使用上述 CSV 将数据集从 Google 云存储导入 AutoML。
- 在 AutoML 中训练数据并创建所需的分类模型。
- 在模型上测试随机图像。
上述过程的逐步指南
从对象的视频中提取帧(使用 OpenCV) :
我拍了 2 段视频,每段大约 25 秒,用来解决我的问题。在 Google Cloud 中,你需要准备一个桶用于你的项目。但是对于所有的事情,我都是用 Python 来做的,而不是使用控制台。我用 Jupyter 笔记本写的 Python 代码。
在上面的命令中,我们导入所需的库,使用项目路径和项目 Id、存储桶名称以及所需的修改来创建项目。
用正确的文件夹结构将图片上传到 Google 云存储:
以下命令帮助我从没有 ffmpeg 功能的视频中提取图像,并直接上传到 Google Cloud 上。我使用 openCV 库从视频中提取图像。OpenCV-Python 是一个强大的 Python 绑定库,旨在解决计算机中的视觉问题。在提取图像时,我在主任务文件夹下分别创建了两个子文件夹cup
和watch
,以便于对它们进行分类。
准备带标签的 CSV 数据集并上传到 Google 云存储:
最后,这将创建一个带有谷歌存储位置 URIs 和相应标签(cup
和watch
)的demo_data.csv
文件,并将它上传到谷歌云存储。
使用上述 CSV 将数据集从 Google 云存储导入到 AutoML:
在上面提到的代码中,随着数据集的生成,将提供一个dataset_id
,我们将使用它将图像从 CSV 导入到 Google Cloud。不要担心,导入数据需要一些时间。
在代码中,我们可以看到某些信息必须从我们这边提供,比如- compute_region
,默认情况下是用于 AutoML 处理的us-central1
。在这里,我的数据集中有两个标签,因此,当使用上面的 CSV 从 Google 云存储导入数据集到 AutoML 时,我设置了multilabel=True
。
对于异步数据,我们需要提供回调函数,否则可能会抛出错误。在准备导入代码时,我得到了一个代码为 504 的错误,它指出了最后期限异常。如果您遇到这样的错误,您可以专门提到超时,因为默认情况下,任务花费的时间是 60 秒。一旦所有数据都被导入,它将打印以下输出:
在 AutoML 中训练数据并创建所需的分类模型:
在训练模型时,所需的第一个参数是需要由我们定义的数据集,我们将从上一步中获得相同的参数。其次,我们需要提供一个模型名。最后,培训预算。培训预算是用于模型的培训时数。在我的任务中,最多需要 1 小时。
接受训练需要一些时间,同时你可以去喝杯咖啡。一旦你回来,你的模型就可以接受测试了。
在模式下测试随机图像:
现在,在谷歌云的控制台中,你可以去查看数据集的统计数据,在那里你可以评估你的数据。如果你认为你做错了什么,你可以进行必要的修正,并在数据集最终准备好接受测试之前再次重新训练数据集。
在我的数据集中,我拍摄了两个视频——一个杯子和一个手表。此外,在测试原始数据(即这里的图像)时,我可以得到特定的结果。一旦训练完成,我拍了几张图片来测试我的模型,我得到的结果几乎和我的图片一样准确。
结论:
我们看到了如何在不使用 Google Cloud 控制台的情况下,用 Python 将原始视频转换为图像并制作 CSV 数据集,以及导入和训练数据。现在,当从谷歌云中的 CSV 文件导入数据时,可能会出现一些其他问题(除了我提到的问题)。您需要了解错误,并为调试该错误进行研究。我希望这将消除您的一些疑虑,同时让您轻松使用 Google Cloud。
完整的代码可以在我的 github 回购:https://github.com/Priktopic
万事如意。
资源:https://cloud.google.com/vision/automl/docs/
使用 AWS S3 和 Python 处理 Hive
使用外部数据存储维护配置单元模式和使用 Python 执行配置单元查询的初学者指南
Photo by Aleksander Vlad on Unsplash
在本文中,我将分享我维护 Hive 模式的经验。这将对那些愿意涉足大数据技术的大一新生很有用。这将主要描述如何使用 python 连接到 Hive,以及如何使用 AWS S3 作为数据存储。如果你不熟悉 Hive 概念,请浏览维基百科上的文章。
本文的主要目的是提供一个通过 python 连接 Hive 和执行查询的指南。为此,我使用了“*py hive”*库。我将我的连接类创建为“Hive connection”,Hive 查询将被传递到函数中。AWS S3 将用作配置单元表的文件存储。
**import** pandas **as** pd **from** pyhive **import** hive**class** HiveConnection:
@staticmethod
**def** select_query(query_str: str, database:str =HIVE_SCHEMA) -> pd.DataFrame:
*"""
Execute a select query which returns a result set* **:param** *query_str: select query to be executed* **:param** *database: Hive Schema* **:return***:
"""*
conn = hive.Connection(host=HIVE_URL, port=HIVE_PORT, database=database, username=HIVE_USER)
**try**:
result = pd.read_sql(query_str, conn)
**return** result
**finally**:
conn.close()
@staticmethod
**def** execute_query(query_str: str, database: str=HIVE_SCHEMA):
*"""
execute an query which does not return a result set.
ex: INSERT, CREATE, DROP, ALTER TABLE* **:param** *query_str: Hive query to be executed* **:param** *database: Hive Schema* **:return***:
"""*
conn = hive.Connection(host=HIVE_URL, port=HIVE_PORT, database=database, username=HIVE_USER)
cur = conn.cursor()
*# Make sure to set the staging default to HDFS to avoid some potential S3 related errors* cur.execute(**"SET hive.exec.stagingdir=/tmp/hive/"**)
cur.execute(**"SET hive.exec.scratchdir=/tmp/hive/"**)
**try**:
cur.execute(query_str)
**return "SUCCESS"
finally**:
conn.close()
我将查询保存为单独的字符串。这样,您可以在必要时用外部参数格式化查询。配置单元配置(配置单元 URL、配置单元端口、配置单元用户、配置单元模式)为常量。函数" select_query" 将用于检索数据,函数" execute_query 将用于其他查询。
Hive 提供了一个 shell 交互工具来初始化数据库、表和操作表中的数据。我们可以通过输入命令“*Hive”*进入 Hive 命令行。您也可以在 shell 中执行本文中给出的所有查询。
创建新模式
模式是类似于数据库的表的集合。Hive 中允许使用关键字 SCHEMA 和 DATABASE。我们可以选择任何一个。这里我们用模式代替数据库。可以使用“创建模式”来创建模式。要进入模式内部,可以使用关键字“USE”。
**CREATE SCHEMA** userdb;
**USE** userdb;
创建表格
有三种类型的配置单元表。它们是内部的、外部的和暂时的。内部表存储数据库中表的元数据以及表数据。但是外部表将元数据存储在数据库中,而表数据存储在远程位置,如 AWS S3 和 hdfs。删除内部表时,所有表数据都将随元数据一起被删除。删除外部表时,只会删除元数据。而不是表数据。这样,实际数据将得到保护。如果您将新表指向相同的位置,数据将通过新表可见。
Hive 是一个数据仓库,使用 MapReduce 框架。因此,数据检索的速度对于小型查询来说可能不够公平。为了提高性能,可以对配置单元表进行分区。分区技术可以应用于外部表和内部表。像 bucketing 这样的概念也是有的。您可以选择这些技术中的任何一种来提高性能。
将数据从一个地方复制到另一个地方时,临时表非常有用。它充当数据库会话中保存数据的临时位置。会话超时后,所有临时表都将被清除。创建临时表对" Pyhive" 库没有用,因为单个会话中不支持多个查询。即使我们创建了一个表,也不能再使用同一个会话来访问表。但是这在 Hive 命令行中是可能的。您可以创建一个临时表,然后在单个会话中从该表中选择数据。
内部表格
以下查询将创建一个具有远程数据存储 AWS S3 的内部表。文件格式为 CSV,字段以逗号结尾。“ s3_location 指向数据文件所在的 s3 目录。这是用户为查询字符串定义的外部参数。应该在查询格式化的时候传递。
CREATE TABLE `user_info` (
`business_unit` INT,
`employee_id` INT,
`email` VARCHAR(250),
`first_name` VARCHAR(250),
`last_name` VARCHAR(250),
`gender` VARCHAR(250),
`birthday` DATE,
`created_date` DATE,
`updated_date` DATE)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ESCAPED BY '\\'
LOCATION '{s3_location}'
TBLPROPERTIES (
"s3select.format" = "csv",
"skip.header.line.count" = "1"
);
如果数据字符串包含逗号,将会破坏表格结构。所以我定义了一个转义符,在创建表之前,所有不必要的逗号都需要放在这个转义符的前面。
下面是一个示例记录。请注意,电子邮件包含一个逗号。
1,1,安,史密斯@加米尔。com,Ann,Smith,女,’ 1992–07–01 ‘,’ 2019–09–01 ‘,’ 2019–12–31**'**
上面的记录需要这样格式化 : 1,1,ann\,smith@gamil.com,安,史密斯,女,“1992–07–01”,“2019–09–01”,“2019–12–31”
外部表格
这里,我用“*业务单位”*和“*创建日期”*对“*用户信息”*表进行了分区
CREATE EXTERNAL TABLE `user_info` (
`employee_id` INT,
`email` VARCHAR(250),
`first_name` VARCHAR(250),
`last_name` VARCHAR(250),
`gender` VARCHAR(250),
`birthday` DATE,
`updated_date` DATE
) partitioned by(
`business_unit` INT,
`created_date` DATE,
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ESCAPED BY '\\'
STORED AS
INPUTFORMAT
'com.amazonaws.emr.s3select.hive.S3SelectableTextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION '{s3_location}'
TBLPROPERTIES (
"s3select.format" = "csv",
"s3select.headerInfo" = "ignore"
);
工作单元表
创建临时表的查询。
CREATE TEMPORARY TABLE `user_info` (
`business_unit` INT,
`employee_id` VARCHAR(250),
`email` VARCHAR(250),
`first_name` VARCHAR(250),
`last_name` VARCHAR(250),
`gender` VARCHAR(250),
`birthday` DATE,
`created_date` DATE,
`updated_date` DATE
) ;
翻桌
删除表的查询。如果删除外部表,远程文件存储中的数据不会被删除。
DROP TABLE IF EXISTS `user_info`;
插入数据
一旦使用外部文件存储创建了表,远程位置的数据将通过没有分区的表可见。但是当涉及到带有分区的表时,情况就不一样了。这意味着数据不能直接复制到分区表中。我们需要创建一个没有分区的临时表,并通过提供分区值将数据插入到分区表中。以下查询描述了如何向这样的表中插入记录。
INSERT INTO TABLE user_static_info PARTITION (business_unit={business_unit}, `created_date`='{execution_date}')
SELECT
Employee_id,
email,
secondary_email,
first_name,
last_name,
orig_gender,
gender,
signup_channel ,
signup_from_fb ,
birthday,
signup_date,
updated_date,
last_activity_date,
subscription_status
FROM
tmp_user_static_info
WHERE business_id={business_unit}
因为单个会话中的多个查询不支持“py hive”;我必须创建内部表“tmp _ user _ static _ info”,它指向没有分区的 S3 数据目录。然后,在将数据插入外部分区表后,删除了该表。
检索数据
选择查询用于检索配置单元中的数据。这些非常类似于 SQL 选择查询。它具有以下形式。您可以根据自己的需求构建查询。
SELECT [ALL | DISTINCT] select_expr, select_expr, …
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[HAVING having_condition]
[CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list]]
[LIMIT number];
更新和删除数据
配置单元不支持直接更新和删除数据。如果您想更改表格中的任何内容;使用 SELECT 查询将必要的数据复制到新表中。然后,通过删除旧表并重命名新表,可以用新表替换旧表。
更改表格
在 Hive 中可以更改表格。但这需要在不影响现有数据的情况下非常小心地完成。因为我们不能改变数据。例如,在中间添加一个新字段不会移动数据。如果我们添加一个新字段作为第二个字段,属于第三列的数据仍将出现在第二列,第四个字段的数据出现在第三个字段,依此类推。最后一个字段将不包含任何数据。这是因为更新配置单元表数据的限制。如果我们添加一个新字段作为最后一个字段,将会有一个空字段,我们可以将数据插入到该字段中。
ALTER TABLE user_static_info ADD COLUMNS (last_sign_in DATE);
如果我们想删除外部数据,我们可以使用以下步骤。
ALTER TABLE user_static_info SET TBLPROPERTIES('EXTERNAL'='False');
DROP TABLE user_static_info;
例子
最后,以下代码显示了如何使用“ HiveConnection ”类中的“ execute_query ”函数执行查询。
from src.database.hive_con import HiveConnection
create_temp_table_query_str = """CREATE TABLE `user_info` (
`business_unit` INT,
`employee_id` INT,
`email` VARCHAR(250),
`first_name` VARCHAR(250),
`last_name` VARCHAR(250),
`gender` VARCHAR(250),
`birthday` DATE,
`created_date` DATE,
`updated_date` DATE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ESCAPED BY '\\'
LOCATION '{s3_location}'
TBLPROPERTIES (
"s3select.format" = "csv",
"skip.header.line.count" = "1"
);""".format(
s3_location="s3://hive-test/data/user_info/"
)
HiveConnection.execute_query(query_str=create_temp_table_query_str, database=userdb)
使用 Python 回答问题:“我如何在 Excel 中打开这个 JSON 文件?”
在这篇文章中,我描述了在 python 中使用 JSON 文件的一个非常入门/基本的过程:1)加载一个 JSON 文件,2)进行一些数据操作,然后 3)将一些数据保存到一个像 CSV / Excel 这样的二维格式中。
https://www.flaticon.com/authors/smashicons
当我开始接触编程和数据科学时,我害怕不是 csv 或 Excel 表格的文件格式。
如果你递给我一个 JSON 文件,我会跑去躲起来。如果您找到我并让我使用 JSON,我将不得不做一些不可复制的乱七八糟的查找和替换。
在使用 JSON 并学习了更多 python 之后,我可以告诉你,它并没有看起来那么糟糕。请跟我一起踏上旅程,我将介绍一些使用 Python 对 JSON 文件进行数据操作的简单方法,并向您展示如何使用 Excel 打开 JSON。
JSON 是什么
JSON 代表JavaSscriptOobjectNrotation,是用于在网络上存储和交换数据的主要文件格式。
与以二维矩阵显示数据的 CSV 文件不同,您可以将数据存储为复杂的嵌套结构。这些嵌套结构可以处理当今编程中的所有主要数据类型(如字符串、数字、列表、字典)。
如果你曾经打开过一个 JSON 文件,它看起来有点像这样:
如果您检查上面的 JSON,您可以开始推断其结构:
- 包含 6 种颜色列表的“颜色”键
- 列表中的每个词典都是一种具有特定属性的颜色
- 在“代码”属性中,这是指包含 rbga 列表和十六进制表示的字典
我能够推断出上述情况,因为我知道:
- [ ]代表一个列表
- {}表示这是一个字典
- :表示字典的键/值对
一旦你推断出了结构,你就可以开始提取你需要的部分或者做一些数据操作。
用 Python 读取 JSON 文件
对于这个例子,我将使用 Google Colab 笔记本。
Google Colab 笔记本基本上是一个托管在 Google 服务器上的 Jupyter 笔记本。我喜欢这一点,因为你不必担心任何技术问题——你只需去https://colab.research.google.com/就可以发布一款新笔记本
加载新笔记本后,我可以导入三个模块:
import requests
import json
import pandas as pd
- 请求让我从网络上获取数据
- json 提供了所有使用 json 文件的方法
- 熊猫是我最喜欢的数据处理包
JSON 文件可以作为一个单独的文件下载,但是通常 JSON 是作为 API 请求的结果返回的。
我想加载一个我在网上找到的颜色的 json,这样我就可以使用请求模块来获取它:
Data = requests.get('https://gist.githubusercontent.com/cpreid2/86d1fc08fc21094b2af098082cbbb49b/raw/3434521113467ed636b26e0e1d4f8c952b6a8b0a/colors.json')Colors = Data.json()
如果我将 Colors 变量打印到屏幕上,我可以看到 JSON 的结构:
如果您正在加载一个本地 json 文件,您应该这样做:
with open('data.json') as json_file:
data = json.load(json_file)
解析出数据
假设我们只对这个数据集中的原色感兴趣(type = 'primary ')? 我们怎么只选择那个数据 ?
答案是使用 python 中的循环——我们可以遍历整个 JSON,检查 type 属性是否设置为‘primary’。如果是真的,我们可以把这个数据附加到一个新的变量上。
可以使用相应数据类型的 python 选择方法来选择 JSON 的各个部分:
- dictionary**{ }—***dictionary _ name【key】,*其中 key 为字符串
- list**[]—**list _ name【I】,其中 I 是一个数字,代表列表中该项从 0 开始的索引
例如,如果您想获取 JSON 文件中的第一种颜色,您可以编写如下代码:
Colors['colors'][0]
如果你想得到第四种颜色的十六进制值,你应该这样写:
Colors['colors'][3]['code']['hex']
了解了 JSON 中的值是如何被访问的,您可以编写以下代码来只选择原色:
primary_colors = []**for** color in Colors['colors']: **try**: **if** color['type'] == 'primary': primary_colors.append(color) **except** KeyError: **continue**primary_colors
将这个新的 primary_colors 变量打印到屏幕上显示,它只获取了四种原色:
通过在 for 循环或 if/then 语句中添加更多约束,您可以在过滤中获得更高级的效果。
操纵数据
在我加载的 JSON 中,我注意到十六进制字段缺少三个尾随的 0,以使它成为完整的十六进制代码。如何将文本添加到十六进制值中?
您可以使用类似的 for 循环过程来实现。我在列表中循环,并在每个十六进制值后追加“000”。下面的代码实现了这一点:
for color in primary_colors: color['code']['hex'] = color['code']['hex'] + '000'primary_colors
打印此变量表明列表中的“十六进制”字段已更新:
这是一个简单的例子很多 python 字典的数据操作方法都有。
保存回 JSON 格式
要将原色保存回 JSON 文件,可以使用以下代码引用新创建的“primary_colors”变量:
with open('primary_colors.json', 'w') as outfile:
json.dump(primary_colors, outfile)
将 JSON 中的自定义字段保存为 CSV 文件
通常情况下,您需要为从 JSON 文件中获得的一些数据创建一个 CSV 文件。
在本例中,我只想提取 json 中的颜色字段和 rgba 字段中的 r、g、b、a 值,作为 csv 文件中的一个单独的列。我可以通过再次执行另一个 for 循环来获取我感兴趣的每个字段,并将其“展平”为一个可以转换为数据框的简单列表。下面的代码实现了这一点:
primary_color_flattened = []for color in primary_colors: primary_color_flattened.append({ "color" : color['color'], "r" : color['code']['rgba'][0], "g" : color['code']['rgba'][1], "b" : color['code']['rgba'][2], "a" : color['code']['rgba'][3]})
上面我遍历了所有的颜色,并为每种颜色添加了一个新的字典条目到一个名为‘primary _ color _ flattened’的列表中
然后,可以将该变量读入 pandas 数据框,如下所示:
您可以使用 pandas to_csv 方法将此 csv 保存到文件中:
然后,您应该能够在类似 Excel 的东西中打开这个 csv。
这是一个非常肤浅的观点,说明当你转向脚本/编程思维模式时会发生什么。您将更倾向于为数据操作构建一个可再现的管道。
我在这里附上了 Colab 笔记本,因此如果您是 python 编程新手或一般编程笔记本的新手,您可以尝试一下。
JSON 到 CSV 示例—colab.research.google.com](https://colab.research.google.com/drive/1HSB8ZUnaTbfbZbMaxxPNUJBqYuY3d0oX)
在机器学习的单个问题中处理多种类型的数据
在我之前的一篇帖子中,我提出了 3 个在面试中被问到的主观问题。在这篇文章中,我将回答第三个问题。当我有时间的时候,我可能也会写下其他人的答案。到那时,请分析这个有趣的问题和解决方案。
今天,我将分享我第一次参加数据科学面试的经历,那是在一所顶尖的…
towardsdatascience.com](/my-first-data-science-interview-bccc006ba2c8)
问题
你给病人提供病史。您拥有包含数字数据(血压、血糖、心率、血脂等)的所有测试报告,包含图像+文本报告(医生根据图像得出的结果)的放射报告,以及包含有关患者健康的主观和客观信息的护理报告。患者数据以报告的形式出现,即时间序列数据。基于这些报告,你必须计算 3 件事:
a)病人存活的几率有多大,他要在医院呆多少天。你将使用哪种算法,为什么?
b)死亡率是多少,即一个人在接下来的一周内是否会死在 ICU。你会选择哪种算法,为什么?
c)定义一些对你的问题有用的额外特征,这些特征是问题中所没有的。如果这是一个人工智能驱动的医生,它将如何向病人的家人传达当前的状况?
解决办法
在这种类型的问题中,你应该简明扼要地回答对方到底在寻找什么是明智的。此外,图表充分说明了你的理解能力,所以请画一个图表来描述你的解决方案管道。
问题中的数据类型:
- 表格数据:报告包含数字历史数据*(血压、血糖、心率、血脂等)。*
- 图像数据:放射学报告(医学成像数据集)
- 文本数据:护士记录加上来自图像数据的医生发现(放射学元数据)
表格数据
ML 方法:经典机器学习
对于表格数据,我们必须使用经典的机器学习方法。这种方法首先涉及到特征工程,在这种工程中,我们可以组合特征以形成新的特征或者丢弃一些不重要的特征。
Example for 1 Report
考虑到这是数据准备步骤中的时间序列数据,我们需要定义一个窗口大小。窗口大小是您希望将历史中的多少观测值作为要素包括在内。例如:
如果您有最后 5 个报告,并且您的窗口大小为 5,则在最终表中,一个单一特征列可以有 5 个条目,如-
Remember to take previous columns as the features.
如果窗口大小为 2,那么最终的表格将具有如下特征:
If we take window size = 2
图像数据
ML 方法:卷积神经网络
对于图像数据,我们使用卷积神经网络来提取特征或标签。这可以通过两种方式中的任何一种来实现:
- 假设我们有一个标记的医学数据集,它突出显示了肺部受肺炎影响的部分。这个数据集有 2 个标签,如果这个人有肺炎或没有。在这种情况下,我们可以对图像进行二进制分类,并将最终标签作为特征列添加到表格数据中。
Binary Classification on the Image
Classification result added as a feature column with Tabular data
2.如果我们没有一个标签数据集,我们可以通过一个预先训练的 CNN 比如 Densenet-121 来传递图像。这样做是为了从图像中提取特征。请确保不要运行完整的 Densenet-121 模型,而是在完全连接的层中断开它,在该层中,我们有一个 256 矢量的特征。下一步是将这些要素作为列添加到表格数据中。
Features Extracted from DenseNet-121 added as feature columns with tabular data
文本数据
ML 方法:经典的自然语言处理和/或 RNN
对于文本数据,我们使用经典的自然语言处理或 RNN。必须对文本进行解析,以删除被称为标记化的单词。则需要将这些字编码为整数或浮点值,以用作 ML 算法的输入。在这个数据中,我们也有两个选择:
- 使用 tfidf vectorizer:tfidf vectorizer 代表词频—逆向文档。 TF-IDF 是词频分数,试图突出更有趣的词,例如,在文档中频繁出现但不跨文档出现。使用 TF-IDF,我们可以将单词转换为特征,并将它们附加到我们的表格数据中,以便进行最终分类。
The TF-ID vectors appended as feature columns with tabular data
2.如果我们已经标记了报告的情感数据集,那么我们可以使用 LSTM/RNN 来执行分类,并且获得作为表格数据中的特征的最终标签。
Final Pipeline of this problem.
问题 3-a:患者存活的几率有多大,需要住院多少天。你将使用哪种算法,为什么?
回答:为了进行这种回归预测分析,我假设我有一个带标签的数据集。
正如我所解释的,从不同类型的数据中提取特征,我们可以将所有东西组合成表格数据,最后使用经典的机器学习。我将使用的算法是 XGBoost 和 Light Gradient Boosting,因为它们组合了许多弱回归器/分类器来给出最终输出。
或者,最好的方法是创建一个人工的完全连接的神经网络,并传递这些特征,以获得关于%机会和住院天数的最终答案。
问题 3-b:死亡率是多少,即一个人在接下来的一周内是否会死在 ICU。你会选择哪种算法,为什么?
回答:一个人在接下来的一周内是否会死在 ICU,这是一个分类问题。对于这种分类问题,最好的算法还是 XGBoost/ LGBM 或人工神经网络。
c)定义一些问题中缺少的对你的问题有用的额外特征。如果这是一个人工智能驱动的医生,它将如何向病人的家人传达当前的状况?
问题中缺少的特征是患者的年龄、性别和既往病史。大部分治疗取决于患者的性别和年龄。
作为一名人工智能医生,我们可以向家庭成员展示回归结果,以便他们可以确定未来会发生什么。比如,如果在过去的 5 天里,存活的机会从 40%增加到 60%,那么给他们一个积极的信号,病人正在好转!
我希望这篇文章对你有所帮助。如有更多疑问,您可以通过 LinkedIn 与我联系。
在 pandas 和 sklearn 中使用稀疏数据集
在机器学习中,有几种情况下我们会遇到稀疏数据集。以下是一些例子:
- 推荐系统的用户评级
- 用户点击内容推荐
- 自然语言处理中的文档向量
Photo by David Dvořáček on Unsplash
稀疏数据集通常很大,很难使用标准的机器学习 python 工具,如 pandas 和 sklearn。普通本地机器的内存不足以存储或处理大型数据集的情况并不罕见。即使内存足够,处理时间也会显著增加。
在本文中,我们将给出一些简单的技巧,在使用 python 进行机器学习项目时,我们可以遵循这些技巧。
什么是稀疏矩阵?
稀疏矩阵是大部分元素为零的矩阵。相反,大多数元素都不为零的表称为稠密表。我们将矩阵的稀疏性定义为零元素的数量除以元素的总数。稀疏度大于 0.5 的矩阵是稀疏矩阵。
将稀疏矩阵作为密集矩阵处理通常是低效的,会过度使用内存。
使用稀疏矩阵时,建议使用专用的数据结构来实现高效的存储和处理。我们将在接下来的章节中引用 Python 中的一些可用结构。
通常,我们从包含分类变量的密集数据集开始。通常,我们必须对这些变量应用一键编码。当这些变量具有高基数(大量不同的值)时,一键编码将生成一个稀疏的数据集。
例子
考虑下面的用户电影评级表
Dense matrix
其中“评级”是多类分类问题的目标变量。
现在假设我们想要训练一个因式分解机器分类器。因子分解机器(FMs)是一种通用的预测器,能够在高稀疏性的问题中表现良好,如推荐系统。根据最初的论文我们需要将数据集转换成以下格式:
Sparse matrix
在上面的结构中,两个输入属性(用户和电影)都是一次性编码的。对熊猫来说,这是一个简单的单行转换。然而,在大型数据集的情况下,这可能会非常麻烦。
下面我们将展示一些在 pandas 和 sklearn 中促进这种数据集的转换和处理的方法。
数据集
我们将使用 MovieLens 100K 公共数据集,在这里可用。培训文件包含 943 个用户对 1682 个项目的 100,000 个评级。对于这个分析的范围,我们将忽略时间戳列。
让我们将数据加载到熊猫数据框中。
一键编码
假设我们想要将这个数据集转换成上一节中所示的格式,我们必须对列 user_id 和 item_id 进行一次性编码。对于转换,我们将使用get _ dummiespandas 函数,它将分类变量转换为指示变量。
在应用转换之前,让我们检查一下原始数据框的内存使用情况。为此,我们将使用memory _ usagepandas 函数。
Memory usage of data frame is 2.4 MB
现在,让我们应用转换并检查转换后的数据帧的内存使用情况。
在一次性编码之后,我们为每个用户创建了一个二进制列,为每个项目创建了一个二进制列。因此,新数据帧的大小为 100.000 * 2.626,包括目标列。
(100000, 2626)Memory usage is 263.3 MB
我们看到,与原始数据帧相比,转换后的数据帧的内存使用量显著增加。这是意料之中的,因为我们增加了数据框的列数。然而,新数据帧中的大多数元素为零。
技巧 1:使用 pandas 稀疏结构来存储稀疏数据
熊猫稀疏结构
Pandas 提供了有效存储稀疏数据的数据结构。在这些结构中,零值(或任何其他指定值)实际上并不存储在数组中。
仅存储非零值及其位置是存储稀疏数据集的常用技术。
我们可以使用这些结构来减少数据集的内存使用。您可以将此视为“压缩”数据帧的一种方式。
在我们的示例中,我们将把 one-hot 编码列转换成SparseArrays,这是一个一维数组,其中只存储非零值。
rating int64
user_id_1 Sparse[uint8, 0]
user_id_2 Sparse[uint8, 0]
user_id_3 Sparse[uint8, 0]
user_id_4 Sparse[uint8, 0]
...
item_id_1678 Sparse[uint8, 0]
item_id_1679 Sparse[uint8, 0]
item_id_1680 Sparse[uint8, 0]
item_id_1681 Sparse[uint8, 0]
item_id_1682 Sparse[uint8, 0]
Length: 2626, dtype: objectMemory usage is 1.8 MB
如果我们检查新数据帧的 dtypes ,我们会看到我们转换的列现在属于类型 Sparse[uint8,0] 。这意味着不存储零值,非零值存储为 uint8 。转换成稀疏数组时可以设置非零元素的数据类型。
此外,我们看到我们已经成功地显著减少了数据帧的内存使用。
到目前为止,我们已经设法减少了数据帧的内存使用,但是要做到这一点,我们首先要在内存中创建一个大型的密集数据帧。
技巧 2:在熊猫 get_dummies 中使用稀疏选项
使用熊猫 get_dummies 中的稀疏参数,可以直接创建稀疏数据框。该参数默认为假。如果为真,则编码列作为稀疏数组返回。通过设置 sparse=True ,我们可以直接创建一个稀疏数据帧,而无需在内存中预先存储密集数据帧。
rating int64
user_id_1 Sparse[uint8, 0]
user_id_2 Sparse[uint8, 0]
user_id_3 Sparse[uint8, 0]
user_id_4 Sparse[uint8, 0]
...
item_id_1678 Sparse[uint8, 0]
item_id_1679 Sparse[uint8, 0]
item_id_1680 Sparse[uint8, 0]
item_id_1681 Sparse[uint8, 0]
item_id_1682 Sparse[uint8, 0]
Length: 2626, dtype: objectMemory usage is 1.8 MB
在一键编码中使用稀疏选项使我们的工作流在内存使用和速度方面更加高效。
让我们继续分割输入和目标变量。我们将创建两组 X,y 向量,使用密集和稀疏数据帧进行比较。
分割 X,y
Memory usage is 262.5 MB
Memory usage is 1.0 MB
训练-测试分割和模型训练
接下来,我们转到 sklearn 对我们的数据集执行训练测试分割,并训练一个逻辑回归模型。虽然我们使用因子分解机器作为参考模型来创建我们的训练集,但这里我们将在 sklearn 中训练一个简单的逻辑回归模型,仅用于演示密集和稀疏数据集之间在内存和速度方面的差异。我们将在本例中讨论的技巧可转移到 Python FM 实现,如 xlearn 。
Pandas dataframe
Train-test split: 6.75 secs
Training: 34.82 secs
Sparse pandas dataframe
Train-test split: 17.17 secs
Training: 41.69 secs
我们注意到,尽管 X_sparse 更小,但是的处理时间比密集的 X 要长。原因是 sklearn 不处理稀疏数据帧,根据这里的讨论。相反,稀疏列在被处理之前会被转换为密集列,从而导致数据框大小激增。
因此,到目前为止使用稀疏数据类型实现的大小减少不能直接转移到 sklearn 中。此时,我们可以利用 scipy 稀疏格式,将我们的 pandas 数据帧转换成 scipy 稀疏矩阵。
技巧 3:转换成 scipy 稀疏矩阵
稀疏矩阵
Scipy 包提供了几种类型的稀疏矩阵来实现高效存储。Sklearn 和 imblearn 等其他机器学习包接受稀疏矩阵作为输入。因此,在处理大型稀疏数据集时,强烈建议将我们的 pandas 数据帧转换为稀疏矩阵,然后再将其传递给 sklearn。
在本例中,我们将使用 lil 和 csr 格式。在 scipy docs 中,你可以看到每种格式的优缺点。为了有效地构建矩阵,建议使用 dok_matrix 或 lil_matrix 。[ 来源 ]
下面我们定义一个函数来将数据帧转换成一个稀疏矩阵。我们首先按列构建一个 lil 矩阵,然后将其转换为 csr 。
Memory usage is 2.000004 MB
让我们用 csr 矩阵重复训练测试分割和模型训练。
Pandas dataframe
Train-test split: 0.82 secs
Training: 3.06 secs
Sparse pandas dataframe
Train-test split: 17.14 secs
Training: 36.93 secs
Scipy sparse matrix
Train-test split: 0.05 secs
Training: 1.58 secs
使用X _ 稀疏时,train_test_split 和模型训练都明显更快。因此,我们得出结论,使用稀疏矩阵是最有效选择。
稀疏矩阵的优势在较大的数据集或者稀疏度较高的数据集上会更加明显。
外卖食品
- 在 pandas 中处理大型稀疏数据帧时,我们可以利用 pandas 稀疏数据类型
- 我们也可以利用 get_dummies 中的 稀疏 选项,自动创建稀疏数据帧
- 当使用机器学习库时,我们应该考虑将我们的数据转换成稀疏矩阵
参考
稀疏矩阵的例子上面的稀疏矩阵只包含 9 个非零元素,有 26 个零元素。它的稀疏…
en.wikipedia.org](https://en.wikipedia.org/wiki/Sparse_matrix) [## 稀疏矩阵(scipy.sparse) - SciPy v1.3.1 参考指南
用于数值数据的二维稀疏矩阵包。构建稀疏矩阵:保存和加载稀疏矩阵:稀疏…
docs.scipy.org](https://docs.scipy.org/doc/scipy/reference/sparse.html) [## 稀疏数据结构-pandas 0 . 25 . 3+0 . g 43013d 49 f . dirty 文档
注意不推荐使用 SparseSeries 和 SparseDataFrame。他们的目的是同样服务于一个或稀疏…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html)
https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf
使用时间序列数据
如何使用 Python 准备用于分析的时间序列数据
NYC’s daily temperature chart (November 1, 2019 to December 11, 2019) produced with Matplotlib
数据科学家研究时间序列数据,以确定是否存在基于时间的趋势。我们可以分析每小时的地铁乘客,每天的温度,每月的销售额,等等,看看是否有各种类型的趋势。这些趋势可以用来预测未来的观测。Python 有许多可以很好地处理时间序列的库。我研究了金宝汤公司过去十年的股票价格,前五个交易日如下:
Campbell Soup Company stock price obtained from Yahoo Finance organized in a Pandas dataframe
准备数据
无论您的数据来自数据库、. csv 文件还是 API,您都可以使用 Python 的 Pandas 库来组织您的数据。一旦您将数据加载到 pandas 中,您将需要检查基于时间的列的数据类型。如果值是字符串格式,您需要将数据类型转换为 pandas datetime 对象。股票价格日期列是“年-月-日”格式的字符串数据类型我利用 pandas 的[to_datetime](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html)
方法将该列转换为 datetime 对象:
cpb.Date = pd.to_datetime(cpb.Date)
注意:我将数据存储在一个名为“cpb”的熊猫数据帧中
一旦日期列被转换成适当的数据类型,那么您就可以用set_index
方法设置数据帧的索引。
The data types of the ‘Date’ column before and after the to_datetime() and set_index() methods are applied. Note the change in the number of columns, this occurs when the index column is set.
您可能有多个时间间隔不同的数据帧。这就需要对数据的频率进行重新采样。假设您想要合并一个包含每日数据的数据帧和一个包含每月数据的数据帧。这就需要进行缩减采样,或者按月对所有每日数据进行分组,以创建一致的时间间隔。数据分组后,您需要一个兼容的指标,通常是更频繁的数据的平均值,以恢复一致性。这些步骤可以通过.resample()
和.mean()
方法在一行中完成:
cpb[‘Adj Close’].resample(‘MS’).mean()
MS
参数对应每月重采样。如果重新采样的值比使用的参数更频繁,则这称为下采样。.resample()
方法的有效间隔参数列表可在这里的“日期偏移对象”表中找到。
Downsampling from daily values to monthly values with the .resample() method
如果重新采样的值没有所用参数频繁,这称为上采样。在创建新日期间隔的位置添加空值。我将每日价格的值向上采样到每日两次。在我们的股票价格场景中,pandas 会将原始价格分配给午夜时间戳,将 null 值分配给中午时间戳:
The stock price data upsampled to twice daily. Hourly time stamps have been added at midnight and noon.
Pandas 的向前填充和向后填充选项是处理这些 NaN 值的两种方法。.ffill()
用当天早些时候记录的值填充 NaN 值:
Forward filling the NaN values. Each noon value was filled in with the value recorded before that time.
.bfill()
用当天晚些时候记录的值填充 NaN 值:
Back filling the NaN values. Each noon value was filled in with the value recorded after that time.
形象化
我以前写过关于 OHLC(开盘-盘高-盘低-收盘)图表,这篇文章包含复制下面图表的代码:
The Campbell Soup Company OHLC chart created with Plotly
有几种图表可以用来分析时间序列数据。一个对价格数据有帮助的是跨周期图表。Campbells 的年度价格图表如下:
Campbell’s Year Over Year Price Change in monthly intervals
在 2016 年和 2017 年,金宝汤的名义调整后月度股价相对较高,就像我们在 OHLC 图表中看到的那样。今年的同比图表没有很好地揭示趋势,因为我们处理的是名义价格,而不是增长率变化(见下文):
The year over year growth percentage for the Campbell Soup Company in monthly intervals
金宝汤的股票价格在八月到九月间持续增长,在二月到四月间持续下跌。应该进一步分析这些潜在的趋势。
结论
利用 Python,数据科学家可以为分析准备时间序列数据。Pandas 有基于附近日期的值来填充缺失值的方法。通过上面这样的可视化,我们可以看到是否有值得进一步研究的趋势。一旦发现趋势,就可以用它来预测未来的观察结果。
使用 VSCode 和 Jupyter 笔记本风格
Jupyter Notebook style in VSCode
如果你是机器学习算法入门,你会碰到 Jupyter Notebook。为了最大化效率,您可以将其概念与 VS 代码集成。因为这需要对如何设置 Python 环境有所了解,所以本文将提供一个介绍。
有几个原因可以解释为什么在单独的 IDE 中开发算法是有意义的。尽管 Jupyter 笔记本很棒,但是一旦代码变得越来越复杂或者项目越来越大,将所有代码放在一个文件中就不太方便了。VScode 提供了强大的 Jupyter 支持,允许将 python 代码转换成 Jupyter 笔记本代码,反之亦然。
目录
- 设置本地 python 环境
- 设置本地 pyenv
- 用 pipenv 安装依赖关系
- 使用具有 jupyter 功能的 python 文件
- 转换 python 文件和。ipynb 文件
- 附加:在 Vscode 中预览笔记本(不推荐)
- 结论
- 关于
设置本地 python 环境
首先,您需要设置 python 环境。有许多方法来组织 python 包/依赖项。这对一个初学者来说太难了。查看这篇文章,它解释了安装和管理 python 包的当前标准。非常准确地说,Gioele Barabucci 在安装软件包时会出现以下问题:
-您有足够的权限安装该软件包吗?也许你需要使用 sudo pip。这听起来不对。
-您的应用需要 2.0 版的 foolib。您系统中的另一个 Python 应用程序需要版本 1.8。糟糕,API 冲突。
其他将安装你的应用程序的人将需要 foolib。你需要把它记录在某个地方,例如 requirements.txt 中。但是使用 pip freeze 将会记录你现在正在使用的 foolib 的确切版本。2.x 系列的任何版本都可以,但是没有简单的方法告诉 pip 这一点。
实际上所有这些都是没有实际意义的,因为你需要原生数据类型,而它们只在 Python 3.7 中可用。不幸的是,您的 Linux 发行版只提供 3.5 版本。
设置本地 pyenv
安装 pyenv 来管理 python 版本。检查文档以完成安装过程。
例如,你可以用pyenv install 3.7.0
安装 Python 版本。
设置本地 python 环境后,您将能够在 VSCode 中设置新的 python 环境:
select the interpreter
如果您在设置解释器时遇到问题,请查看 VScode 文档。
使用 pipenv 安装依赖项
之后你可以用pipenv install jupyter
安装 jupyter 包。选择了正确的环境后,您将能够在 VSCode 中开发 jupyter 单元。
使用具有 jupyter 功能的 python 文件
在 VSCode 中,您可以将 jupyter 功能用作 python 文件中的单元格。执行单元格或运行命令Run Current File in Python Interactive window
打开一个已执行的、类似笔记本的概览:
Run the cells like in Jupyter Notebooks
转换 python 文件和。ipynb 文件
VSCode 提供了将 python 文件(带有 jupyter 标记单元格)转换为 Jupyter 笔记本文件的功能。这样,您就可以将笔记本导入为 python 文件,并像平常一样运行单元格。
Convert Jupyter Notebook files into Python files
更多信息参见文档。
附加:在 Vscode 中预览笔记本(不推荐)
VSCode 有一个扩展,允许您显示*。ipynb 文件。它叫做 VS Code Jupyter 笔记本预览器。尽管在 VSCode 中显示完整的笔记本很好,但在编辑或更改任何东西时,都没有像在笔记本中一样的功能。所以这只是为了预览。
Source from the extension docs
当我使用这个扩展时,它干扰了我的 python 扩展。有一个问题是所有的 python 文件都不能重新加载了。卸载这个扩展再次为我解决了这个问题。参见此处讨论的相关问题。
结论
总的来说,我们可以看到在 VSCode 中开发 jupyter 风格的单元格是很容易的。它允许你使用 VSCode editor 提供给你的所有特性(比如片段,从而提供丰富的开发体验。也许在这种环境下开发对你来说也是有意义的,而不是把所有的东西都写在笔记本上。;)
You can support me on https://www.buymeacoffee.com/createdd
关于
我认为自己是一个解决问题的人。我的强项是在复杂的环境中导航,提供解决方案并分解它们。我的知识和兴趣围绕商业法和编程机器学习应用发展。我在构建数据分析和评估业务相关概念方面提供服务。
连接到:
邮件
在西雅图不值钱?
或者,再见蓝色星期一
公平地说,我以前的经理是个有钱人。除了在我之前的公司担任董事之外,他还有一份利润丰厚的房地产兼职。鉴于他的计算机科学背景,他能够从房地产网站收集数据,并分析房子的属性如何影响其价格。因此,他可以识别相对于预期被严重低估的资产,明智地投资,并在转售时获得可观的利润。
因此,一段时间以来(或者至少从他第一次带我去他在伦敦市中心的两个私人会员俱乐部中的一个开始),与房地产销售的黑暗世界相关的任何类型的数据科学的商业案例对我来说都是显而易见的。
The Skyneedle — probably an outlier in King County’s property market…
我们在这里考虑的数据集是华盛顿金县的房产销售,它从西北部的西雅图延伸到东部的 Okanogan-Wenatchee 国家森林。这篇博客将探讨我们如何清理这个数据集,以期执行一个多变量线性回归模型来预测房地产的价格。
这项工作的 github 库可以在这里找到。
第一步。检查和理解数据
我们可以使用 Pandas 库导入数据(以 csv 文件的形式提供给我们),并使用。info()方法。
df = pd.read_csv(‘kc_house_data.csv’)
df.info()
这里的关键要点是:
- 我们有 21597 个观察值(每个代表一个销售交易),所以这是一个不错的样本量。
- 数据是完整的,除了“海滨”和“yr _ renovated”栏。我们将需要调查这些,看看我们是否能填补空白。
- “date”应该具有日期时间类型,以便于进一步分析。此外,“sqft_basement”具有“object”类型,这是我们不希望的(这应该是一个浮点数或整数)。这需要调查。
- 许多列标题都是不言自明的,尽管这并不普遍。我们可以利用(相当不错的) King County 网站来更好地了解等级和状况等属性。
- 我们可能需要对数据集做进一步的研究,以掌握“视图”等属性,这些属性的含义并不明显。
在继续之前,我们还应该通过检查下面的代码返回 0 来检查我们没有重复的数据。
len(df[df.duplicated()])
第二步。调查不完整的数据
我们检查“waterfront”和“yr _ renovated”列,查看我们的行中有多少部分包含至少一个 NaN 值。
len(df.loc[(df[‘yr_renovated’].isna()) | (df[‘waterfront’].isna())]) / len(df)
这会返回 27% —数据集的一个大块,太大而无法彻底删除。让我们依次研究这些列,看看我们是否可以更加细致入微。首先看一下 renovation 列,让我们通过查看最常出现的值来感受一下数据。
df.groupby(by = ‘yr_renovated’)[‘yr_renovated’].count().sort_values(ascending = False).head()
因此,我们看到,除了缺失值之外,还有 17,011 行的“0”是最后一次翻新的年份。这表明房子从未翻修过,或者更有可能的是,没有相关数据。我们可以放弃这个专栏——鉴于这里未知的份额,它不会提供洞察力。
df.drop(‘yr_renovated’, axis = 1, inplace = True)
我们现在检查“waterfront”列,发现只有 11%的行有 N/a。
len(df.loc[(df[‘waterfront’].isna())]) / len(df)
这是示例中足够小的一部分,我们可以使用。dropna()方法。
df.dropna(inplace=True)
第三步。修复数据类型
当我们开始进行探索性分析时,确保数据被转换为最合适的类型是非常重要的——当我们试图使用甚至基本的函数时,错误类型的数据可能会引发错误(当然,计算字符串的平均值在概念上是不可能的)。
使用 to_datetime()方法可以很容易地处理日期列:
df[‘date’] = pd.to_datetime(df[‘date’])
这种格式将允许熊猫识别日期的星期几、月份数,并将使该功能“有序”——即我们将能够按时间顺序排列日期。
“sqft_basement”功能当前是“object”类型,这表明我们可以在列中使用字符串条目作为占位符。让我们看看最常见的值。
df.groupby(by = ‘sqft_basement’)[‘sqft_basement’].count().sort_values(ascending = False).head(4)
它看起来像问号“?”条目是给我们带来麻烦的原因。我们需要删除它们,但理想情况下,我们会用值来替换问号,而不是简单地删除行。
令人高兴的是,我们可以从总居住空间和“上面”居住空间之间的差异来计算地下室面积,这在“sqft_living”和“sqft_above”列中给出。
我们可以编写代码来隔离“?”的索引基础值,并用此计算替换它们。
for i in list(df.loc[df[‘sqft_basement’] == ‘?’].index.values):
df.loc[i,’sqft_basement’] = df.loc[i,’sqft_living’] - df.loc[i,’sqft_above’]
完成这些后,我们可以将列转换为浮点数。
df[‘sqft_basement’] = df[‘sqft_basement’].astype(‘float64’)
第四步。寻找异常值
而最后几个步骤是相当客观的(一个地下室不可能有?平方英尺),处理异常值需要更多的细微差别。完全有理由问:如果没有客观的方法来识别和消除离群值,那么为什么要做这一切呢?这个答案真正触及了我们正在做的事情的核心。
最终,我们需要决定我们希望我们的模型做什么。这通常包括在模型精度和模型范围之间进行权衡。换句话说,你想要:
- 一个非常 精确的模型,涵盖一个种类广泛的物业类型,或者:
2.一种精确的模型,涵盖非常广泛的财产类型。
通常,我们会选择第一个选项——更好的预测是我们所追求的,即使这意味着缩小一点范围。
那么我们如何选择丢弃什么呢?考虑到我们变得有点主观,我们可以从视觉上思考这个问题。特别是,直方图将显示长尾(例如,非常昂贵的属性)。
我们可以得出结论,超过 7 间卧室、超过 5 间浴室或超过 30 万平方英尺的房子不太可能代表样本。类似地,根据上面的直方图,售价超过 100 万美元(实际上是 10 万美元)的房产容易扭曲模型。
看看市场的低端——我们只能看到 21 栋 3 级或 4 级的房子(小木屋或不符合建筑标准的房子)。这些不太可能有代表性。我们也可以假设一处房产应该至少有一间浴室。
我们可以从数据集中删除这些房子。
to_drop = df.loc[(df[‘bedrooms’] > 7) | (df[‘bathrooms’] > 5) | (df[‘bathrooms’] < 1) | (df[‘sqft_lot’] > 300000) | (df[‘price’] > 1000000) | (df[‘price’] < 100000) | (df[‘grade’] < 5)].indexdf.drop(to_drop, inplace = True)
与此同时,显示经度和纬度的散点图可能会显示地理异常值,但这并不代表更广泛的样本。我们可以使用熊猫内置的散点图来显示价格。
ax = df.plot.scatter(‘long’,’lat’,c = ‘price’,cmap = ‘cool’, alpha = 0.6, figsize = (10,8), grid = True);
请注意我们如何将这种分散与金县的实际地图联系起来。注意图表右上角(东北)的点。根据下面的地图,这些似乎对应于巴林、葛洛托或斯凯科米什。
当然,我们可以将这些城镇的房屋销售留在数据集中,但它们相对于其他房产在地理上是如此孤立,因此它们可能不具有代表性。计算出该组的经度和纬度范围后,我们可以用通常的方法删除这些行。
far_east = df.loc[df[‘long’] > (-120–1.69)]
df.drop(far_east.index, inplace = True)
当然,我们可以对数据集设置任意数量的限制来剔除异常值。但是这些看起来是明智的步骤,可以确保我们在进行回归时,结果不会有偏差。
这件衣服适合我吗?
了解机器学习如何用于向顾客推荐合适的服装尺寸
介绍
谁不爱网购?这是方便的,提供了广泛的产品选择,我们得到了很大的交易。零售商也受益于互联网提供的覆盖范围,并有机会建立自己的品牌。因此,网上时装业近年来出现了巨大的增长。然而,网上购物仍然很棘手,因为不同品牌的衣服尺码差异很大,这使得顾客很难找到合适的衣服。对于消费者来说,这会导致糟糕的购物体验,因为他们可能不得不退货,而对于零售商来说,这会导致金钱损失。
因此,自动提供准确和个性化的试穿指导对于改善在线购物体验和降低产品退货率至关重要。
先决条件
这篇文章假设读者熟悉以下概念。在继续下一步之前,您可以通过相关链接了解这些内容。
目标
我们知道,任何服装产品都有许多不同的目录尺寸。如今,许多在线零售商允许客户在退货过程中或留下评论时,对所购产品提供合适的反馈(如Small
、Fit
或Large
)。
为了进行区分,我们将一个产品(例如 Northface 夹克)称为“父产品”,而将它的不同尺寸(例如小型/中型 Northface 夹克)称为“子产品”。
假设我们有交易数据,其中每笔购买可以表示为(customer
、child product
、fit feedback
)的三元组,我们可以陈述我们的目标如下:
从顾客对购买的
child
产品的反馈中了解顾客的合身偏好和child
产品的尺码相关属性,以便向顾客推荐更合身的产品尺码。
数据集
在前一节定义目标时,我们假设我们有表单(customer
、child product
、fit feedback
)的事务数据。对于任何在线零售商来说,建立这种数据集都很容易,但对我来说却不是这样。
在网上进行大量搜索后,我发现了 ModCloth 和renttherrunway网站,它们提供了解决这个问题所需的数据信号。这些数据集包含顾客自我报告的合身度反馈以及其他附带信息,如评论、评分、产品类别、目录尺寸、顾客测量值等。我使用 python 的 Selenium 包提取了数据集,并作为我研究的一部分公开发布了它们。你可以在他们的页面上了解更多。
它开始的地方
我们试图解决的问题是由亚马逊印度的研究人员在 2017 年提出的产品尺寸推荐问题。他们专注于创建一个向顾客推荐鞋号的模型。由于鞋子的合适性可以通过 1 或 2 个维度(长度和/或宽度)来判断,从建模的角度来看,这是一个很好的起点。那么,我们先来了解一下他们提出的模型。
简单的模型
对于每一个客户和子产品,我们考虑一个潜在的变量,并说它表示他们的真实大小。
由于不同品牌之间的尺寸差异,子产品的实际尺寸可能与零售商指定的目录尺寸不同。如果我们能够知道真正的尺寸,那么所有子产品的尺寸都将是相同的,这反过来会使测量适合度变得更容易。
假设***u_c***
表示客户 c 的真实尺寸,而***v_p***
表示子产品 p 的真实尺寸。直观上,如果有一笔交易( c , p ,Fit
),那么真实大小***u_c***
和***v_p***
一定很接近,也就是说,|***u_c***
-***v_p***
|一定很小。另一方面,如果有一个事务( c , p ,Small
),那么客户的真实大小***u_c***
一定比子产品的真实大小***v_p***
大得多,或者说,|***u_c***
-***v_p***
|一定很大。最后,对于( c , p ,Large
)类型的交易,***v_p***
-***u_c***
必须是大的。
为了量化子产品对客户的适合度,将每个交易 t 的适合度分数定义为:
equation 1
其中 w 为非负权重参数。由于该拟合分数是连续的,并且最终目标是基于该拟合分数将交易分类为三个拟合类别之一,研究人员使用 有序回归 进行建模。
从概念上讲,我们定义了两个阈值参数
***b_1***
和***b_2***
,它们将连续标度分成对应于三个拟合等级的三个部分。我们可以给这种划分赋予意义,使得大于***b_2***
的拟合分数对应于*Small*
,小于***b_1***
的分数对应于*Large*
,并且在***b_1***
和***b_2***
之间的分数对应于*Fit*
。对于这三个部分中的每一个,我们可以认为大于阈值的分数在正面类中,小于阈值的分数在负面类中。解决这三个二进制分类问题将告诉我们一个事务属于哪个类。
为了学习潜在变量***u_c***
和***v_p***
,我们现在只需要两件事情:一个损失函数优化和一个优化技术。亚马逊作者使用 铰链损失 用于序数回归中的每个二元分类问题。已知铰链损失最大化分类器的决策边界之间的余量。因此,任何交易的目标函数 t 都可以写成:
equation 2
总损失只是每笔交易的损失之和,当任何交易具有合适的结果***Y_t***
、***Y_t*** = Large
的***f_w*(*t*) *> b_2***
、***Y_t*** = Small
的***f_w*(*t*) *< b_1***
和***Y_t*** = Fit
的***b_1 < f_w*(*t*) *< b_2***
时,总损失最小。作者使用随机梯度下降来优化目标。
预测和建议
由于在模型的离线评估中推荐产品是不可能的,作者考虑模型预测看不见的交易的拟合结果的能力作为模型推荐性能的代理。为此,他们将学习到的客户和子产品的潜在特征输入标准分类器,如逻辑回归分类器和随机森林分类器*,以产生合适的预测。*
挑战
尽管上述模型在鞋子数据集上运行良好,但它可能不够灵活,无法解决以下挑战:
- 像连衣裙和衬衫这样的服装产品具有相对更多的尺寸来确定是否合身。此外,对于不同的产品类别,每个顾客的合身偏好可能会有所不同,例如,顾客可能更喜欢宽松一点的夹克,而湿西装则更合身。因此,每个子产品和客户的单个潜在特征可能不足以捕捉数据中的所有可变性。
- 由于大多数交易被报告为
Fit
,因此客户的适合度反馈分布不均匀,因此很难了解购买何时不是Fit
。标准分类器不能处理标签不平衡问题,并导致有偏的分类,即在这种情况下,Small
和Large
类将具有较差的估计率。
如何才能提高?
在我们的研究工作中,我们应对上述挑战。为了应对第一个挑战,我们考虑了每个客户和子产品的多种潜在特性。直观地说,这使我们能够捕捉顾客对不同产品方面(如肩部、腰部等)的合身偏好。).为了解决第二个挑战,我们借助原型的度量学习技术。下图概述了该框架:
让我们深入研究一下方法论。
学习适合的语义
为了对 fit 的语义进行建模,我们使用潜在因素模型公式(最终帮助我们从数据中提取更多信息特征)来分解 fit 反馈信号。为此,我们将拟合分数定义为:
equation 3
其中下标 pp 表示父产品,和 v 是 k 维潜在特征, α 是全局偏差项, ⊕ 表示串联,表示元素式产品。偏见术语***b_t_pp***
表达了这样一种观点,即某些母产品由于其固有的特征/构造而更容易被报道unfit
,而偏见术语***b_t_c***
表达了这样一种观点,即某些顾客对合身性高度敏感,而其他顾客可能更容易适应。
虽然我们将能够从这个公式中学习到好的特性,但一个棘手的问题是,同一父产品的不同目录大小的拟合分数之间的顺序不能保证是一致的。这会使我们的模型变得无用。
我们希望,如果一个客户的子产品是Small
(分别是Large
),那么相应父产品的所有较小(较大)尺寸也应该是Small
( Large
)。
注意equation 3
,我们看到父产品 pp 和客户 c 的适合度分数仅根据子产品的潜在特性***v_p***
而变化。因此,为了解决这个问题,如果存在一个更小(更大)的目录产品*【p-(p+)的潜在特征,强制要求子产品 p 的所有潜在特征严格大于(更小)下一个更小(更大)的目录产品【p-的潜在特征就足够了。***
既然我们已经公式化了事务的适合度,我们可以将我们的目标函数写成如下:
equation 4
这类似于equation 2
,只是我们改变了拟合分数的定义并增加了单调性约束。我们可以使用投影梯度下降来优化该目标,该目标类似于随机梯度下降,不同之处在于每次更新后都会强制实施约束。
处理标签不平衡
为了处理标签不平衡问题(即Fit
标签的数据量比Small
和Large
多得多),我们求助于一种结合原型的度量学习技术。
原型技术的目标是从可用数据中创建一定数量的“原型”,使它们能够代表数据。原型可以是数据集中的一些关键数据样本,也可以是几个数据样本的组合。通常,创建的原型数量远远少于数据集中的数据样本数量。
简而言之,我们提出的原型技术首先通过从不同的类重新采样来改变训练数据的分布,这被证明在处理标签不平衡问题上是有效的。随后,我们采用大间隔最近邻 ( LMNN )度量学习技术,该技术通过将具有相同 fit 反馈的事务移动得更近而具有不同 fit 反馈的事务移动得更远来改进局部数据邻域,从而帮助 k-NN 方法更好地分类。形象地说,这个过程可以描述为:
度量学习技术
度量学习的目标是学习距离度量 D 使得 D ( k ,l)>D(k, m ) 对于任何训练实例 (k,l,m) 其中事务【T24 在这项工作中,我们使用 LMNN 度量学习方法,除了使同类事务更接近之外,还旨在保持不同类事务之间的差额。这最终改进了分类。具体来说, LMNN 通过以下方式做到这一点:**
- 识别每个事务的目标邻居,其中目标邻居是那些期望最接近正在考虑的事务的事务(即,来自同一类的事务)。
- 学习输入空间的线性变换,使得在变换空间中事务的结果最近邻居确实是其目标邻居。 LMNN 中的最终分类然后通过在变换(度量)空间中应用 k -NN 给出。 LMNN 使用的距离度量 D 就是 马氏距离 。
样机研究
LMNN 中的一个警告是,它在运行之前为每个事务固定 k 目标邻居。这允许在本地定义约束。然而,这也使得该方法对欧几里德距离选择相关目标邻居的能力非常敏感。**
为了减轻欧几里德距离的这种限制并解决标签不平衡问题,我们开发了一种启发式方法,通过仔细地对事务进行采样来减少来自离群值和其他无贡献事务(如过于接近其各自类的质心或已经选择的事务)的噪声,从而为每个类提供良好的表示。
你可以从我们的研究论文中获取详细的算法。
实验和结果
实验装置
我们试验并比较了以下五种方法:
- 1-LV-LR :亚马逊提出的鞋号推荐方法,如上所述。
- K-LV-LR:1-LV-LR 的简单扩展,我们认为每个客户和子产品的潜在特征是 K 维度的。其他一切都保持不变。
- K-LF-LR :在“学习 Fit 的语义”一节中给出的建议潜在因素变化。我们将学习到的因素作为特征直接用于逻辑回归分类器,以产生合适的结果。
- K-LV-ML :该方法与 K-LV-LR 相似,不同之处在于它使用建议的度量学习方法,而不是逻辑回归,来产生最终的拟合结果。
- K-LF-ML :这是我们提出的方法。
这些方法旨在评估:
- 捕捉配合语义相对于真实尺寸的有效性。
- 学习良好潜在表征的重要性。
- 提出的度量学习方法在处理标签不平衡问题中的有效性。
结果
我们根据平均 AUC 指标来衡量所有方法的性能。平均 AUC 只不过是单个类的 AUC 分数的平均值。
从表中,我们观察到我们提出的模型大大改进了鞋号推荐模型( e 对)。这可以归因于学习真实尺寸的合身语义。我们还观察到 模块布 的改进相对小于 模块布 的改进。这可能是由于与renttherrunway相比 ModCloth 拥有相对更多的冷产品和客户(交易非常少的产品和客户)(参见数据集页面上的统计数据)。我们还注意到,当使用来自 K-LV 方法的表示( d 对 b )时,度量学习方法并没有显著提高性能。这强调了学习良好表达的重要性。最后,我们看到 K-LF-ML 在两个数据集上都大大优于所有其他方法。
除了学习良好的表示之外, K-LF-ML 的良好性能也可以归因于所提出的度量学习方法在处理标签不平衡问题方面的能力,如上面左侧图中所示。
此外,右侧图表描绘了 K-LF-ML 在冷启动和热启动情况下的表现。对于冷产品,我们注意到 K-LF-ML 的表现始终优于 1-LV-LR ,虽然它们的表现整体略差。当我们考虑事务较多的产品时, K-LF-ML 的性能提升很快,而 1-LV-LR 的性能只有在给定足够多的样本时才能显著提升。
结束语
希望这篇文章能很好地概述产品尺寸推荐领域的当前研究。你可以在我的这个 Github Repo 找到这篇文章中描述的方法的实现。这是一个非常实际的问题,需要很好地解决,并且有几个开放的方向。例如,进一步改进的一个方向可以是利用审查来提高模型的可解释性,因为目前很难理解每个潜在维度对应于什么。通过将语言模型与潜在因素模型相结合,为每个潜在维度(由相应的主题表示)赋予特定的含义,这是可能的,正如本文中所做的那样。如果你有其他改进的想法,请在评论中告诉我,我们可以讨论。
原贴@https://rishabhmisra . github . io/Would-This-Clothing-Product-Fit-Me/
如果你喜欢这篇文章,并对我未来的努力感兴趣,可以考虑在 Twitter 上关注我:https://twitter.com/rishabh_misra_
你会从这个人那里购买见解吗?
如何评估和管理数据科学供应商
“你需要做的第一件事是把所有的数据放在同一个地方”。我们都记得启动了一千个痛苦的 IT 项目的咒语。理由是这将实现“360 度客户视角”或“数据驱动”决策。在数百万美元和多年的实施之后,许多公司很难量化这些计划的商业利益。这里最能说明问题的是,这些努力应该是“支持”分析,而不是提供价值。
从数据科学的角度来看,“数据第一”企业是被误导的。首先,有无限量的有用数据。其次,严格的数据科学自上而下地工作,从目标数据到相关数据。因此,您必须做的第一件事是定义一个业务目标和指标。
一种新的江湖骗子出现了,他正在出售*可操作的见解——*就像明胶一样有形。我们现在进入人工智能炒作泡沫已经有几年了。每一家咨询公司、系统集成商、IT 公司、软件提供商,甚至是税务和审计公司,现在都声称自己是专家。他们都声称占据了魔力象限中的黄金地段。当专注于交付价值时,一个构思良好的数据科学项目应该能够在 6 个月内实现收支平衡。你如何筛选数学、可视化和魔术演示来选择一个能够实现这一承诺的分析伙伴?
在本文中,我们认为分析与任何其他业务努力没有什么不同,可以相应地进行评估和管理。本文为这两个阶段都提供了建议。第一部分强调了在提案阶段鉴别和评估供应商的关键问题。第二部分重点介绍了成功管理项目的一些最佳实践。
1。 供应商尽职调查
问的第一个问题应该很明显:“他们独特的价值主张(UVP)是什么?”然而,许多供应商很难给出一个可信的答案。他们有唯一的数据源吗?他们有经过验证的分析资产吗?他们能够轻松集成和评估第三方数据的价值吗?他们有稳定的经验丰富的世界级科学家吗?
大多数大型组织依靠惯性运行。他们还倾向于只重视自己知道的东西,这导致他们将传统实践与数据科学混为一谈。如果他们的传统做法是销售大型机,他们现在正在推动云。系统集成商和咨询公司继续销售复杂的集成和咨询服务。四大倾向于销售商业智能和报告工具。其他人在卖“平台”。人们熟悉的说法是,“你首先需要这些东西;数据科学可以在以后添加进来。”这种推销有一个方便的附带好处,即基础设施、BI、计算资源、平台和软件工具不一定要根据业务价值或 ROI 来论证。可能需要很多这样的东西,但是它们的成本应该通过经验分析来证明,换句话说,就是数据科学。
**领导层实际上拥有数据科学专业知识吗?**许多公司只是将他们的服务和员工重新命名为“数据科学”所以,索要凭据并不失礼。试着在 Linkedin 或谷歌学术上查找他们的资料。令人惊讶的是,有多少组织实际上没有科学家,或者声称“在海外某个地方有一群数据科学家。”
在一次富有启发性的采访中,一位 Big 4 人工智能高级合伙人承认,他的团队没有数据资产,没有分析资产,没有数据科学家,两年内只有一个成功的咨询项目。他继续吹嘘说,他从未雇佣过超过 26 岁的数据科学家。他的业务主管甚至没有大学学位。在一次晚宴上与整个团队会面时,人工智能的全球负责人向我们讲述了科学家在社交方面有多么笨拙的故事。当被问及他的公司如何与其他供应商竞争时,他解释了自己的策略:“我们是值得信赖的专业服务合作伙伴。我们已经融入了他们的业务。我们也可以做他们的数据科学。”显然,他们的主要 UVP 是胆大妄为。
与当前的时代精神相反,该行业并不缺乏技能或初级资源。但是,对底层数学有深刻理解并有成功的数据科学解决方案记录的领导者肯定是缺乏的。大多数项目还需要现场工程主管,即直接与业务和运营主管合作的人员,以捕获流程流和业务约束、IT、决策点以及输入和结果数据的最终来源。没有人希望通过中介、跨时区等方式运行项目。它增加了大量的混乱、延迟和开销,并使交付面临风险。
他们的提案是否足够详细,可以进行技术评估?供应商的方法需要在技术上可信且可行。如果他们不能解释他们的技术,你为什么要相信他们?如果供应商通过引用专有知识产权来逃避细节,就没有“那里”。在 15 分钟的对话中就能被窃取的知识产权并不令人印象深刻。
此外,提出的特定算法可能与您的业务问题相关,也可能没有价值。“神经网络”是一个真实的东西,经过几十年的研究,已经得到了很好的定义。“自然语言处理”简单地意味着“我们从文本中提取信息”,可以指像关键字匹配这样简单的技术。“认知”是一个形容词。
合理的技术方法是成功的必要条件,但当然不是充分条件。例如,商业上成功的欺诈检测解决方案采用了各种各样的高级算法,包括异常检测、网络分析、图论、聚类分析、数论、决策树、神经网络、线性规划和卡尔曼滤波。图 1 比较了两种实时欺诈解决方案的性能。现有的解决方案(蓝色)结合了专家规则和优化的决策树。挑战者解决方案(黄色)结合了时间信号处理、NLP 和神经网络。(第三种方法,使用认知线性规划解决方案,未能改进传统解决方案,因此被淘汰出局。)challenger 解决方案的统计性能几乎是前者的两倍,这直接转化为欺诈损失或运营成本的 100%降低。所有这三种方法都有可靠的分析和理论基础;判断哪种方法更好的唯一方法是经验测试。
Figure 1: Performance Comparisons: don’t leave home without it
**在他们的案例研究中,有投资回报率或统计性能比较吗?**没有任何借口可以不报业绩。数据科学意味着对业务问题采取一种规范的、经验主义的方法。相对于 BAU 实践的性能和商业利益可以直接根据数据计算,或者如果必要的话,在冠军/挑战者现场展示中测试。
**案例研究是基于真实的客户数据进行的吗?**这个问题应该引起警惕,但市场上数量惊人的解决方案从未在真实数据上测试过,或在辅助或不相关的数据集上开发过。对于数据科学家来说,这简直是不可思议的,但基于“合成数据”的解决方案在传统软件公司中很常见,因为历史上的重点一直是建立标准化的 API,而不是从数据中提取价值。许多公司甚至无法安全地访问他们的客户数据,因此根本无法验证他们的解决方案是否能够带来价值。这种系统通常本质上是规则引擎,并且会严重限制下游决策技术的复杂性和价值。
**他们能提供参考网站吗?**一个令人沮丧的事实是,客户并不总是愿意作为参考网站。鉴于一些项目的敏感性,这是可以理解的。然而,如果一个有前途的供应商没有“Alpha”部署,并且其技术方法和团队看起来可信,那么您就有一个独特的机会来协商价格。作为一个公共参考网站和数据研究的权利都是资产,交易服务。签订联合开发协议可以让您以折扣价开发定制的新功能。在许多大型咨询公司,数据科学团队多年来一直处于亏损状态,他们渴望公开证明自己的财富,并获得真实数据集以进行研究和产品改进。
1.供应商管理最佳实践
理想情况下,数据科学项目应产生三个交付成果:诊断、价值证明和实施计划。应召集一个指导委员会来审查项目过程中的每项交付内容。
**建立指导/审核委员会。**从一开始,项目就应该由主要利益相关者(通常是损益表所有者、业务线或产品所有者、运营主管和分析主管)组成的常务委员会进行监督。内部 IT 团队领导可以进行尽职调查,但 IT 部门通常不具备数据科学技能,如果他们不了解任务或正在使用的技术,无论多么微不足道,他们都可能“耗费”一个项目(夸大实施成本估计)。通过不合作,分析团队可以主动或被动地破坏客观测试。另一种方法是聘请第三方顾问对供应商进行尽职调查。
**定义业务目标和绩效指标。**工程目标应尽可能由业务指标(利润、收入、成本、损失、发生率、转换率等)来定义。).清晰的指标还简化了尽职调查,为供应商建立了具体的客户期望和“成功标准”。两个糟糕的概念验证目标选择的例子是预测客户流失或创建客户细分。这些努力都没有任何直接的商业利益。(流失模型只是预测你会失去某些客户,而不是如何应对。)这两个用例都可以由中等技能的分析师快速交付;因此,这两个用例都不能作为对供应商能力或技术的有力测试。
任何业务成果或 KPI 都可以作为目标,如果可以衡量,就可以预测。在完整的数据诊断中,当前和潜在数据源的信息价值可以根据这些指标来衡量。即使是“可能的艺术”概念验证的价值也可以简单明了地用成本降低或确定的收入机会来表述。
在项目早期安排一次“可行/不可行”的评审。“没有计划能在接触数据后存活”,这句从克劳塞维茨那里借来的格言很少被证明是错误的。在提供数据访问权限的 2-3 周内,应安排中期审查,以审查初步结果。此时,供应商应该能够验证目标是否得到数据支持,并提供最低性能保证。另一方面,在分析过程中,供应商可能已经发现并推荐了备选目标和优先级。
这份初步报告,有时被称为“诊断”,或“规模和机会分析”,其本身应被视为可交付成果。通常,对效率、性能驱动因素和根本原因的深入实证分析会产生政策和流程的增值建议,而不需要预测性分析解决方案。从这个意义上来说,这种“可操作的见解”是数据科学项目的额外附带收益。虽然不能保证这种“快速修复”存在,但实施这些建议的好处通常会超过整个项目的成本。
审查结果后,指导委员会可以决定中止项目,重新承诺或重新确定目标的优先顺序,并创建一个计划来利用和测试迄今为止的学习成果。从这一点开始,该方法是否会交付价值应该是毫无疑问的;应该只有关于收益的大小和实施成本的问题。
最后,小心昂贵的实施计划。预先,有时仅估算指示性实施成本,因为在构建原型的过程中会发现或确定许多约束条件、数据和基础架构要求。无论供应商是销售 SaaS 平台,还是在内部安装,都应尽早为扩展功能的成本制定合理的规定,包括安装第三方和内部解决方案的能力。
一些公司将试图在这个阶段通过过度销售平台和基础设施来收回成本。一些行业严重依赖几十年前的决策引擎。替换这些系统是一个昂贵的提议,并且对于数据科学交付来说通常是不必要的。一个与基础设施无关的评分引擎可用于创建客户决策,而这些决策又可以“推”入传统决策引擎,作为表格加载到数据库中,或输入到现有的 BI 工具中。这种微创方法与生产数据流或记录系统并行工作,是实现价值的最快途径,也是成本最低的途径。增强和添加功能相对容易,因为数据科学交付团队拥有引擎的持续所有权。
数据科学的潜力继续被考虑不周的计划和伪装者稀释。成功的关键是进行严格的尽职调查,定义业务问题,建立明确的衡量标准,并进行价值验证。这些山里有金子,但要小心你选择的勘探对象。
Russell Anderson(交易分析咨询总监)在金融服务、零售、电子商务和生物医药行业开发数据科学解决方案方面拥有 30 多年的经验。他曾担任多家著名分析公司的科学顾问,包括 IBM、KPMG、Opera Solutions、NICE/Actimize、HCL、HNC Software、Mastercard Europe、JP Morgan Chase 和 Halifax Bank of Scotland。他拥有加州大学生物工程博士学位,发表了 30 多篇科学论文,并拥有多项商业预测解决方案专利。
欢迎提问/评论:anderson.transactionanalytics@outlook.com
你会让电脑选择你的衣服吗?
一个 AI 风格的盒子
不久前,人们普遍认为人们不会在网上买衣服——当然是书和 DVD,但不是你习惯看到、触摸和试穿的东西。然而现在,亚马逊是美国第二大服装零售商,最新的趋势是超越网上购物,现在是让算法为你购物。我不经常谈论时尚,因为我对它知之甚少,而且更喜欢坚持我对技术感兴趣的正常领域。但随着一项名为 Stitch Fix 的新服务(针对英国)的推出,这两个领域发生了碰撞,最终为像我这样的时尚挑战以及时间紧迫但有时尚意识的人带来了一些希望。
Stitch Fix 已经在美国获得了超过 300 万的活跃用户,现在可以在英国使用,这是它的第一次海外扩张。注册并完成你的个人资料不到 10 分钟,几天后,一箱由 Stitch Fix 的算法为你单独挑选并由他们(目前)的一位人类造型师精制的衣服会送到你的门口。对于懒惰的购物者或那些想得到私人购物者的建议但负担不起的人来说,Stitch Fix 是最方便的,还增加了一点时尚建议。
The kinds of questions that help Stitch Fix profile you
我的第一个盒子(或者他们称之为我的第一次“修复”)是成功的。尽管我对让算法向我推荐商品并不陌生,但我还是有点犹豫地打开了盒子——它们真的能挑选出我喜欢的衣服吗?作为算法的胜利,他们为我选择的一切都是合适的,是一个非常恰当的选择。(你可以要求他们突破你的风格界限,送你一些超出你正常衣柜舒适区的衣服)。如果你不想保留他们寄给你的所有东西,有一个免费的信封。我特别欣赏指导我如何组合物品的额外的风格建议。
我们购物的方式和地点正在迅速变化,Stitch Fix 是影响每个行业的不懈追求便利的又一个例子。我敢肯定,下一步 Stitch Fix 将与谷歌助手或亚马逊的 Alexa 合作,这样订购就像“嘿,谷歌,我想要一些新衣服”一样简单。在那之后,我想象我的谷歌助手将很快扫描我的日历,看到我可能需要一套新衣服的活动,并与 Stitch Fix 协调,及时为我找到另一个适合活动的修复。但是现在,您可以根据需要订购新的修复程序,或者安排每月或每季度更新一次。
“[我们的顾客]来找我们是因为他们不想去购物。五年后,人们会说,‘还记得我们不得不逛商场寻找自己的东西吗?“这太疯狂了!”"
Eric Colson,首席算法官,Stitch Fix
也许带有一点自私的夸张,Stitch Fix 表明,在未来的几年里,我们曾经去服装店寻找我们喜欢的商品,这将显得很奇怪。算法可以在瞬间评估数百万件商品,并非常准确地将它们与我们的偏好匹配起来。我知道很多人喜欢购物,如果把购物委托给一个看不见的分派箱子的算法,他们会感到震惊。但是,箱子出现在你家门口的纯粹便利性,以及合理的价格、优质的衣服和准确的选择,可能会让相当多的人依赖这项服务来购买他们的大部分衣柜。所需要的只是对网上购物感觉如何的另一个小小的信念飞跃。
用线性回归破解 Ames 住房数据集
在数据世界里争吵
预测和统计推断的回归模型
Image of Ames, Iowa by Tim Kiser from Creative Commons
埃姆斯住宅数据集(2011)的特征丰富性既诱人又令人困惑。当试图揭示其模式时,很容易被其丰富的特征所吸引。首先,了解艾姆斯数据集符合历史悠久的 特征定价法 来分析房价是非常有用的。一些领域知识会有很大帮助。
我之前学过一些详细的统计学/计量经济学。认识到“大数据”革命并被其前景所吸引,过去几个月我一直沉浸在编码和机器学习中。正是在这个过程中,我遇到了埃姆斯住房数据集。我结合了传统的计量经济学研究和机器学习工具。
我选择坚持使用透明的线性模型,在这种模型中,预测和统计推断的目标可以一起追求。这意味着运行普通最小二乘法(OLS)、岭和套索回归模型。请注意,我进行的分析是基于完整的原始 Ames 数据集,而不是数据集的 Kaggle 版本。
特征定价法
在计量经济学研究中,人们试图站在该领域前人的肩膀上拓展研究视野。没有哪两栋房子是完全相同的,特征价格模型的基本思想是特定社区和特定单元的特征有助于决定房价。
因此,重温一下影响房价的常见疑点是有用的。如果因变量(或目标变量)是销售价格,那么在其他条件不变的情况下,房子越大,价格自然越高。这应该和断言一个 18 英寸的披萨可能比一个 12 英寸的披萨更贵一样不言而喻。因此,资产规模度量将是关键变量,在 Ames 数据集中有几个这样的变量。一个更有趣的问题可能是,每平方英尺的价格如何以及为什么会因房屋而异,这就像问为什么某些比萨饼店能够收取更高的每片价格。房地产专业人士也倾向于关注每平方英尺(或每平方米)的价格,而不是整体价格。但这不是本文的目标。
任何一个购房者都会听说过这个表达**“位置,位置,位置”**,这也恰好是一个关于购房者的流行英国电视节目的标题,该节目已经连续播出了 19 年。因此,人们会认为社区“位置”和相关特征,如便利设施(如好学校、休闲设施)、交通网络(如地铁站附近)、社区美学(如绿树成荫的街道、漂亮的房子)以及社会经济声望等,会对房价产生影响。
另一组通常被认为对决定房价很重要的因素是房间的数量,尤其是卧室和浴室,以及厨房的条件。除此之外,房子的年龄和物理条件也很重要,建筑材料和任何结构改进也很重要。
影响房价的因素可以概括为房产大小、位置吸引力、附近的便利设施、房间数量、建筑材料以及建筑的年龄和条件。有了之前的研究,我开始使用 Python 分析数据。
数据清理&异常值
第一项任务是数据清理,一如既往。数据集最初有 2930 个观察值,我立即删除了三个变量,每个变量的观察值都不到 300。我认为很重要的“LotFrontage”(与房产相关的街道的线性英尺数)变量缺少 490 个观察值。当按照它们各自的“LotShape”类别分组时,我用平均“LotFrontage”来填充缺失的值。
Assign missing ‘LotFrontage’ by the mean value according to ‘LotShape’
然后,我绘制了因变量**“销售价格”的直方图。很明显,它有极高的值(一个长的右尾),并且不是正态分布的。这表明该数据包含大量异常值**。处理异常值可能很棘手,因为它们中的一些可能提供重要的信息,所以人们不希望过于宽松地定义异常值,然后将它们全部抛弃。****
Very skewed sale price target variable
鉴于物业规模指标的重要性,我迅速放大“GrLivArea”、“LotSize”和“GarageArea”作为潜在的解释变量。在美国,将地下室面积包括在房产面积中是不常见的。事实上,房利美(Fannie Mae)和 ANSI 的指导方针禁止在评估房产居住面积时计算地下室的面积,尽管人们可以想象这仍可能对房产价格产生一些影响。因此,我选择将地下室大小度量分开,并从其他地下室度量中构造了一个完整的地下室面积变量“BaseLivArea”。
现在是时候寻找离群值了。我分别在“SalePrice”、“GrLivArea”和“BaseLivArea”中寻找那些低于或高于各自平均值的 3 标准差的观察值。在“低于”类别中没有观察值,但是在所有三个变量的“高于”类别中有 66 个观察值。我从分析中删除了这 66 个观察值,约占样本的 2.3%。
然后,我查看了“SaleCondition”变量,它记录了每笔交易的销售类型,大多数观察结果属于“正常”销售类别,但有 218 个观察结果被记录为“异常”或“家庭”(家庭内部销售)。我放大了这些,注意到它们的平均售价和中值售价(以及每平方英尺的价格)都远低于整体样本。这促使我将这 218 笔不寻常的销售交易排除在分析之外。如果这些交易本质上不是主要的商业交易,那么将它们包含在数据集中会将偏差引入到试图估计住房特征和房价之间的经济关系的模型中。
“Abnormal” and “Family” transactions had mean values that were well below the average sale price
特征工程
我现在已经把观察次数从最初的 2930 次减少到 2617 次。然后,在对“销售价格”进行自然对数转换之前,我构建了一个变量来表示房产的年龄“年龄”。原木销售价格的直方图看起来更加对称,没有太多的极端值。得到的模型是一个对数线性模型,意味着是一个对数因变量,带有线性解释变量。**
Distribution of the log sale price is much more symmetrical
下一个问题是缺少一个位置合意性变量。“邻居”变量中列出了 28 个社区,但是不清楚如何对它们进行排序。根据每平方英尺的价格对这些社区进行排名,并以此作为位置合意性的衡量标准,这可能是显而易见的,但这种人为的构建自然会与房价相关联。这并不是一个好的研究方法,因为我们将构建一个部分由目标变量本身产生的复合特征变量。一个明显的位置合意性的代表是根据平均家庭收入对每个社区进行排名,但是数据集没有这样的可用数据。
Too many neighborhoods and difficult to tell many of them apart by the price distribution
处理这个问题的一个简单方法是把这 28 个街区虚拟化,然后把它们都扔进模型里。为如此大量的社区设置单独的假人的问题是:
- 对于某些街区,只有少数个观测值,其中 8 个不到 30 个观测值,在 2617 个样本观测值中,大多数不到 100 个;
- 在某些具有相似特征并因此具有相似房价分布的社区之间会有显著的多重共线性,正如在上面的剥离图中可以观察到的;和
- 如果一个人对统计推断感兴趣,那么在没有事先了解这些社区在合意性方面可能如何排名的情况下,他无法从这些假人中得出太多推断。
相反,我选择从数据集中的各种建筑质量和条件变量中构建一个序数邻域“位置”合意性变量,特别是“总体质量”、“总体成本”、“外部质量”、“外部质量”和“功能性”。我的方法背后的动机是假设一个社区越令人满意,它的住房结构和条件的质量就越好。
Crafting a “location” score out of the quality and condition variables
在没有额外信息或见解的情况下,我决定最好保持事情简单,只分配 4 个序数值用于“位置”合意性:1(低)、2(中低)、3(中高)和 4(高)。这 28 个街区按照它们的平均“位置”分数排序,我使用整体“位置”平均值将这些街区分为下半部分和上半部分。
平均“位置”值方便地将 28 个街区分成 14 个或以上的街区,以及 14 个以下的街区。然后,这两个组再次减半,这样,具有最低平均“位置”分数的七个邻域被指定为“位置”=1,随后的七个邻域被指定为“位置”=2,接下来的七个被指定为“位置”=3,剩下的七个被放置在“位置”=4。
完成这些任务后,我检查了每个“位置”排名的平均和中值“销售价格”和每平方英尺的价格,在“位置”得分和两个价格变量之间存在明显的正相关关系。这证实了所构建的“位置”变量可以是邻域合意性的良好代理。
‘Location’ ordinal variable has positive correlation to the sale price
这是练习中使用单个变量最多的工作。我还创建了年度虚拟数据,因为这些数据跨越了对美国房价产生重大影响的 2008 年金融危机。此外,我为四个区域中的每一个创建了四个假人(“分区”),一个假人靠近主干道或铁路线(“公路铁路”),一个假人靠近积极的便利设施,如公园(“便利设施”),一个用于车库(“车库”),一个用于平屋顶(“平屋顶”),一个用于高于一层楼的房屋(“两层楼”),一个用于地产地块的平坦轮廓(“平坦轮廓”)。
就室内机特有的特性(除了尺寸变量)而言,我选择了“总质量”作为顺序变量,“总长度”作为顺序变量,“卧室”代表卧室数量,“浴室”代表浴室数量,“壁炉”代表壁炉数量,“中央厨房”代表有中央空调,1 代表“卓越厨房”。
俗称【厨房卖房】**,壁炉尤为可取。所以我们会看到。**
回归分析
所得工程数据的相关矩阵显示,所选解释变量之间的相关性一般在+/-0.50 以下。唯一显示相关性大于+/-0.70 的解释变量对是“GrLivArea-浴室”对。“浴室”也分别显示了与“年龄”和“整体质量”的高相关性(超过 0.50)。因此,我决定从回归建模中去掉的“浴室”变量,以减少多重共线性。
在运行回归分析之前,工程数据被分割。 2010 销售数据被保留为保持测试集,而2006–2009数据被用于训练验证过程。
Separating the 2006–2009 data for train-validation, and reserving the 2010 data as the holdout test set
回归模型纳入了以下解释变量:年龄、GrLivArea、BaseLivArea、LowQualFinSF、车库面积、地段面积、地段临街面积、位置、卧室、壁炉、总面积、总面积、便利设施(虚拟)、道路轨道(虚拟)、两个仓库(虚拟)、平轮廓(虚拟)、平屋顶(虚拟)、车库(虚拟)、中央厨房(虚拟)、优秀厨房(虚拟)、Zoning_2(虚拟)、Zoning_3(虚拟)、Zoning_4(虚拟)和年度虚拟。
Splitting 2006–2009 data for train-validation procedure, and scaling them
目标变量是“销售价格”的自然对数。对于 2006-2009 年的数据,我使用了 70-30 的训练验证分割。在进行回归之前,解释变量也被标准化。训练验证集随后通过 OLS、岭和拉索线性回归模型运行。所有三个线性模型都提供了 0.91–0.92 的训练测试分数、大约 0.011 的 MSE 和大约 0.106 的 RMSE。****
OLS regression scores, MSE and RMSE
Ridge/Lasso regression scores, MSE and RMSE
因此,所有三个线性模型产生了相当一致的分数。这些系数在各个模型中也相当一致,如下所示。这些结果暗示了非常稳定的模型参数估计。模型系数暗示对 Ames 房价影响最大的六个因素依次为地上居住面积(正)整体品质(正)房龄(负)整体状况(正)地下室居住面积(正)和区位排名(正)。这六个变量在 95%的水平上也是统计显著的,我将在下面讨论。
Consistent coefficients across the OLS, Ridge and Lasso regressions
普通最小二乘回归假设残差相互独立,具有正态分布、均值为零和同伦方差。偏斜度和峰度表明误差分布近似正态,均值为零。残差图还表明,关于残差分布的各种 OLS 假设可能成立。完整的残差分析可以在我的 GitHub 页面上找到,文章底部有链接。
********
Residuals appear approximately normal and do not show a relationship with the target variable
坚持测试分数
在关键时刻,我在 2010 年的数据上测试了这个模型。从训练(2006 年至 2009 年的全部)数据来看,2010 年抵制测试集的 R 平方略有下降(0.9014 对 0.9160),但在 OLS、里奇和拉索模型中,得分再次相当一致。在维持测试集上,OLS 回归的 MSE 和 RMSE 分数分别为 0.012 和 0.110,再次显示出比上面的训练验证结果略有下降。****
OLS scores on the 2010 holdout set
Ridge/Lasso scores on the 2010 holdout set
残差也近似正态分布,均值为零。测试残差的分位数-分位数图** (qq-plot)特别显示了对正态分布的合理拟合。**
****
Residuals appear approximately normal and do not show a relationship with the target variable
统计推断
我利用2006-2010 年的全部数据集进行统计推断。这是为预测目的运行回归模型和为因果推断运行回归模型之间的主要区别之一。当我们为预测训练一个模型时,我们分割数据并分离一个测试集来评估模型预测的准确性(如上所述)。另一方面,在统计推断中,完整数据集通常用于得出参数估计值,并根据各种测试统计值进行评估。
该模型的 qq 图包含了 2006 年至 2010 年的全部数据,告诫人们不要过于乐观地使用该模型来预测房价。qq 图表明,该模型倾向于低估最高分位数的房价,高估最低分位数的房价**。因此,虽然最终模型可以解释超过 91% (R 平方)的房价变化,但在处理价格范围的极端情况时,它并不可靠。该模型在针对价格在 25%-75%四分位区间的房产时最为有效。**
q-q plot of the residuals for the full 2006–2010 data indicates some sizeable skews
至于准确计算各种解释变量如何影响房价,我首先需要计算个体系数标准误差和 p 值,以确定某个特定变量是否具有统计显著性。我还需要回到最初的非标准化解释变量**。下面是来自 Statsmodels 模块的2006–2010 年全部数据的 OLS 回归结果汇总表。**
OLS regression on full 2006–2010 unscaled data
可以看出,OLS 模型中的绝大多数解释变量在 95%的水平上具有统计显著性**(即 p 值 < 5%),如单个 t 统计和相关 p 值所示。例外情况是“RoadRail”和“LowQualFinSF”变量。如上所述,对房屋销售价格影响最大的六个变量都具有统计学意义。**
Transforming the log-linear coefficients back to their original units
提醒一下,模型是对数线性,其中目标变量是“销售价格”的自然对数。为了计算每个变量的单位变化对“销售价格”的影响,我们首先需要每个系数的指数。此外,对数线性模型的系数意味着目标变量的百分比变化对于解释变量的小单位变化。要获得具体的美元影响,最好使用平均值“销售价格”作为参考比较。****
Dollar impact of variables on “SalePrice”
左边的表格列出了每个解释变量的一个单位变化对埃姆斯平均房价的美元影响。为了简明扼要,我将只讨论那些在统计上有意义的变量。****
例如,住房单元的【年龄】增加一年,平均房价将减少 487 美元,,其他条件不变。相比之下,“位置”(记住这是一个四类有序变量)增加一个类别将使平均房价增加 6050 美元(第四个变量),所有其他因素不变。
此外,一个壁炉将使平均房价增加 4725 美元,而一个被评为“优秀”的厨房将使价格增加 13250 美元(第 19 和第 20 个变量)。****
所以确实“ 厨房卖房子 ”!
结论
基于从关于房价的享乐定价文献中获得的见解,我放大了各种房产规模指标,认为它们可能是回归模型的重要特征。我还对高度扭曲的房价目标变量进行了对数转换**。**
随后,对各种结构和内部因素进行了广泛的功能工程设计,这些因素预计将成为房价的重要决定因素,主要是位置合意性变量、分区模型、建筑质量和条件、便利设施的接近度以及高评级厨房等。工程数据然后通过三个线性回归模型** : OLS、里奇和拉索。**
在三个线性模型中发现了稳定的结果和分数。OLS 回归系数几乎与从岭回归和套索回归得到的系数相匹配。建模在训练验证集上实现了大约 91-92% 的 R 平方,这在 OLS、岭和拉索回归中是一致的。随后,它设法在 2010 年的维持测试集**中获得了 90%的分数,MSE 和 RMSE 的分数分别为 0.012 和 0.110。不同算法的结果也是一致的。**
各种房产大小变量**,即内部居住面积、地下室完工面积、地段面积和车库面积,都被发现与销售价格正相关和统计显著。建筑质量和条件也被发现是房价最重要的决定因素之一,同样重要的还有位置吸引力。**
实现这些结果不需要任何深奥的黑盒算法。仅仅是简单的线性回归**,即透明且可解释的,并且其中的单个解释变量可能要接受假设检验。因此,预测和统计推断的目标可以一起追求。更重要的是,在模型 和 个体变量水平上,线性回归结果可以很容易地传达给外行观众。**
(本练习的完整 Python 代码和数据可以在我的 GitHub 资源库中找到。如果直接渲染 GitHub 笔记本文件有问题,请使用 nbviewer 。)
如果你看到了阅读这些文章的价值,你可以在这里订阅 Medium来阅读我和无数其他作家的其他文章。谢谢你。
以数据为中心,并在模型校准过程中包括季节性选项
towardsdatascience.com](/time-seasonality-features-in-time-series-7e8236f7b04a) [## 揭开股票价格背后的潜在因素
美国大盘股的动态因素模型
medium.datadriveninvestor.com](https://medium.datadriveninvestor.com/uncovering-the-hidden-factors-driving-stock-prices-2891a1b99024) [## 用预测概率处理不平衡数据
葡萄牙银行营销数据集的案例研究
towardsdatascience.com](/tackling-imbalanced-data-with-predicted-probabilities-3293602f0f2) [## 外汇市场的等级聚类
使用无监督的机器学习来识别行为货币群
towardsdatascience.com](/a-hierarchical-clustering-of-currencies-80b8ba6c9ff3)***
编写干净、可靠的 Scala Spark 作业
编写可扩展和可维护的 Spark 作业的指南,这些作业提高了数据团队的生产力,并保证了更好的产品。
Photo by Andrew Ridley on Unsplash
由于新工具和数据平台的发展,现在通过编写 spark 作业来创建数据管道变得更加容易,这些工具和平台允许多个数据方(分析师、工程师、科学家等)。)来着重理解数据,写逻辑来获得洞见。然而,像笔记本这样允许简单脚本编写的新工具,有时没有得到很好的使用,并可能导致新的问题:大量的数据管道被编写为简单的 SQL 查询或脚本,忽略了重要的开发概念,如编写干净和可测试的代码。因此,确保代码可维护和可扩展的设计原则可能会被打破,在我们的产品应该是动态的环境中导致进一步的问题。
我们将展示一个包含一组步骤和模式的过程,以一个基本的 spark 管道为例,帮助您创建更好的 spark 管道。
第一步:首先定义你的管道结构
在每个管道中,第一步也是最重要的一步(甚至比代码清洁更重要)是定义其结构。因此,应该在数据探索过程之后定义管道结构,该过程提供了从输入产生预期输出所需的阶段。
让我们来看一个基本的例子,并定义一个管道结构。因此,我们有三个数据集:
userssourceexamples包含用户的信息。
gendersourceaexamples从源“ exampleA ”中读取包含某个国家特定名称的性别。
genderSourceBExampleDS读取源“ exampleB ”并包含另一个按名称排列的性别列表。但是,在这种情况下,它不会根据国家进行区分,而是添加一个计算的概率。
然后,管道的目标是生成一个数据集,其中列性别被添加到userssourceexamples中,如下所示:
- 当后缀有明确的性别时,例如,先生或女士会立即在性别栏中添加性别。
- 如果后缀没有性别那么在***【gendersourceaexamples】和 上搜索姓名添加一个新列 来源 _ a _ 性别 。然后,在genderSourceBExampleDS***上搜索名称,添加 source_b_gender 列。
- 最后,当 source_a_gender 不为空时,将此值设置为gender*,否则只有当概率大于 0.5 时,才使用source _ b _ gender**。*
此外,一些指标(如男性百分比和女性百分比)会生成到指标存储系统中。
管道结构
定义了以下阶段:
- ***数据读取:*从数据源读取。在这种情况下,数据存储在 S3 中。
- 数据预处理:**正如我们在数据中看到的,没有唯一的 ID 来连接或搜索数据,那么使用列名称、国家和后缀中的文本。但是,这些列包含要删除的无效数据(NaN)、多个字母大小写、首字母缩略词和在此阶段预处理的特殊字符。
- ***数据丰富:*在数据被清理并准备好使用之后,它在这个阶段中被转换和解析。因此,在一些业务验证之后,会添加新的列。
- ***数据度量:*该阶段包含与对转换后的数据进行聚合和计算相关的任务。
- ***数据写入:*最后,这个阶段处理将性别化结果写入 S3,将指标写入外部工具。
是不是挺简单的?是的,我们知道你在想什么。使用 spark 可能很容易编写这个管道。您打开 spark 上下文,读取数据集,解析数据集,最后连接以获得最终结果,并将它们写入输出数据存储。这看起来像这样。
Writing pipeline with SQL queries
这个脚本运行得相当好;它使用一些广泛的 spark SQL,并对 3 个不同的源重复相同的逻辑。除此之外,让我们想象以下场景:
- 这只是管道的第一个版本,将来还会添加多个新的性别化来源。
- 将添加多个新的共享预处理和转换任务。
- 一些清洁功能必须单独测试。例如,删除首字母缩写词的函数。此外,应该测试每个中间步骤的输出。
- 新的扩展和测试将自动配置 CI/CD。
因此,前面的源代码在一些不需要扩展性的情况下变得很有用;否则将变得不可扩展、不可测试和不可维护。因此,建议采取以下步骤。
步骤 2:保证一个合适的项目结构
定义一个适合您管道阶段的项目结构,这将使您的管道成为一个数据产品。
a)为之前定义的阶段定义项目结构,如下所示:
b)定义你的项目依赖,避免循环/未使用的依赖,明确定义测试*、【T2 提供】和编译之间的依赖范围。*
c)开始在每个将要定义小函数和通用函数的包中添加一个通用助手。现在,在主类的一个伴随对象中定义其余的函数。
d)根据您的需求,定义您将使用哪个版本的 spark API:RDDs、 datasets 或 dataframes 。
e)当您有多个使用相同逻辑的作业时,考虑创建一个通用的 spark-utils 库,供您公司的所有作业共享。
步骤 3:确保干净代码规则的符合性
创造符合预期的工作岗位是不够的。目前,公司努力减少技术债务,并创建易于所有数据方阅读和维护的自我记录管道。
这里要讨论的有迷思:“我作为数据科学家写代码”或者“数据工程师太严格了,读不了代码”。我们应该开始为人类编写代码,并确保最低限度的整洁,以保证我们的数据团队的生产力。因此,下面列出了需要考虑的重要规则。
A)使用有意义的名称
在管道中使用有意义的名称至关重要。让我们关注 Spark 变量、函数和列。
***基于 Spark 的变量:*始终使用不可变的变量,并停止用“df1”这样的名称来调用数据框。相反,使用代表 API 版本的有意义的名称和后缀。示例:
*val **usersExampleDS** = ...
val **usersExampleDF** = ...
val **usersExampleRDD** = ...*
*不要使用 **var,*而是使用 transform 方法,我们将在后面展示。
***函数:*连同有意义的名字,为你的函数创建一个带有动词前缀的白名单,比如 clean,with,filter,transform/enrich,explode ,它们是你的管道中唯一可能使用的单词。示例:
*def **cleanAcronymsFromText**
def **enrichNameWithPrefix***
***栏目:*栏目名称要清晰统一。如果您使用 dataset API,那么最好使用 camelcase 列名来匹配 case 类,如果您喜欢使用数据库表示法,则使用下划线。作为一个例子,下面的名字清楚地表明性别概率大于 50。
*gender_probability_**gt**_50*
如果您希望使用 case 类来定义数据集架构,请在设置架构之前,在伴生对象上定义一个列映射器并修改列名。示例:
***case** class **UserExample**(name: String,
email: String,
country: String)
object **UserExample** {
def **columnstranslatorMap**: Map[String, String] = *Map*(
**"First Name"** -> "name",
**"Email"** -> "email",
**"Country Code"** -> "country")
}*
那么你可以这样读:
*val **usersSourceExampleDS** = spark.read
.option("header", "true")
.csv(PATH)
.select(**UserExample.columnstranslatorMap**.keys.toList.map(c => *col*(c).as(**UserExample.columnstranslatorMap**.getOrElse(c, c))): _*)
.as[**UserExample**]*
在某些情况下,列名在多个阶段和功能中使用。因此,用列名定义一个特征*,并在主 spark 作业类中使用它。*
*trait **GeneralColumnConstants** {
val **N*ameSourceColumn***= **"firstName"** }
object **NamesGenderizerJob** with **GeneralColumnConstants** {}*
最后,只有在少数情况下,列应该是可变的,而不是使用带有 Column 函数的向 dataframes 添加一个新列来跟踪原始数据。**
***B)避免副作用:*永远首选无副作用的手术。例如,最好使用一行三元运算符 always if/else。
***if** (true) statement1 **else** statement2*
C)明智地在 null 或 Option 之间做出决定:我们被告知在 scala 中编写干净的代码 match-pattern 和 Optional 是强制性的。然而,在 spark 中,可选的可能会成为瓶颈,因为这种评估会增加额外的成本。因此,这里的建议是在开始时使用可选的,并且仅在性能可能受到影响的情况下将值更改为空。
D) 避免过度使用 scala implicit: 更喜欢始终使用 transform 函数来使用隐式转换数据帧,从而导致猴子修补。
尽可能避免 UDF:UDF 大部分时间都是黑盒,只有当你正在做的事情无法用 spark 优化的自定义 Spark 函数时才使用它们。然而,当你写 UDF 时,请确保你处理了所有的空情况。
避免过度使用累加器:累加器不是用来存储数据的,只有在需要少量计数时才使用,例如计算流水线中的错误数。
步骤 4:保证可扩展和可维护的管道
到目前为止,我们可以使用适当的结构并遵循一些干净的代码原则来编写 spark 作业。这很棒,但这并不意味着我们的代码已经是可扩展和可测试的了。为了达到这种可扩展性,坚实的原则可以发挥关键作用。让我们通过例子来了解这些原则。
单一责任原则
一个类应该有且只有一个改变的理由。
所有的管道模块、类或函数都应该负责功能的一个部分,在简单的代码单元中分离责任。我们可以看到 Step1 中定义的阶段,每个阶段都是一个责任单元。此外,这些步骤中的所有内部函数也应该负责一件事,在更大的功能中是可测试的和可链接的。让我们关注一下示例数据集的通用数据清理器。
Simple Names Normalizer.
上面,我们有两个函数负责一件事 removeUselessChars 和remove abstracts。然后,一个名为的链接函数清理使文本正常化。因此,namenormalizer只负责规范化姓名文本。让我们为这些函数中的每一个单独编写测试。
Names Normalizer Test.
打开/关闭原理
你应该能够扩展一个类的行为,而不用修改它。这个原则是构建可维护和可重用代码的基础。
罗伯特·马丁
让我们看看如何为我们的示例编写一个更干净的模块。
Cleaner Service Code.
在这里,遵守这一原则的要点是:
- 抽象 CleanerService 定义了通用的清理函数模板。这个抽象对于修改是封闭的,但是对于扩展是开放的。
- 清洁器的多种实现,例如:source cleaner, SourceBCleaner , SourceUsersCleaner 扩展抽象行为并添加具体实现。
- 然后,在我们必须使用清理器的地方,依赖注入是首选,我们将注入一个 CleanerService 实例,而不是一个低级类的实例。
- cleanerServiceHelper 使用转换函数链接多个函数。
- 最后,再次参见规格化文本函数,它将循环关注点从规格化文本的函数中分离出来,简单地调用先前定义的文本规格化器。此外,使用 foldLeft 使功能易于重用,并保持一个干燥的代码库。
让我们看看它的可测试性:
Cleaner Service Test Example.
利斯科夫替代原理
派生类必须可以替换它们的基类。
为了用我们的例子更容易地解释这个原理,一些清理功能由所有的实现共享。在这种情况下,我们可以在通用结构中定义实现。例如,我们可以在 cleanerServiceHelper 中为重复数据删除定义一个通用行为,并且只在与其他实现不同时使用具体实现。
界面分离原理
不应该强迫客户实现他们不使用的接口。
罗伯特·马丁
不要在你有所有管道函数的地方写大的单体特征或助手。你还记得我们定义阶段的第一步吗?。这些在这里是有用的,我们应该只能够清理,或者例如清理和浓缩而不调用其余的阶段。因此,建议通过阶段创建一个非耦合的抽象。让我们看一个丰富函数的例子。
Enrich Service Example.
在这之后,一个作业的主类应该是这样的。
Main Job Example.
从属倒置原则
依赖抽象,而不是具体。
在前面的例子中,我们看到了如何注入 CleanerService 抽象。这是 Spark 中这个原理的一个很好的例子。然而,我们可以更进一步,看看如何使用我们的数据框架应用依赖注入。假设我们想要创建一个函数,将 usersSourceExampleDS 与性别化的源代码连接起来,并单独测试。让我们为此写一个函数。
Dataframe Injection Example.
然后看看我们测试起来有多简单。
Testing Dataframe Injection.
结论
- 当生产 spark 作业并使它们可维护、可测试和可扩展时,这是一个需求;数据团队应该开始关注创建遵循干净代码原则并使测试任务更容易的管道。
- 一些产品开发概念适用于管道创建的开始,然后分析阶段和划分关注点是一个关键的活动。
- 干净的代码和坚实的原则适合 spark,通过使用它们我们可以保证团队生产力和代码质量保证。
少写,多探索
我经常陷入写代码的困境——我关注的是代码,而不是代码产生了什么。这从本质上来说并不是不好的做法,但是在排除的情况下,它没有实现探索性数据分析的目标。EDA 的目标是建立数据的心理模型。如果你把 90%的时间花在代码上,10%的时间花在数据上,那么在实现你想要的结果时,你只有 10%的效率。了解这一点后,您应该最大限度地利用您浏览数据的时间。如果你发现你在 EDA 上花了很多时间,但是感觉你不理解数据,考虑改变你的过程。改变你的过程的一个快速方法是问你自己,“我现在写的东西会提高我对数据的理解吗,或者它会帮助我实现另一个目标吗?”
重新调整你的过程是获得更好结果的捷径,但是改变你的习惯会有更深远的影响。改变两个习惯对我的探索性数据分析产生了巨大的影响:使用简单的图形和自动化我的过程。
简单的图形传达理解
If you squint and focus really hard you can see The Matrix! Oh wait, not, it’s just a regular matrix.
这不是一个简单的图形。它包含简单的元素,但作为一个整体,它是一个复杂的信息数组。研究它 15 秒钟。你看到了什么?
如果你在 15 秒后的回答是,“有些特征是相关的,有些是不相关的”,那么你就在一个好公司里。你甚至可以说,“有一些相关性,一些特征是离散的,而一些是连续的。”甚至更好!但总的来说,你并没有真正学到什么,是吗?当您导入数据时,您应该已经知道一些特征是离散的,并且我想您期望一些事情是相关的。所以总的来说,看了这张图之后,你的情况并不比看之前好多少!
这幅图信息密集,但含义却出人意料地浅。图表不能帮助你理解,也不能传达你对数据的理解。请记住,你的目标是开发一个心智模型——你通过将数据处理成一系列关系来实现这一点,而不是通过盲目记忆信息。为了有效地做到这一点,你应该致力于制作在信息密度和含义密度之间取得平衡的图形。这可能说起来容易做起来难。我将向您展示两个例子来启发这个概念,并希望帮助您改进实践。
使用离散数据探索要素的散点图
具有离散数据的要素散点图看起来很混乱。在最好的情况下,您会对特征的一些描述性统计数据(平均值、众数、最小值、最大值)有所了解,并对相关性有一个模糊的认识。这是国王郡住房数据中房价与卧室数量的散点图。
What am I supposed to get out of this?
这只是你在上面的散点图中看到的一个图表的放大版。在这种情况下,我们甚至无法了解价格对卧室的反应,因为存在我们看不到的潜在关系。简而言之,我不能从这个图表中推导出很多意义,因为有太多的信息需要处理,有些信息被隐藏了。
旨在制作在信息密度和意义密度之间取得平衡的图形
确定离散预测值和目标值(如果存在)之间关系的一种方法是查看预测值中每个离散值的可比数据。为了做到这一点,我们假设数据在每个离散值上表现相同。也就是说,离散值的目标分布(例如,有 3 间卧室的房屋的价格)具有与其他离散值的目标分布(例如,有 1 间、2 间、4 间等卧室的房屋的价格)相同的一般形状。这允许我们在给定的离散值下比较目标的平均值、中值或众数,以评估关系。考虑起来有点棘手,但下图应该有助于明确这个想法:
This is a graphic that allows me to extract meaning!
看起来价格和卧室之间的关系是线性的,尽管有很多卧室的房子也有问题。快速浏览一下预测值中的唯一值就会发现,拥有 8 间以上卧室的房子并不多。我们应该只考虑少于 8 间卧室的房子的数据,因为这符合我们关于分布行为的假设。
决定何时(以及如何)装箱
决定何时以及如何装箱并不总是那么简单。有时可以在散点图中发现要素中的离散分组,但通常您最终看到的是没有多大意义的散点图。这里有一个不是特别有用的价格和地段差异散点图(地段差异是一所房子和它的 15 个最近邻居之间的地段大小的比例差异)。
Relationship status: Complicated
价格和批次差异之间看起来肯定没有关系,但也许分位数分类的数据子集是有预测性的。问题是,你如何决定在哪里切断你的垃圾箱?为了回答这个问题,你需要平衡信息密度和图形的含义密度。
宁滨数据的目的是创建具有显著不同均值的组(1 ≠ 2 ≠ 3 … ≠ n)。在这种情况下,最好的图形叠加了分箱数据的分布,以便您可以快速评估宁滨是否合适。
That blue, green, red combination makes a gross color. This is a reminder to yourself not use those colors in the future.
从这个图表中可以清楚地看出,宁滨的这个数据并不能预测价格,即使它被归入极端分位数。我很有信心做出这个声明,因为不同组的分布没有显著差异,这一点我只需看一下图表就能知道。查看最有意义的图形使我能够理解数据中的细微差别,这意味着在这种情况下,我不会在我们的模型中找到一个无意义的预测值。
使用简单的图形
前面例子的成功在于它们的简单性。一眼看去,你和其他任何人都能够理解所描述的关系。你应该尽可能简化你的图形,遵循平衡信息密度和意义密度的原则。
自动化您的流程
到目前为止,如果你对自相矛盾的信息感到恼火,你可能并不孤单。我开始写这篇文章是让你“少写”和“多探索”,但是到目前为止我告诉你的是你应该多写和多探索。
事实是,没有一些摩擦你就无法探索,在我们的例子中就是写代码。所以,是的,为了探索更多,你必须多写一点代码。然而,关键是增加探索与写作的比例。最好的方法之一是有效地重用代码来自动制作图表。
您应该最大限度地利用浏览数据的时间
为此,我使用了 ipywidgets。Ipywidgets 是一个简单的模块,它允许您通过将控件嵌入到输出中来动态地与函数的参数进行交互。用一个例子来解释更容易,所以这里有一段代码和结果输出。
If only this was interactive!
不太明显的是,这段代码允许我查看这个数据集的小提琴图的 60 种不同组合!这是一个令人眼花缭乱的数量,但选择情节的能力提高了我理解我正在看的东西的可能性。原因很简单:我已经准备好更新我的心理模型,我可以有目的地浏览情节。
现在我已经向您展示了 ipywidgets,我可以澄清上一节中的示例了。这些图形都是使用 ipywidgets 自动生成的。他们都是一个更大的调查的一部分:第一个例子检查了离散的预测因子和它们与目标的关系;第二个示例检查邻居的数据。
如果你有兴趣了解关于 ipywidgets 的更多信息,那么我推荐你阅读链接到这里的 Will Koehrsen 的题为“Jupyter 笔记本中的交互控件”的文章。他通过大量的例子一步一步地向您介绍如何使用 ipywidgets。ipywidgets上的文档也为那些想直接找到源代码的人写得非常好。
有了 ipywidgets,您现在应该可以轻松地通过简单的图形探索更多的数据了。祝你好运,探索愉快!
编写自定义脚本来语音控制您的 wifi 智能插件
今天我们将使用一个 TP-LINK HS100 智能插头,直接从我们自己的电脑上控制它。(我个人用我的电脑关掉我电脑桌前所有的灯、扬声器和显示器。)
TP LINK 已经为 iOS 和 Android 提供了一个移动应用程序“Kasa Smart”,但当我试图直接从我的电脑上进行操作时遇到了麻烦,因此我决定开发自己的程序。
先决条件(我用的是 Macbook):
- 一个 API 调试器(我用的是失眠
- TP-LINK HS100
- 您已经在智能手机上的 Kasa smart 应用程序中设置了您的设备
- 可以用来编写 shell 脚本的东西(默认的文本编辑打开就可以了)
首先,我们需要从失眠症向设备发送一个 API 请求,以获取设备信息。因为我们想要提交一个请求,所以我们选择“Post”作为方法,选择“https://wap.tplinkcloud.com”作为我们想要发送请求的 URL。
对于要批准的请求,我们还必须添加我们的自定义参数,即“appType”、“cloudUsername”、“cloudPassword”和“terminalUUID”。apptype 将取决于您的操作系统,我的是“Kasa_iPhone”,cloudUsername & cloudPassword 将是用于登录移动应用程序的凭据,最后,terminalUUID 是一个自定义的 UUID,您可以自己指定(我建议使用这样的在线生成器:https://www.uuidgenerator.net/,并记下您的 UUID)。
下面是我最终请求的照片,其中删除了敏感信息:
Our first post request, just fill in your personal information
完成后,我们可以点击右上角的“发送”,如果所有信息都正确,您应该会收到如下内容:
Our response from the POST request
这里我们感兴趣的是“token”参数,它是我们检索设备信息的安全令牌。所以,复制/写下你的令牌,保存起来以备后用。
现在我们有了设备令牌,我们可以发送另一个更完整的 Post 请求来接收关于设备的完整信息。与另一个请求类似,我们将发送一个 post 请求到之前相同的地址,将我们的令牌参数添加到 URL。它应该看起来像这样https://wap.tplinkcloud.com?token =XXXXXXXX-Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx其中所有这些 X 都是您之前的个人令牌。包括
{“method”:“getDeviceList”}
在消息体中,我们准备发送。它应该类似于下面这样(去掉了我的令牌):
如果您做了所有正确的事情,您应该会收到一个没有错误的响应,应该是这样的:
Response from the request with sensitive information removed.
这里需要注意的是,“status”属性告诉我们智能插头的当前状态,“1”表示智能插头打开,“0”表示智能插头关闭。然而,我们感兴趣的,我需要你记下来的是“设备 Id”。
现在我们已经有了关于设备的所有必要信息,我们准备创建我们的脚本来打开/关闭 smartplug。
打开你最喜欢的文本编辑器(我只是使用 mac 默认的文本编辑器),插入这段代码:
curl—request POST “https://wap.tplinkcloud.com?token =XXXXX”
—data ’ { " method “:” pass through “,” params ":{ " device id “:XXXX,” request data ":{ \ " system \ ":{ \ " set _ relay _ state \ ":{ \ " state \ ":0 } } " } ’
—header " Content-Type:application/JSON "
删除粗体 X 并插入您自己的参数。这个脚本将向您的特定设备发送一个请求,要求它将状态设置为您指定的任何状态。
如前所述,这个脚本用于关闭设备;“0”表示设备关闭,“1”表示设备打开,因此在上面的代码中将“状态”从 0 更改为 1,表示您想要打开设备。
将文件保存为“外壳文件”(文件扩展名为。命令,这样“终端”会自动打开并运行该脚本。).因此,将该文件命名为“desk_off.command”并保存它。创建另一个文件,从“desk_off.command”文件中复制代码,并将状态从 0 更改为 1,并将其命名为“desk_on.command”。
您应该有两个文件;一个用于打开智能插头,一个用于关闭智能插头。尝试运行这两个文件,检查是否一切正常,如果不正常,返回到前面的步骤,以确保您没有遗漏任何内容。
现在我们准备好启动这些基于语音命令的脚本。打开“系统偏好设置”,进入“辅助功能”选项卡>“语音控制”>“命令”,然后按左下角的小“+”符号来添加新命令。在这里,您可以选择您应该说什么来触发动作,如果它应该能够在使用特定应用程序时触发,最后,在“执行”选项卡下,选择“打开 finder 项目…”并选择脚本。应该是这样的:
现在试着说出你的命令并测试它是否有效:)
编写 Python 包
对 Python 打包和编写您的第一个 Python 包的世界的温和介绍。
几年前当我键入pip install opencv-python
时,几秒钟过去了,神奇的事情发生了——我能够使用 OpenCV 不需要从源代码构建它,不需要编译器,这绝对是惊人的。我可以安装任何我想要的包,而不用担心编译它的源代码/安装或配置系统变量。
Python Packaging
这些年来,我继续使用 pip,每次都让我着迷。这真的让我想知道一项技术可以有多简单。作为一名 Windows 用户,每次我安装新东西时,我都必须配置系统路径。所以这绝对让我的生活更简单了。
几个月前,我决定编写自己的 python 包。我一直觉得图形很有趣,并决定编写一个完整的图形库,于是我开始编写 Grapho(这是正在进行的工作)。
目录中的所有文件总是可以导入同一模块中的文件。但是,如果您想让您的模块在整个系统中都可用,该怎么办呢?
你添加一个 setup.py 到你的模块中(当然有相关的配置)。
但是,如果您希望 python 包对全球每个人都可用,该怎么办呢?
你在 PyPI 上发布你的包。(所以大家可以 *pip install your-package-name*
)
说够了,我们来写点代码吧。我们先写一个简单的函数,然后打包。
# hello.py
def heythere(): print("hey there")
# setup.py
#!/usr/bin/env python# -*- coding: utf-8 -*-from setuptools import setup, find_packagessetup( author="Chinmay Shah", author_email='chinmayshah3899@gmail.com', classifiers=[ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3.7', ], description="Says hello", license="MIT license", include_package_data=True, name='hello', version='0.1.0', zip_safe=False,)
Setup.py 是 pip 在给定目录中查找的内容。它使用一种叫做 setuptools[1]的东西来实现打包。它包含软件包的名称、软件包的简要描述以及作者信息。不要忘记提到它是为哪个 python 版本制作的。所有这些元数据都很重要。
看起来简单?让我们试试这个东西。让我们安装它— pip install .
Installing your Package
但是我说它安装它是什么意思呢?
- 首先,它创建了一个轮子。whl)文件;这是用于包分发的可接受文件。
- 在安装过程中,它使用这个 wheel 文件,安装在
site-package
目录下(Anaconda 使用这个)。 - 如果从互联网下载,通常会在
pkgs
文件夹中创建一个本地缓存。
Running the installed module
但是什么是pip
?
pip 是 Python 的一个包安装程序,用来安装(主要)来自 PyPI(Python 包索引)的包。作为easyinstall
的升级版于 2008 年推出,尽管两者都是基于setuptools
构建的。[1]
PyPI 是一个巨大的包索引,任何人都可以提交他们的包,全球的任何人都可以做pip install your-package-name
。
请关注下一篇文章,在那里我将介绍如何编写包以及如何在 PyPI 上发布它。
有什么想法吗?在 Twitter 、 Linkedin 或电子邮件上联系。
参考:
[1]设置工具—https://setuptools.readthedocs.io/en/latest/
编写人们想阅读的代码
Image courtesy of Pixabay
或者如何写出不会让你想揪头发的代码!
那是 5 年前,我第一次创建我的第一个编程项目。我是一个毫无头绪的大一新生,正在学习 C++编程入门课程。我很想撒谎说我是一个天生的程序员,但是我的第一个项目的一个片段是这样的:
int main()
{
string a;
string b; getline(cin, a);
getline(cin, b); if ((a == 'g') || (b == 'c')
{
...
if (condition#2)
{
...
if (condition#3)
{
...
}
else if (condition#4)
{
...
}
} return 0;
}
耶克斯。我希望我能说筑巢就此停止。
提醒你,我的程序成功了。我曾经认为,只要程序产生适当的输出,它内部看起来如何并不重要。反正没有人会去读它。错。
事实上,花在阅读和写作上的时间比远远超过 10 比 1。作为编写新代码工作的一部分,我们不断地阅读旧代码。…[因此,]让它容易阅读,就更容易写作。罗伯特·马丁
事实是,人们花在阅读代码上的时间比写代码的时间还多。乍一看,这似乎并不明显,但是无论你是在浏览文档、调试旧程序,还是仅仅想出要写的下一行代码,你都在阅读代码。
这里有一个简单的练习:找一个你不久前写的程序,试着理解它做什么。真正投入进去。注意你如何命名你的变量和函数,缩进每一行,形成每一个if
语句。检查哪个函数调用哪个函数,以及为什么程序是这样构造的。目标是确定您是否可以在不运行程序的情况下重新创建程序的逻辑流。
准备好了?走吧。
Image courtesy of Pixabay
很难,不是吗?即使您的代码是平滑和干净的,您仍然必须努力尝试重新创建逻辑流程,因为您必须及时倒回,并记住您做出的导致您的代码的决策。
现在想象一下:如果是另一个人写的代码会怎么样?你需要付出更多的努力来理解它,因为你不知道代码是如何创建的。你所拥有的只是写在你面前的东西。如果那个人写了糟糕的代码呢?如果我写在上面的代码中有一个 bug(很可能有),而你的任务是调试它,你会有什么感觉?
这是另一个练习。下面是做完全相同事情的三段代码(注意,这些代码不是用特定的语言编写的)。看看哪个最容易理解:
What?
I can at least understand it
Aha!
在功能上,这三个代码片段是相同的。当你把它喂给这个假想语言的假想编译器(和 Python 没关系,我保证)时,它们都会产生同样的程序,你可以保证函数的所有输出都是一样的。
但是,很明显,第三个片段要好看得多。这本书不仅可读,而且读起来很有乐趣。它不会让人挠头或产生歧义。事情就是这样。通过简单地阅读它,你可以毫无疑问地确定这个函数是做什么的:它计算一个圆柱体的体积。如果你能一直这样写代码,那你就可以自我表扬了。你有伟大的代码。
虽然我承认这是一个简单的演示,但它包含了深刻的思想,可以帮助您轻松提高代码的可读性:
- 抓紧时间起名字,起好名字。这适用于变量、函数、文件名、包等等。确保你写的是自己的意思,写的是自己的意思。如果只有一种合理的方式来解释你的代码,那么你就降低了任何人(包括你自己)误解你的逻辑的风险。
- 注释解释代码是好的。解释代码的代码是最好的。评论的普遍问题是它们经常被忽视。当带有注释的代码被重构时,注释很有可能不会随之更新。曾经有用的评论现在变成了彻头彻尾的谎言。自文档化代码(例如上面的第三个片段)提供了与上面第一点相同的好处:减少了歧义和误解的机会(另外,看起来更清晰!).
作为奖励,我从我的第一个项目中学到了一课:
- 委托:使函数更短。把你的代码想象成一门生意。企业由不同的部门组成,每个部门都被期望执行特定的任务,并可能返回从这些任务中获得的结果。如果你在一个巨大的函数中运行所有的代码,你就像一个没有明确边界的公司;也许在开始时你能妥善管理,但一旦公司扩大,肯定会有问题,而且你不知道谁该负责。制作更短的函数。将特定的任务委派给这些职能部门,只让主要职能部门负责协调每项任务。他是头儿,那是他的工作。如果你的代码出现任何问题,你将确切地知道它发生在哪里以及为什么会发生。
Image courtesy of Pixabay
虽然这绝不是一个全面的指南列表,您可以做这些来使您的代码更具可读性和更令人愉快(事实上,它只是触及了表面),但是采用这三个简单的原则可以显著提高代码的质量。再次:适当命名,委派任务,让代码自己说话。
任何傻瓜都能写出计算机能理解的代码。优秀的程序员编写人类能够理解的代码。—马丁·福勒
记住:程序员不像作者。我们是作者。我们的工作不仅仅是编写功能性代码,还包括编写能够向他人传达我们的意图、独创性和创造力的代码,即其他人想要阅读的代码。
延伸阅读
- 编写干净代码的最高权威,鲍伯·马丁叔叔。他的名为 Clean Code 的书是任何程序员的必读之作。
- 设计原则和设计模式 —这是鲍勃大叔介绍面向对象编程的坚实原则的论文。
- 设计图案出自著名的四人组
- 关于设计模式的一站式网站。
阅读更多我的作品
关于阿德里安·佩雷亚
兼职数据科学家,兼职科技作家,全职书呆子。我在教授机器学习和可管理的编程中找到乐趣, 字节 大小的块。是的,我也很搞笑。咖啡上瘾者。数据可视化的吸盘。愿意为下一个最好的树状图付出我的 1 和 0。