Presto 联邦查询
在 AWS 上使用 Ahana 的 PrestoDB 沙箱开始使用 Presto 联邦查询
帖子的音频介绍
介绍
根据 Presto 基金会的消息,Presto ( 又名 PrestoDB ),不要和 PrestoSQL 混淆,是一个开源、分布式、ANSI SQL 兼容的查询引擎。Presto 旨在针对从千兆字节到千兆字节的各种大小的数据源运行交互式专门分析查询。Presto 被许多知名组织大规模用于生产,包括脸书、推特、优步、阿里巴巴、 Airbnb 、网飞、 Pinterest 、 Atlassian 、纳斯达克和 more 。
在接下来的文章中,我们将更好地理解 Presto 执行联邦查询的能力,联邦查询在不移动数据的情况下连接多个不同的数据源。此外,我们将探索 Apache Hive、Hive Metastore、Hive 分区表和 Apache Parquet 文件格式。
AWS 上的 Presto
AWS 上的 Presto 有几个选项。AWS 推荐亚马逊 EMR 和亚马逊雅典娜。Presto 预装在 EMR 5.0.0 及更高版本上。Athena 查询引擎是从 Presto 0.172 衍生而来的,并不支持Presto 所有的原生特性。然而,Athena 有许多类似的特性以及与其他 AWS 服务的深度集成。如果您需要全面、精细的控制,您可以自己在亚马逊 EC2、亚马逊 ECS 或亚马逊 EKS 上快速部署和管理。最后,你可以决定从 AWS 合作伙伴那里购买一个有商业支持的 Presto 发行版,比如 Ahana 或 Starburst。如果您的组织需要来自经验丰富的 Presto 工程师的 24x7x365 生产级支持,这是一个绝佳的选择。
联邦查询
在现代企业中,很少会发现所有数据都存储在一个单一的数据存储中。考虑到组织内部和外部的大量可用数据源,以及越来越多的专用数据库,分析引擎必须能够高效地连接和聚合多个来源的数据。 AWS 将联合查询定义为一种功能,它使数据分析师、工程师和数据科学家能够对存储在关系、非关系、对象和定制数据源中的数据执行 SQL 查询。’
Presto 允许查询其所在位置的数据,包括 Apache Hive 、 Thrift 、 Kafka 、 Kudu 和 Cassandra 、 Elasticsearch 和 MongoDB 。事实上,目前有 24 种不同的 Presto 数据源连接器可用。使用 Presto,我们可以编写连接多个不同数据源的查询,而无需移动数据。下面是一个 Presto 联邦查询语句的简单示例,它将客户的信用评级与他们的年龄和性别相关联。该查询联合了两个不同的数据源,一个是 PostgreSQL 数据库表postgresql.public.customer
,另一个是 Apache Hive Metastore 表hive.default.customer_demographics
,其底层数据位于亚马逊 S3。
阿哈纳
Linux Foundation 的 Presto Foundation 成员, Ahana 是第一家专注于将基于 PrestoDB 的即席分析产品推向市场并致力于促进 Presto 社区发展和传播的公司。Ahana 的使命是为各种形状和规模的组织简化即席分析。阿哈纳在 GV ( 前身为谷歌风险投资)的领导下,已经成功筹集到种子资金。Ahana 的创始人拥有丰富的科技公司工作经验,包括 Alluxio、Kinetica、Couchbase、IBM、苹果、Splunk 和 Teradata。
PrestoDB 沙盒
这篇文章将使用 Ahana 的 PrestoDB 沙箱(在 AWS Marketplace 上可用的基于 AMI 的 Amazon Linux 2 解决方案)来执行 Presto 联邦查询。
Ahana 的 PrestoDB 沙盒 AMI 允许您快速开始使用 Presto 查询数据,无论您的数据驻留在哪里。这个 AMI 将单个 EC2 实例沙箱配置为既是 Presto 协调器又是 Presto 工作器。它附带了一个由捆绑的 PostgreSQL 支持的 Apache Hive Metastore。此外,还捆绑了以下目录,以便使用 Presto 进行尝试、测试和原型制作:
- JMX:对监控和调试很有用
- 内存:在 RAM 中存储数据和元数据,当 Presto 重新启动时,这些数据和元数据将被丢弃
- TPC-DS:提供一组模式来支持 TPC 基准 DS
- TPC-H:提供了一组模式来支持 TPC 基准 H
阿帕奇蜂房
在这个演示中,我们将使用由 PostgreSQL 支持的 Apache Hive 和 Apache Hive Metastore 。Apache Hive 是一个数据仓库软件,它使用 SQL 来帮助读取、写入和管理驻留在分布式存储中的大型数据集。该结构可以被投影到已经存储的数据上。提供了命令行工具和 JDBC 驱动程序来将用户连接到 Hive。Metastore 提供了数据仓库的两个基本特性:数据抽象和数据发现。Hive 通过提供与 Hive 查询处理系统紧密集成的元数据存储库来实现这两个功能,以便数据和元数据保持同步。
入门指南
要开始用 Presto 创建联邦查询,我们首先需要创建和配置我们的 AWS 环境,如下所示。
演示的 AWS 环境和资源的架构
订阅 Ahana 的 PrestoDB 沙盒
首先,在 AWS Marketplace 上订阅 Ahana 的 PrestoDB 沙盒。确保你知道所涉及的费用。美国东部(N. Virginia)基于 Linux 的 r5.xlarge 按需 EC2 实例的 AWS 当前价格为每小时 0.252 美元。为了进行演示,因为性能不是问题,所以您可以尝试一个较小的 EC2 实例,比如 r5。
配置过程将引导您创建一个基于 Ahana 的 PrestoDB 沙盒 AMI 的 EC2 实例。
我选择在默认的 VPC 中创建 EC2 实例。演示的一部分包括使用 JDBC 本地连接到 Presto。因此,还需要为 EC2 实例包含一个公共 IP 地址。如果您选择这样做,我强烈建议将实例的安全组中所需的端口22
和8080
限制为您的 IP 地址(一个/32
CIDR 块)。
限制仅从我当前的 IP 地址访问端口 22 和 8080
最后,我们需要为 EC2 实例分配一个 IAM 角色,该实例可以访问亚马逊 S3。我将 AWS 托管策略[AmazonS3FullAccess](https://console.aws.amazon.com/iam/home?region=us-east-1#/policies/arn:aws:iam::aws:policy/AmazonS3FullAccess$jsonEditor)
分配给 EC2 的 IAM 角色。
连接亚马逊 3FullAccess AWS managed policy to the Role
部分配置还要求一个密钥对。您可以使用现有密钥或为演示创建新密钥。为了在将来的命令中引用,我使用一个名为ahana-presto
的键和我的键路径~/.ssh/ahana-presto.pem
。确保更新命令以匹配您自己的密钥的名称和位置。
完成后,将提供使用 PrestoDB 沙箱 EC2 的说明。
您可以从基于 web 的 AWS EC2 管理控制台查看包含 Presto 的正在运行的 EC2 实例。请务必记下公共 IPv4 地址或公共 IPv4 DNS 地址,因为在演示过程中将需要此值。
自动气象站云形成
我们将使用亚马逊 RDS for PostgreSQL 和亚马逊 S3 作为 Presto 的附加数据源。GitHub 上的项目文件中包含一个 AWS CloudFormation 模板cloudformation/presto_ahana_demo.yaml
。该模板在默认 VPC 中为 PostgreSQL 实例创建一个 RDS,并创建一个加密的 Amazon S3 存储桶。
这篇文章的所有源代码都在 GitHub 上。使用下面的命令来git clone
项目的本地副本。
git clone --branch master --single-branch --depth 1 --no-tags \
[https://github.com/garystafford/presto-aws-federated-queries.git](https://github.com/garystafford/presto-aws-federated-queries.git)
要从模板cloudformation/rds_s3.yaml
创建 AWS CloudFormation 堆栈,请执行下面的aws cloudformation
命令。确保您更改了DBAvailabilityZone
参数值(粗体显示的*)以匹配创建 Ahana PrestoDB 沙箱 EC2 实例的 AWS 可用性区域。以我为例,us-east-1f
。*
*aws cloudformation create-stack \
--stack-name ahana-prestodb-demo \
--template-body file://cloudformation/presto_ahana_demo.yaml \
--parameters ParameterKey=DBAvailabilityZone,ParameterValue=**us-east-1f***
要确保运行在 Ahana PrestoDB 沙箱 EC2 上的 Presto 可以访问 RDS for PostgreSQL 数据库实例,请手动将 PrestoDB 沙箱 EC2 的安全组添加到数据库实例的 VPC 安全组入站规则内的端口5432
。我还将自己的 IP 添加到端口5432
,使我能够使用 JDBC 从我的 IDE 直接连接到 RDS 实例。
AWS CloudFormation 堆栈的 Outputs 选项卡包括一组值,包括 PostgreSQL 实例的新 RDS 的 JDBC 连接字符串JdbcConnString
,以及亚马逊 S3 bucket 的名称Bucket
。在演示过程中,所有这些值都是必需的。
准备 PrestoDB 沙箱
我们需要采取几个步骤来为我们的演示正确准备 PrestoDB 沙箱 EC2。首先,使用 PrestoDB 沙盒 EC2 SSH 密钥将scp
和properties
目录指向 Presto EC2 实例。首先,您需要将EC2_ENDPOINT
值(粗体显示的)设置为 EC2 的公共 IPv4 地址或公共 IPv4 DNS 值。您可以硬编码该值,或者使用下面显示的aws ec2
API 命令以编程方式检索该值。**
**# on local workstation
EC2_ENDPOINT=$(aws ec2 describe-instances \
--filters "Name=product-code,Values=ejee5zzmv4tc5o3tr1uul6kg2" \
"Name=product-code.type,Values=marketplace" \
--query "Reservations[*].Instances[*].{Instance:PublicDnsName}" \
--output text)scp -i "~/.ssh/ahana-presto.pem" \
-r properties/ sql/ \
ec2-user@${EC2_ENDPOINT}:~/ssh -i "~/.ssh/ahana-presto.pem" ec2-user@${EC2_ENDPOINT}**
设置环境变量
接下来,我们需要设置几个环境变量。首先,替换下面的DATA_BUCKET
和POSTGRES_HOST
值(粗体显示的)以匹配您的环境。PGPASSWORD
值应该是正确的,除非你在 CloudFormation 模板中修改了它。然后,执行命令将变量添加到您的.bash_profile
文件中。**
***echo """
export DATA_BUCKET=**prestodb-demo-databucket-CHANGE_ME**
export POSTGRES_HOST=**presto-demo.CHANGE_ME.us-east-1.rds.amazonaws.com**export PGPASSWORD=5up3r53cr3tPa55w0rd
export JAVA_HOME=/usr
export HADOOP_HOME=/home/ec2-user/hadoop
export HADOOP_CLASSPATH=$HADOOP_HOME/share/hadoop/tools/lib/*
export HIVE_HOME=/home/ec2-user/hive
export PATH=$HIVE_HOME/bin:$HADOOP_HOME/bin:$PATH
""" >>~/.bash_profile***
或者,我建议用可用的更新来更新 EC2 实例,并安装您喜欢的工具,如htop
,来监控 EC2 的性能。
***yes | sudo yum update
yes | sudo yum install htop***
运行在 r5.xlarge EC2 实例上的htop
视图
在进一步配置演示之前,让我们回顾一下 Ahana PrestoDB EC2 实例的几个方面。Ahana 实例上预装了几个应用程序,包括 Java、Presto、Hadoop、PostgreSQL 和 Hive。显示的版本是截至 2020 年 9 月初的最新版本。
***java -version
# openjdk version "1.8.0_252"
# OpenJDK Runtime Environment (build 1.8.0_252-b09)
# OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)hadoop version
# Hadoop 2.9.2postgres --version
# postgres (PostgreSQL) 9.2.24psql --version
# psql (PostgreSQL) 9.2.24hive --version
# Hive 2.3.7presto-cli --version
# Presto CLI 0.235-cb21100***
Presto 配置文件在/etc/presto/
目录中。配置单元配置文件在~/hive/conf/
目录中。这里有几个命令,您可以使用它们来更好地了解它们的配置。
***ls /etc/presto/cat /etc/presto/jvm.config
cat /etc/presto/config.properties
cat /etc/presto/node.properties# installed and configured catalogs
ls /etc/presto/catalog/cat ~/hive/conf/hive-site.xml***
快速配置
要配置 Presto,我们需要为新创建的 RDS for PostgreSQL 实例创建并复制一个新的 Presto postgresql
目录属性文件。修改properties/rds_postgresql.properties
文件,将值connection-url
(粗体显示的)替换为您自己的 JDBC 连接字符串,显示在 CloudFormation Outputs 选项卡中。**
**connector.name=postgresql
connection-url=**jdbc:postgresql://presto-demo.abcdefg12345.us-east-1.rds.amazonaws.com:5432/shipping**
connection-user=presto
connection-password=5up3r53cr3tPa55w0rd**
使用sudo
将rds_postgresql.properties
文件移动到正确的位置。
**sudo mv properties/rds_postgresql.properties /etc/presto/catalog/**
我们还需要修改现有的配置单元目录属性文件,这将允许我们从 Presto 写入非托管配置单元表。
**connector.name=hive-hadoop2
hive.metastore.uri=thrift://localhost:9083
**hive.non-managed-table-writes-enabled=true****
以下命令将用包含新属性的修改版本覆盖现有的hive.properties
文件。
**sudo mv properties/hive.properties |
/etc/presto/catalog/hive.properties**
为了完成目录属性文件的配置,我们需要重新启动 Presto。最简单的方法是重启 EC2 实例,然后 SSH 回到实例中。由于我们的环境变量在.bash_profile file
中,它们将在重启和登录回 EC2 实例后继续存在。
**sudo reboot**
将表添加到 Apache Hive Metastore
我们将使用 RDS for PostgreSQL 和 Apache Hive Metastore/Amazon S3 作为联邦查询的附加数据源。Ahana PrestoDB 沙盒实例预配置有 Apache Hive 和 Apache Hive Metastore,由 PostgreSQL 支持(EC2 上预安装的独立 PostgreSQL 9.x 实例)。
沙箱的 Presto 实例预配置了用于 TPC 基准 DS (TPC-DS)的模式。我们将在 Apache Hive Metastore 中创建相同的表,它们对应于 TPC-DS 数据源的sf1
模式中的三个外部表:tpcds.sf1.customer
、tpcds.sf1.customer_address
和tpcds.sf1.customer_demographics
。Hive 外部表描述了外部文件的元数据/模式。外部表文件可以由 Hive 外部的进程访问和管理。例如,下面是在配置单元 Metastore 中创建外部customer
表的 SQL 语句,该表的数据将存储在 S3 存储桶中。
**CREATE EXTERNAL TABLE IF NOT EXISTS `customer`(
`c_customer_sk` bigint,
`c_customer_id` char(16),
`c_current_cdemo_sk` bigint,
`c_current_hdemo_sk` bigint,
`c_current_addr_sk` bigint,
`c_first_shipto_date_sk` bigint,
`c_first_sales_date_sk` bigint,
`c_salutation` char(10),
`c_first_name` char(20),
`c_last_name` char(30),
`c_preferred_cust_flag` char(1),
`c_birth_day` integer,
`c_birth_month` integer,
`c_birth_year` integer,
`c_birth_country` char(20),
`c_login` char(13),
`c_email_address` char(50),
`c_last_review_date_sk` bigint)
STORED AS PARQUET
LOCATION
's3a://**prestodb-demo-databucket-CHANGE_ME**/customer'
TBLPROPERTIES ('parquet.compression'='SNAPPY');**
三条CREATE EXTERNAL TABLE
SQL 语句包含在sql/
目录中:sql/hive_customer.sql
、sql/hive_customer_address.sql
和sql/hive_customer_demographics.sql
。在继续之前,需要在所有三个文件中手动更新桶名(上方粗体显示的)为您自己的桶名。**
接下来,运行下面的hive
命令,在现有的default
模式/数据库中的 Hive Metastore 中创建外部表。
**hive --database default -f sql/hive_customer.sql
hive --database default -f sql/hive_customer_address.sql
hive --database default -f sql/hive_customer_demographics.sql**
为了确认成功创建了表,我们可以使用各种hive
命令。
**hive --database default -e "SHOW TABLES;"
hive --database default -e "DESCRIBE FORMATTED customer;"
hive --database default -e "SELECT * FROM customer LIMIT 5;"**
使用“描述格式化的客户地址”Hive 命令
或者,您也可以使用hive
命令访问 CLI,从 Hive 内部交互地创建外部表。将 SQL 文件的内容复制并粘贴到hive
CLI。使用quit;
退出配置单元。
Apache Hive 中的交互式查询
亚马逊 S3 数据源设置
创建外部表后,我们现在将从 TPC-DS 数据源的三个表中选择所有数据,并将这些数据插入到相应的 Hive 表中。物理数据将以高效的列存储格式写入亚马逊 S3, SNAPPY 压缩的 Apache Parquet 文件。执行以下命令。接下来,我将解释为什么customer_address
表语句有点不同。
**# inserts 100,000 rows
presto-cli --execute """
INSERT INTO hive.default.customer
SELECT * FROM tpcds.sf1.customer;
"""# inserts 50,000 rows across 52 partitions
presto-cli --execute """
INSERT INTO hive.default.customer_address
SELECT ca_address_sk, ca_address_id, ca_street_number,
ca_street_name, ca_street_type, ca_suite_number,
ca_city, ca_county, ca_zip, ca_country, ca_gmt_offset,
ca_location_type, ca_state
FROM tpcds.sf1.customer_address
ORDER BY ca_address_sk;
"""# add new partitions in metastore
hive -e "MSCK REPAIR TABLE default.customer_address;"# inserts 1,920,800 rows
presto-cli --execute """
INSERT INTO hive.default.customer_demographics
SELECT * FROM tpcds.sf1.customer_demographics;
"""**
使用 AWS 管理控制台或 AWS CLI 确认数据已加载到正确的 S3 存储桶位置,并且是拼花格式。请放心,即使 S3 控制台错误地将Compression
显示为None
,拼花格式的数据也会被快速压缩。您可以使用像 parquet-tools 这样的实用程序轻松地确认压缩编解码器。
亚马逊 S3 中按关键字前缀组织的数据
使用 S3 的“选择”功能预览快速压缩的拼花地板格式数据
分区表
customer_address
表是唯一的,因为它是由ca_state
列进行分区的。分区表使用PARTITIONED BY
子句创建。
**CREATE EXTERNAL TABLE `customer_address`(
`ca_address_sk` bigint,
`ca_address_id` char(16),
`ca_street_number` char(10),
`ca_street_name` char(60),
`ca_street_type` char(15),
`ca_suite_number` char(10),
`ca_city` varchar(60),
`ca_county` varchar(30),
`ca_zip` char(10),
`ca_country` char(20),
`ca_gmt_offset` double precision,
`ca_location_type` char(20)
)
**PARTITIONED BY (`ca_state` char(2))** STORED AS PARQUET
LOCATION
's3a://**prestodb-demo-databucket-CHANGE_ME**/customer'
TBLPROPERTIES ('parquet.compression'='SNAPPY');**
根据 Apache Hive ,一个表可以有一个或多个分区列,并且为分区列中的每个不同的值组合创建一个单独的数据目录。由于 Hive 表的数据存储在亚马逊 S3 中,这意味着当数据被写入到customer_address
表中时,它会根据状态被自动分成不同的 S3 键前缀。数据被物理地“分区”。
亚马逊 S3 按州划分的客户地址数据
每当在 S3 中添加新分区时,我们需要运行MSCK REPAIR TABLE
命令来将该表的新分区添加到 Hive Metastore 中。
**hive -e "MSCK REPAIR TABLE default.customer_address;"**
在 SQL 中,谓词是一个条件表达式,其计算结果为布尔值,可以是真或假。定义与查询的条件/过滤器(谓词)中经常使用的属性一致的分区可以显著提高查询效率。当我们执行一个使用相等比较条件的查询时,比如ca_state = 'TN'
,分区意味着查询将只处理对应的ca_state=TN
前缀键中的一部分数据。在customer_address
表中有 50000 行数据,但是在ca_state=TN
分区中只有 1418 行(占总数据的 2.8%)。由于 Parquet 格式具有快速压缩的额外优势,分区可以显著减少查询执行时间。
将数据添加到 PostgreSQL 实例的 RDS
为了进行演示,我们还将把tpcds.sf1.customer_address
表的模式和数据复制到新的 PostgreSQL 实例的shipping
数据库中。
**CREATE TABLE customer_address (
ca_address_sk bigint,
ca_address_id char(16),
ca_street_number char(10),
ca_street_name char(60),
ca_street_type char(15),
ca_suite_number char(10),
ca_city varchar(60),
ca_county varchar(30),
ca_state char(2),
ca_zip char(10),
ca_country char(20),
ca_gmt_offset double precision,
ca_location_type char(20)
);**
像 Hive 和 Presto 一样,我们可以从命令行以编程方式或交互方式创建表;我更喜欢程序化的方法。使用下面的psql
命令,我们可以在shipping
数据库的public
模式中创建customer_address
表。
**psql -h ${POSTGRES_HOST} -p 5432 -d shipping -U presto \
-f sql/postgres_customer_address.sql**
现在,要将数据插入到新的 PostgreSQL 表中,运行下面的presto-cli
命令。
**# inserts 50,000 rows
presto-cli --execute """
INSERT INTO rds_postgresql.public.customer_address
SELECT * FROM tpcds.sf1.customer_address;
"""**
为了确认数据被正确导入,我们可以使用各种命令。
**-- Should be 50000 rows in table
psql -h ${POSTGRES_HOST} -p 5432 -d shipping -U presto \
-c "SELECT COUNT(*) FROM customer_address;"psql -h ${POSTGRES_HOST} -p 5432 -d shipping -U presto \
-c "SELECT * FROM customer_address LIMIT 5;"**
或者,您可以通过将sql/postgres_customer_address.sql
文件的内容复制并粘贴到psql
命令提示符来交互式地使用 PostgreSQL 客户端。要从psql
命令提示符与 PostgreSQL 交互,请使用以下命令。
**psql -h ${POSTGRES_HOST} -p 5432 -d shipping -U presto**
使用\dt
命令列出 PostgreSQL 表,使用\q
命令退出 PostgreSQL 客户端。现在,我们已经创建并配置了所有新的数据源!
与 Presto 交互
Presto 提供了一个监视和管理查询的 web 界面。该界面提供了对 Presto 集群和集群上运行的查询的仪表板式洞察。Presto UI 在使用公共 IPv4 地址或公共 IPv4 DNS 的端口8080
上可用。
有几种方法可以通过 PrestoDB 沙箱与 Presto 交互。这篇文章将展示如何使用 JDBC 连接和 Presto CLI 在 IDE 中对 Presto 执行特定查询。其他选项包括从 Java 和 Python 应用程序、 Tableau 或 Apache Spark/PySpark 中运行对 Presto 的查询。
下面,我们看到一个来自 JetBrains PyCharm 的针对 Presto 的查询,使用 Java 数据库连接(JDBC)连接。使用像 JetBrains 这样的 IDE 的优点是有一个单一的可视化界面,包括所有的项目文件、多个 JDBC 配置、输出结果,以及运行多个特定查询的能力。
下面,我们将看到一个使用 JDBC 连接字符串配置 Presto 数据源的示例,该字符串在 CloudFormation stack Outputs 选项卡中提供。
确保下载并使用最新的 Presto JDBC 驱动 JAR。
使用 JetBrains 的 ide,我们甚至可以限制数据源显示的数据库/模式。当我们配置了多个 Presto 目录,但是我们只对某些数据源感兴趣时,这是很有帮助的。
我们还可以使用 Presto CLI 运行查询,有三种不同的方式。我们可以将 SQL 语句传递给 Presto CLI,将包含 SQL 语句的文件传递给 Presto CLI,或者从 Presto CLI 进行交互工作。下面,我们看到一个从 Presto CLI 交互运行的查询。
当查询运行时,我们可以观察到实时的 Presto 查询统计信息(在我的终端中对用户不太友好)。
最后,查看查询结果。
联邦查询
演示中使用并包含在项目中的示例查询主要摘自学术文章Why You Should Run TPC-DS:A Workload Analysis,该文章在tpc.org网站上以 PDF 格式提供。我已经修改了 SQL 查询来处理 Presto。
在第一个示例中,我们将运行同一基本查询语句的三个版本。查询的版本 1 不是联合查询;它只查询一个数据源。第 2 版查询查询两个不同的数据源。最后,第 3 版查询查询三个不同的数据源。SQL 语句的三个版本都应该返回相同的结果— 93 行数据。
版本 1:单一数据源
查询语句的第一个版本sql/presto_query2.sql
不是联邦查询。查询的四个表(catalog_returns
、date_dim
、customer
和customer_address
)中的每一个都引用 TPC-DS 数据源,这是预装在 PrestoDB 沙箱中的。注意第 11–13 行和第 41–42 行的表引用都与tpcds.sf1
模式相关联。
我们将使用presto-cli
以非交互方式运行每个查询。我们将选择sf1
(比例因子为 1) tpcds
模式。根据 Presto ,比例因子(sf1
、sf10
、sf100
)中的每个单位对应一个千兆字节的数据。
**presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query2.sql \
--output-format ALIGNED \
--client-tags "presto_query2"**
下面,我们看到了presto-cli
中的查询结果。
下面,我们看到第一个查询在 Presto 的 web 界面中运行。
下面,我们在 Presto 的 web 界面中看到了第一个查询的详细结果。
版本 2:两个数据源
在查询语句的第二个版本sql/presto_query2_federated_v1.sql
中,两个表(catalog_returns
和date_dim
)引用了 TPC-DS 数据源。另外两个表(customer
和customer_address
)现在引用 Apache Hive Metastore 来获取它们在亚马逊 S3 的模式和底层数据。注意第 11 行和第 12 行上的表引用,而不是第 13 行、第 41 行和第 42 行。
再次使用presto-cli
运行查询。
**presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query2_federated_v1.sql \
--output-format ALIGNED \
--client-tags "presto_query2_federated_v1"**
下面,我们在 Presto 的 web 界面中看到了第二个查询的详细结果。
即使数据在两个独立的、物理上不同的数据源中,我们也可以很容易地对其进行查询,就好像它们都在同一个地方一样。
版本 3:三个数据源
在查询语句的第三个版本sql/presto_query2_federated_v2.sql
中,两个表(catalog_returns
和date_dim
)引用了 TPC-DS 数据源。其中一个表(hive.default.customer
)引用了 Apache Hive Metastore。底层数据在亚马逊 S3。第四个表(rds_postgresql.public.customer_address
)引用 PostgreSQL 数据库实例的新 RDS。注意第 11 行和第 12 行以及第 13 行和第 41 行的表格引用,与第 42 行相对。
同样,我们使用presto-cli
运行了查询。
**presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query2_federated_v2.sql \
--output-format ALIGNED \
--client-tags "presto_query2_federated_v2"**
下面,我们在 Presto 的 web 界面中看到第三个查询的详细结果。
同样,即使数据在三个独立的、物理上不同的数据源中,我们也可以很容易地对其进行查询,就好像它们都在同一个地方一样。
其他查询示例
该项目包含几个额外的查询语句,这些语句是我从Why You Should Run TPC-DS:A Workload Analysis中提取的,并修改了 Presto 和跨多个数据源的联邦工作。
**# non-federated
presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query1.sql \
--output-format ALIGNED \
--client-tags "presto_query1"# federated - two sources
presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query1_federated.sql \
--output-format ALIGNED \
--client-tags "presto_query1_federated" # non-federated
presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query4.sql \
--output-format ALIGNED \
--client-tags "presto_query4"# federated - three sources
presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query4_federated.sql \
--output-format ALIGNED \
--client-tags "presto_query4_federated" # non-federated
presto-cli \
--catalog tpcds \
--schema sf1 \
--file sql/presto_query5.sql \
--output-format ALIGNED \
--client-tags "presto_query5"**
结论
在这篇文章中,我们通过使用来自 AWS Marketplace 的 Ahana 的 PrestoDB 沙盒产品,对 Presto 有了更好的理解。我们学习了 Presto 如何查询数据所在的位置,包括 Apache Hive、Thrift、Kafka、Kudu 和 Cassandra、Elasticsearch、MongoDB 等。我们还了解了 Apache Hive 和 Apache Hive Metastore、Apache Parquet 文件格式,以及如何和为什么在亚马逊 S3 对 Hive 数据进行分区。最重要的是,我们学习了如何编写联合查询,将多个不同的数据源连接起来,而不将数据移动到一个单一的数据存储中。
本博客代表我自己的观点,而非我的雇主亚马逊网络服务公司的观点。
窥视黑匣子
内部 AI
更吸引人的部分依赖图如何在黑盒模型中与非技术涉众建立信任。
人们不相信他们不了解的东西。人工智能和机器学习算法是我们拥有的一些最强大的技术,但它们也是最容易被误解的。因此,数据科学家最重要的职责之一就是以易于理解的方式传达复杂的信息。
黑盒模型
也许对神经网络最大的误解之一是,我们不能直接看到产生结果的模型。我们可以看到我们的投入和产出,我们可以衡量结果,但我们并没有真正理解它们之间的关系。从实用性的角度来看,这是有问题的,因为像人类一样,关系的本质会随着时间而改变。人工智能今天对卡车的感知可能会反映出卡车明天的样子。
特斯拉赛博卡车。来源:迈克·马琳/Shutterstock.com
然而,大多数变化都不像特斯拉的网络卡车那样刺耳。如果我们看不到算法的内部,我们怎么知道算法正在跟上普通假设的逐渐变化呢?我们打开盒子。我们拥有的最好的工具之一是部分依赖图(PDP)。
部分相关图
Scikit-Learn 的创作者这样描述部分依赖情节:
部分相关性图(PDP)显示了目标响应和一组“目标”特征之间的相关性,忽略了所有其他特征(“补充”特征)的值。
换句话说,PDP 允许我们看到预测变量的变化如何影响目标变量的变化。下面是一个 PDP 的例子,显示了不同的房子特征对预测价格的影响。
来源: Scikit-Learn
从这些图中,我们可以看到,随着中值收入和房屋年龄的增加,预测价格往往会增加。然而,随着一个地区平均入住率的上升,预测价格会下降。底部的线条代表观察值的分布。
这些情节非常容易理解和创作。使用拟合的模型、数据集(仅限 X 要素)和输入要素列表,您可以在导入相关库后使用单行代码生成上述图:
import matplotlib.pyplot as plt
from sklearn.inspection import partial_dependence, plot_partial_dependenceplot_partial_dependence(model, X, features)
这些图对于几乎任何类型的回归模型都很好。然而,我发现在将 PDP 应用于分类任务时,非技术利益相关者有时很难解释结果。更重要的是,它们看起来并不特别吸引人。让我们打扮一下,并添加一些功能。
美化的 PDP
为了便于说明,我们将使用泰坦尼克号数据集。我们将使用 XGBoost 分类模型构建一个简单的模型,该模型尝试基于几个输入特征来识别幸存者。我们最感兴趣的是弄清楚我们的模型如何使用年龄作为存活率的预测指标(没有双关语)。
from xgboost import XGBClassifierdf = pd.read_csv('titanic.csv')
X = df[['Age', 'SibSp', 'Parch', 'Fare']]
y = df['Survived']model = XGBClassifier()
model.fit(X, y)fig = plt.figure(figsize(10, 9))
plot_partial_dependence(model, X, ['Age'], fig=fig)
plt.show()
正如我们所看到的,我们的模型已经确定,在所有其他因素相同的情况下,老年人更不可能存活。我们还可以看到大多数乘客年龄在 20 到 40 岁之间。
如果我们可以通过在同一个图表上绘制直方图来更清楚地了解年龄分布,这不是很好吗?将部分相关值显示为百分比怎么样?如果我们也能可视化决策边界,那不是很好吗?我们可以通过使用 partial _ dependence 方法获取部分依赖值并自己绘制结果来完成所有这些工作。幸运的是,我已经创建了一个函数来完成这项工作。
from sklearn.inspection import partial_dependence
上述函数将为单个输入变量生成一个 PDP,并允许输入轴标签和图表标题的目标名称。此外,它还提供了将 y 轴刻度显示为百分比、更改决策边界以及返回部分相关值以供进一步分析的选项。通过坚持使用标准设置并为目标传递一个名称,我们得到如下结果:
plot_pdp(model, X, 'Age', target='Survival')
有了这个,我们对年龄分布有了更丰富的了解。我们可以清楚地看到年龄跨越了决策界限。我们以一种让非技术利益相关者更容易阅读和理解的方式来标记轴。从这里开始,您可以试验这些选项,看看它们如何改变图表的显示,或者根据您的喜好修改代码。
如果没有别的,我会鼓励你想出新的方法来分享你的工作,以创造更多的非技术观众的参与。
更新:如果将上述代码与GradientBoostingRegressor或GradientBoostingClassifier一起使用,则需要将方法设置为“brute”
partial_dependence(model, X, ['Age'], method='brute')
身体和精神疾病的流行
在完全观想繁荣。
女性与男性以及年轻人与老年人——在 MIMIC-III 的约 3 万名重症监护患者的数据集上分析身体和精神疾病患病率的差异。
电子健康记录(EHRs)是医疗信息的宝库(点击阅读更多关于 EHRs 的信息)。在本帖中,我们将使用来自 MIMIC-III 的 EHRs 来分析疾病流行率在年龄和性别上的差异。
EHRs 中的数据通常以自由文本的形式提供,因此很难用于任何类型的统计分析。这意味着我们首先需要构建它。为此,我们将使用医学概念注释工具(MedCAT) 。MedCAT 用于从自由文本中提取和组织提及的医学概念(疾病、症状等)。一旦我们有了结构化格式的信息,我们就可以进行分析,并创建几个图表来展示疾病频率的差异。
这篇文章附有一个**Google Colab其中包含了完整的源代码。Colab 使用虚拟数据,模拟 MIMIC-III 中的表格(不公开提供)。**
准备数据
MIMIC-III 数据库的组织方式使得每个文档(EHR)都有一个ID
并且每个ID
都与一个患者相关联。对于每个病人,我们也有年龄和性别的信息。一名患者可以有 0 个或多个文档(临床记录)。如果你对如何组织和准备该项目和类似项目的 MIMIC-III 数据库感兴趣,请查看数据集分析和准备。
这里,我将假设您知道如何使用 MedCAT,并且所有相关的概念都已经提取出来,并且在 python 字典中可用,如下所示。如果你想学习如何使用 MedCAT,请查阅 MedCAT 教程。
**concept_location = {'concept_id': [<documents_with_concept>], ...}**
concept_location
—告诉我们每个概念 ID(疾病)在哪个文档(EHR)中找到。这本字典提供了足够的信息来联系疾病和病人,并分析年龄和性别的差异。
concept_id
—身体或精神疾病的唯一标识(在中为 CUI)。
documents_with_concept
—找到某个概念的所有文档 id(EHR)的列表。
在我们继续之前,我们需要生成几个映射(下面的代码块),考虑到数据集的格式,这样做的方式可以不同。如果你想用一些虚拟数据测试一切,看看附带的 Google Colab 。虚拟日期遵循 MIMIC-III 中数据的精确组织。我们需要的所有映射都可以使用 Colab 中所示的concept_location
来创建。
**# CUI (the disease identifier) to patientId
#that have that disease.
cui_pts = {}# PatientID to gender
pt2gender = {}# PatientID to Age group (find the definitions for age groups [here](https://www.researchgate.net/publication/232746130_Automated_Medical_Literature_Retrieval))
pt2agegroup = {}# PatientId to age (years)
pt2age = {}# PatientId to list of disease identifiers
pt2cuis = {}**
我已经为 MIMIC-III 数据集创建了映射,其方式与 Colab 中显示的方式完全相同。更进一步,我将假设上面的映射已经被计算过了。
现在,我们希望创建一个 CSV 文件,其中包含我们制作图表所需的所有数据,这些图表将显示每个疾病和组(年龄、性别)的患者分布情况。我们将组织 CSV,以便每行包含一种疾病及其所有必要信息:
- 患有该疾病的患者总数
- 患有该疾病的女性/男性患者人数
- 每个年龄组的患病人数
首先,让我们获得每个患者组的计数,如果我们想要计算患有给定疾病的患者的比例,这是必要的:
**pt_total = len(pt2age)pt_male = len([x for x in pt2gender if pt2gender[x] == "M"])pt_female = len([x for x in pt2gender if pt2gender[x] == "F"])pt_adl = len([x for x in pt2agegroup if pt2agegroup[x] == "Adolescent"])pt_yadult = len([x for x in pt2agegroup if pt2agegroup[x] == "Young Adult"])pt_adult = len([x for x in pt2agegroup if pt2agegroup[x] == "Adult"])pt_maged = len([x for x in pt2agegroup if pt2agegroup[x] == "Middle Aged"])pt_aged = len([x for x in pt2agegroup if pt2agegroup[x] == "Aged"])**
现在,我们可以创建 CSV 的头部。唯一未知的一栏是TUI
,它决定了是身体疾病还是精神疾病(摘自 UMLS )。
**csv = [['disease', 'cui', 'tui', 'total', 'male', 'female', 'Adolescent', 'Young Adult', 'Adult', 'Middle Aged', 'Aged']]**
对于每种疾病,我们知道所有患有这种疾病的患者(从cui_pts
图中),我们可以用它来计算每一组患有某种疾病的百分比:
**for cui in cui_pts:
d = cdb.cui2pretty_name[cui]
t = (len(cui_subjects[cui]) / pt_total) * 100
m = (len([x for x in cui_subjects[cui] if
subject2gender[x] == 'M']) / pt_male) * 100
f = (len([x for x in cui_subjects[cui] if
subject2gender[x] == 'F']) / pt_female) * 100
adl = (len([x for x in cui_subjects[cui] if
subject2agegroup[x] == 'Adolescent']) / pt_adl) * 100
yadult = (len([x for x in cui_subjects[cui] if
subject2agegroup[x] == 'Young Adult'])/pt_yadult)*100
adult = (len([x for x in cui_subjects[cui] if
subject2agegroup[x] == 'Adult']) / pt_adult) * 100
maged = (len([x for x in cui_subjects[cui] if
subject2agegroup[x] == 'Middle Aged']) / pt_maged)*100
aged = (len([x for x in cui_subjects[cui] if
subject2agegroup[x] == 'Aged']) / pt_aged) * 100# We get the TUI from medcat
tui = cdb.cui2tui[cui]
# Add to the array that will become the CSV
dt.append([d, cui, tui, t, m, f, adl, yadult, adult, maged, aged]**
现在,我们从数据中创建了一个熊猫数据框架,并根据每种疾病的患者总数对其进行排序:
**df = pd.DataFrame(dt[1:], columns=dt[0])# Sort by total
df = df.sort_values(by=['total'], ascending=True)**
身体和精神疾病
为了绘制前 30 种身体和精神疾病,我们现在可以简单地使用熊猫的内置绘图功能:
**# Physical: T047
# Mental: T048# Subset the data, to a certain type and number
_data = df[df.tui=='T048'].iloc[-30:]ax = _data.plot(y=['total'], x="disease", kind="barh")# Hide the legend and add labels
_ = ax.legend().set_visible(False)
_ = ax.set(xlim=(0, 17), ylabel="Disease Name", xlabel="Percentage of patients with disease")plt.show()**
********
图一。左边是身体疾病的分布,右边是精神疾病的分布。
请注意:图 1 中的情节和本帖中的所有其他情节都是实验性的,主要用于展示 MedCAT。
女性 vs 男性
我们创建的数据框架包含了制作大量绘图所需的所有数据。如果我们现在需要精神和身体疾病的女性和男性患者:
**# Subset the data
_data = df[df.tui=='T048'].iloc[-30:]
ax = _data.plot(y=['male', 'female'], x="disease", kind="barh")# Set the legend and labels
ax.legend(loc='lower right')
_ = ax.set(xlim=(0, 17), ylabel="Disease Name", xlabel="Percentage of patients with disease", )plt.show()**
********
图二。女性和男性患者的身体疾病分布在左侧,精神疾病分布在右侧。
在这种情况下,我们也可以证明我们的结果得到了医学领域已发表的工作的支持。图表显示抑郁症在女性患者中比在男性患者中更常见(由论文支持)。或者说物质滥用(主要是酒精)在男性患者中要常见得多(由论文支持)。****
这表明我们的结果不是随机的,我们使用的整个管道工作得相当好。
年轻人对老年人
最后,我们将分析基于年龄的差异。由于无法按年份显示疾病,我们将患者分组(取自此处的):
- 青少年 : 13 至 18 岁
- 青壮年 : 19 至 24 岁
- 成年人 : 25 至 44 岁
- 中年 : 45 至 64 岁
- 年龄 : 64 岁以上
图 3。身体疾病的年龄分布。
正如预期的那样,对于大多数疾病来说,随着年龄的增长,患病人数的百分比会增加。
图 4。精神疾病的年龄分布。
精神疾病在老年人中也更普遍,这也是意料之中的。唯一的例外是亢奋行为和冲动性格。一个有趣的概念是酒精滥用,成年人达到高峰,青少年比年轻人高。****
结束了
我们探索疾病频率和年龄/性别之间关系的项目到此结束。相同的实验(以及更多在分析 EHRs 中介绍的)可以很容易地在真实世界的医院数据上进行和重复,并有可能用于改善医疗保健。
感谢您的阅读。
预防和应对疾病爆发
群落发现及其在系统发育网络中的应用
为了便于结果的分析和可视化,实现了一个 web 应用程序— Phyl。
随着高通量测序方法的出现,需要新的方法来可视化和分析越来越多的数据。虽然一些软件已经存在,但是它们不能很好地扩展或者需要高级技能才能在系统发育学中有用。
该项目的目的是实现三种社区发现算法——Louvain、Infomap 和分层标签传播(LLP);使用两个合成网络——格文-纽曼(GN)和兰奇希尼蒂-福图纳托-拉迪奇(LFR)对它们进行基准测试;在真实的网络中测试它们,特别是来自金黄色葡萄球菌 MLST 数据集的一个;比较可视化框架——cyto scape . js 和 D3.js,最后,把它们都放到网上(【mscthesis.herokuapp.com】T4)。
鲁汶、信息地图和 LLP 都是用 JavaScript 实现的。除非另有说明,以下结论适用于 GN 和 LFR。就速度而言,卢万胜过所有其他人。考虑到准确性,在有明确社区的网络中,卢万是最准确的。对于更高的混合,LLP 是最好的。与弱混合相反,在高度混合的 GN 中,提高分辨率参数是有利的。在 LFR,较高的分辨率会降低检测的准确性,与混合参数无关。平均节点度的增加提高了划分的准确性,并且最小化了偶然检测的可能性。使用项目中开发的算法或 LFR 实现来生成具有更高混合度或平均度的 GN 在计算上更密集。在金黄色葡萄球菌网络中,鲁汶在检测直接由共同祖先进化而来的 7 组菌株的聚类中是最快最准确的。
介绍
根据经济学家的说法,数据已经成为世界上最重要的资源[1]。越来越多的信息迫切需要新的存储、分析和可视化方法。
真实系统中不同元素之间的相互作用可以看作是网络[2]。从它们的拓扑或连接模式中,可以提取功能知识。虽然经验分析在小型网络中很有用,但对于大数据来说,使用算法来揭示其属性是不可或缺的。因此,被称为图形的数学模型被用来简化它们的表示。图论提供了分析它们的所有工具。这种表示意味着必须获得网络的所有元素及其关系。一般来说,分析越接近人群,收集数据就越困难。两个主要原因是缺乏非侵入性的获取方法或隐私问题[3]。事实上,在医疗领域,关于患者数据保护的立法变得越来越严格[4]。一方面,确保医学数据的伦理使用是基本的,另一方面,它可能构成调查许多病理原因的障碍。接下来,讨论将集中在传染病这个特定的话题上。
正如世界卫生组织所述,耐药性(AMR)是 2019 年需要关注的十大主要威胁之一[5]。在过去的几年里,由于使用了旨在治疗由病原微生物引起的疾病的药物,AMR 得到了加强。根据生物体的不同,它被细分为抗病毒(病毒)、抗生素(细菌)、抗真菌(真菌)或抗寄生虫(寄生虫)抗性。尽管耐药性是一个自然进化过程,随着时间的推移,最有能力的菌株会被选择出来,但抗菌药物的使用频率,甚至误用,正在加速几种微生物物种的耐药率的增长[6]。这不仅在人群中特别相关,而且在农场中也特别相关,在农场中,牛被给予预防性剂量的抗生素作为生长助剂。此外,人们在世界各地旅行的频率也促进了耐药微生物的传播。由于所有这些以及传染病的高死亡率(图 1),授权中央卫生当局使用可扩展且易于使用的工具以允许他们在短时间内进行干预是至关重要的[7]。
图 1 感染仍然是死亡的主要原因之一(*)。世卫组织在 2000 年至 2015 年间从世卫组织成员国的 90,000 多人中收集的数据。改编自[8]。
近年来开发的下一代测序方法为我们提供了大量的微生物数据[9]。事实上,由于全基因组测序方法的高分辨能力,已经有可能推断菌株之间更精确的进化关系。因此,具有更高数量基因的等位基因谱不断增加,并且由此可以评估描述菌株间进化距离的更完整的系统发育树或网络。所有这些在疾病预防的多个阶段都是有用的:疫苗设计、评估已测序菌株的致病性、检测疾病爆发和控制感染。以及诊断,增强混合感染的检测(由多种遗传上不同的生物体引起)[10]。
一般来说,这个项目的目标是从系统发育网络的拓扑结构中提取功能知识。例如,能够根据网络中其他生物的相似性和特征,推断出对抗生素的抗性等特性。与随机网络相反,真实网络呈现不对称的边分布。根据这种不对称性,可以识别出节点群。根据定义,社区是网络中边密度较高的区域。尽管如此,这个定义还不够严格,不足以在图中确定它们。这就提出了下面的问题:如果不清楚我们在找什么,我们怎么能发现社区呢?为了回答这个问题,社区发现理论在过去几年中得到了发展。尽管在检测的准确性和速度方面已经有了相当大的进步,但是在不同的网络中,一些算法比其他算法表现得更好。
目标
该项目的目标是用 JavaScript 实现三种社区发现算法——Louvain、Infomap 和分层标签传播(LLP);在准确性和速度方面,与两个合成网络——格文-纽曼(GN)和兰奇希尼蒂-福图纳托-拉迪奇(LFR)网络进行比较;使用亚马逊、扎卡里空手道俱乐部和葡萄球菌 金黄色葡萄球菌多位点序列分型(MLST)单位点变异(SLV)网络进行额外测试;比较不同可视化框架的工具和对大数据的稳健性——D3 . js(使用 SVG 和 Canvas 元素)和 Cytoscape.js,并创建一个 web 应用程序来实现网络可视化(在运行社区发现算法之前和之后),并绘制从基准测试中获得的所有数据。
文章大纲
在方法上,介绍了已实现的社区发现算法、已实现的基准工具、真实测试数据和数据可视化库。结果表明,社区发现算法在准确性和速度方面进行了分析。基准网络的生成速度是基于网络的不同属性来确定的。比较了不同的 web 可视化框架。并且使用不同的算法和输入参数来划分金黄色葡萄球菌 MLST SLV 网络。最后,对目标是否实现进行了回顾性分析,详细说明了 INSaFLU web 应用中的主要困难和未来需要完成的工作,并对已经完成的工作提出了一系列前瞻性改进建议。
方法学
社区发现
鲁汶算法
该算法在项目中实施,分为两个阶段:模块化优化和社区聚集[11]。合在一起,它们被认为是 1 次通过。第一步完成后,接着是第二步。两者都被执行,直到网络中不再有变化并且实现最大模块化。
模块化优化——该算法将随机排序网络中的所有节点,以便一个接一个地将它们移除并插入不同的社区 C 。这将持续到模块性(输入参数)没有显著增加被证实:
设中的*∑为 C 、 ∑tot 中所有链接到 C 、 ki 中节点的权重之和 i 、 *ki、中所有链接到 i 的权重之和
社区聚合——完成第一步后,属于同一个社区的所有节点合并成一个巨型节点。连接巨型节点的链接是先前连接来自相同不同社区的节点的链接的总和。该步骤还生成自循环,该自循环是在折叠成一个节点之前给定团体内所有链接的总和。
信息地图算法
与 Louvain 类似,也实现了 Infomap 算法。它使用不同的质量函数(最小描述长度[12])来划分网络:
设 qm 是模块 m 的退出概率,而 pα 是相对权重 wα ,其计算方法是将连接到α的边的总权重除以图中所有链接的总权重的两倍。
分层标签传播算法
LLP 算法的发展是基于标签传播(LP) [13]。后者首先为网络中的每个节点分配一个不同的社区,然后,它根据近邻节点中的主导社区迭代地修改相应的社区。在每次迭代中,对所有节点执行这个步骤,按照之前的随机顺序。
LLP 不仅考虑了邻居中的节点,还考虑了剩余网络中的节点[14]。迭代过程是一样的,区别在于它优化的值。在这种情况下,分配的社区是最大化:
被 ki 在给定节点的邻域中带有标签 λi 的节点的数量和 νi 在整个图中以相同方式标记的总数。
这种方法也已实现,通过其在整个网络中的常见存在,缓冲属于某个社区(在节点的邻域中)的节点的数量。
表 1 社区发现算法的时间复杂度。
基准
不同的社区发现算法以不同的方式划分同一个图。因此,了解哪些算法是最重要的。
获得这种洞察力的准确方法是对社区结构已知的网络进行划分。下一小节将介绍 GN 和 LFR 基准网络,以及 NMI,后者用于估计原始分区和检测到的分区之间的相似性。
社区发现算法不仅在准确性方面进行了测试,而且在速度方面也进行了测试。所有基准测试都重复了 10 次。平均值和相应的 95%置信区间包含在结果中。
格文-纽曼网络
在 GN 网络中,128 个节点被分成 4 组,每组正好有 32 个节点[16]。然后,由用户选择的混合参数定义来自不同社区的节点被连接的概率。该概率由下式给出:
每个节点都与另外 16 个节点相连(图 2)。由此实现的算法不仅允许混合参数变化,而且允许平均程度变化。这导致使用这些网络的基准测试数量增加。
图 2 GN 合成网络。恒定的节点数(N),可变的混合参数(μ)和平均节点度(k)。用 D3.js 和 SVG 表示。
兰奇内蒂-福图纳托-拉迪奇网络
LFR 基准网络(图 3) [17,16]试图更好地接近真实网络。这意味着他们不仅考虑具有系数ζ (5)的社区大小的幂律分布,而且考虑具有系数γ (6)的节点度的幂律分布:
图 3 LFR 合成网络。改变混合参数(μ)、节点数(N)和平均节点度(k),同时保持其余 2 个变量不变。用 D3.js 和 SVG 表示。
归一化互信息
使用归一化互信息(NMI) [18]测试社区发现算法的聚类质量。在项目中用 JavaScript 开发了一种算法,并在几个网络中进行了测试。它接收长度相同的 2 个数组的输入,并返回相应的 NMI:
NMI 依赖于互信息 I ,条件熵 H(Y|C) 以及带标签的 H(Y) 和聚类集 H© 的熵。
真实测试数据
使用实现的算法执行的测试数量越多,它们对于最大数量的网络正常工作的确定性就越高。这样,断开的网络(从亚马逊网络取样)、小型网络(扎卡里的空手道俱乐部)和系统网络(金黄色葡萄球菌MLST·SLV)也被额外考虑。然而,没有使用这些进行基准测试。
亚马逊网络
这个网络是抓取亚马逊网站后获得的[19]。每个节点代表商店中的一个给定产品,连接的节点对意味着它们经常一起被购买。原始网络包含 334 863 个节点和 925 872 条链路。测试中只使用了各个节点的前 5 000 条链路(图 4)。
图 4 从 5 000 个链接图中抽取的亚马逊产品联合购买网络样本。用 D3.js 和 SVG 表示。
扎卡里的空手道俱乐部网络
这是一个代表来自大学空手道俱乐部的 34 个人之间的 77 种关系的社交网络[20]。
节点 1 是讲师,34 是总裁(图 5)。深蓝色和浅蓝色的节点代表俱乐部内部的分裂,这是由于他们之间的冲突造成的。每个链接连接两个成员,在他们分开之前,他们经常在俱乐部外面见面。
图 5 扎卡里的空手道俱乐部网络。用 D3.js 和 SVG 表示。
葡萄球菌 金黄色葡萄球菌
为了测试群落发现算法在系统发育网络中的应用,使用了 MLST 的金黄色葡萄球菌等位基因谱数据库(图 6)【21】。MLST 概况包含关于七个基因座 ( arcC 、 aroE 、 glpF 、 gmk 、 pta 、 tpi 和 yqiL )的信息,并且在下载时,它有 5199 个 MLST 概况(ST)。通过将每个 ST 链接到其所有 slv 来创建图,并且最大的部分用于社区发现算法的基准。选择这个测试网络是基于这样一个事实,即它可以模拟与使用 cgMLST 或 wgMLST 配置文件创建的模式相似的模式,这些模式具有比 MLST 多得多的基因座。
图 6金黄色葡萄球菌 MLST SLV 网。用 D3.js 和 Canvas 表示。
可视化框架
在可视化应用程序中,用户可以选择使用 D3 或 Cytoscape JavaScript 库来可视化每个网络。对于第一个框架,有可能通过画布或 SVG 元素来可视化数据。所有的网络都是使用力引导的实现来绘制的。这种模式要求开发人员设置重力、排斥力或弹簧常数等功能,这些功能将影响网络的显示方式。根据这些参数,应该减少节点和链接重叠,以便获得图形的增强视图(图 7)。
图 7 扎卡里的空手道俱乐部网络用 D3.js Canvas(左上)、D3.js SVG(右上)和 Cytoscape.js(下图)表示。
关于基准绘图,使用了 D3.js。它在视觉表现、性能和网上可用实例数量方面的灵活性决定了这一选择。
结果
网络应用
为了便于分析和可视化之前算法执行后的结果,实现了一个 web 应用程序(mscthesis.herokuapp.com)。
它的后端是使用 Node.js 设计的,运行在一个 Heroku 服务器上,该服务器链接到一个 GitHub 存储库(github.com/warcraft12321/Thesis),其中包含所有与项目相关的实现和文档。使用芝诺多(【zenodo.org/badge/latestdoi/162063699】)将数字对象标识符(DOI)归属于这个存储库。在 Docker Hub(cloud . Docker . com/u/war craft 12321/repository/Docker/war craft 12321/thesis)可以获得该应用的图片。以下操作在云中严格执行:
- 生成 GN 和 LFR 基准网络。后者是使用 C++实现中的二进制文件生成的[22]。这是在从 NPM 导入一个模拟服务器命令行的包之后才有可能实现的;
- 真实的测试网络在云中对数据进行了适当的处理,这样数据就可以在前端发送和显示;
- 一旦鲁汶、信息图和 LLP 算法的执行不在用户的浏览器中进行,这可能会引起一些隐私问题。虽然,在用户端运行 Infomap 而仍然保持 web 应用程序在所有设备上的功能是不可行的;
- 应用程序和 NPM 之间的通信是在服务器中建立的,因此可以在应用程序中导入和绘制上传到 NPM 的每个包的统计数据;
- 最后,在云中运行算法后获得所有基准数据。
前端是静态和动态的。静态版本使用 HTML、CSS 和 JavaScript。动态执行由 Node.js 管理,它运行在服务器端(图 8)。
图 8 Web 应用。
社区发现算法
就准确性而言,对于弱混合网络(μ < 0.4),Louvain 优于所有其他网络。其次是对于μ < 0.3 的 Infomap,LP 和 LLP (γ = 0.5)或对于 0.3 ≤ μ ≤ 0.4 的 Infomap,LLP 和 LP (γ = 0.5)。通过增加μ,能够基于整个网络的全景来检测社区的 LLP 对于μ ≥ 0.5 始终是最有效的。比较 Louvain 和 Infomap 在μ ≥ 0.5 时的表现是差不多的。基于 95%置信区间,不可能说一个比另一个表现得更好。要强调的另一点是,Louvain 和 Infomap 非常敏感,因此,在μ ≈ 0.5 的网络中检测社区是不可靠的。LLP 和 LP 没有呈现出任何社区检测受到急剧影响的特定值。这些结论对 GN 和 LFR 网络都有效(图 9 和图 10)。
图 9 根据 GN 网络中的混合参数,每个算法的聚类质量:Louvain、Infomap、LP 和 LLP,其余特性如图 2 所示。
图 10 根据 LFR 网络中的混合参数,每个算法的聚类质量:Louvain、Infomap、LP 和 LLP,其余特性如图 3 所示。
由于只考虑了 LLP 的两个伽马值,因此需要进行更精确的分析,以检查是否有其他值可以获得更好的结果。
在 GN 网络(图 11)的情况下,可以在混合较少的网络中以更高的准确度检测社区。每当μ < 0.5. When μ ≥ 0.5, the NMI between the detected partition and the one in the original network is higher as long as we keep increasing γ. Another point to highlight is the possibility of detecting communities with higher precision in networks with higher μ using appropriate γ than in others with lower μ but with a non-optimal γ. This is valid for networks with μ >为 0.5 时,我们增加γ,性能就会下降(图 12)。
图 11 运行 LLP 后的 GN 网络(图 2)。当γ更接近 0 时,社区少、大、稀的网络粗结构凸显。随着γ的增长,细晶粒结构被揭开。社区变得越来越小,越来越密集。用 D3.js 和 SVG 表示。
图 12 LLP 算法在分辨率参数方面的精确度。对 GN 网络中的混合参数 0–1(蓝色-棕色)进行分析,其余属性如图 2 所示。
在 LFR 网络中,增加 gamma 总是会导致 NMI 下降(图 13)。
图 13 LLP 算法在分辨率参数方面的精确度。对 LFR 网络中的混合参数 0–1(蓝色-棕色)进行分析,其余属性如图 3 所示。
测试平均节点度的影响,以检查它是否影响每个算法识别社区的能力。
在 GN 网络的情况下,对于μ ≤ 0.5,平均度数的增加增强了卢万、Infomap、LP 和 LLP 区分社区的能力。对于μ > 0.5,NMI 聚类质量较低,因为平均节点度较高。假设不期望算法识别μ ≈ 1 的原始社区,这表明较高的平均度倾向于减少偶然的检测。这种观察可以应用于 GN 和 LFR 网络(图 14)。
图 14 LLP 算法在混合参数方面的精度。平均节点度为 15,20 和 25 的 GN 网络。其余的属性类似于图 2 中的属性。
在速度方面,Louvain 算法的表现明显优于其他任何算法。有助于其效率的一个重要属性是,它仅在模块化优化阶段(1)的每次迭代中计算模块化变化。在 Infomap 中,最小描述长度在优化步骤(2)的每次迭代结束时重新计算。由于这一计算密集型步骤,Infomap 在基准测试中表现最差。根据表 1,预计 Infomap 和所有其他文件之间的执行时间差异是对数的。由于实施步骤效率低下,这一目标未能实现。比较 LLP 和 LP,最快的是需要优化最简单方程的那个——LP(图 15)。尽管时间复杂度看起来是相同的,如表 1 中所预测的。同样,使用相同的表,预计 LLP 和 LP 的执行速度与鲁汶一样快。图 15 中观察到的微小差异可以用 LLP/线性规划实施中的一些非优化步骤来解释。
图 15 卢万、Infomap、LLP 和 LP 根据 LFR 网络的规模完成分析所用的时间。其余的属性类似于图 3 中的属性。
基准网络算法
之前生成了 GN 和 LFR 网络,因此可以执行之前的测试。在项目中实现了创建前者的算法。生成这种网络所需的时间随着平均节点度和混合参数呈指数增长(图 17)。在普通计算机中,对于高于 20 度的平均度数,执行它变得不可行。它的性能与 Fortunato 的实现进行了比较,Fortunato 的实现高度可扩展,允许在更短的时间内生成具有更多链路的网络(图 16)。已经证实,随着混合参数逐渐增加,网络生成时间近似线性增加,与项目中实施的网络相反。就像以前一样,生成具有更多边的网络需要额外的计算能力。
图 16 根据混合参数生成 GN 网络(使用 LFR 实现)所需的时间。在平均节点度为 15、20 和 25 的网络中进行的分析。其余的属性类似于图 2 中的属性。
图 17 根据混合参数生成 GN 网络(使用项目实施)所需的时间。在平均节点度为 15 和 20 的网络中进行的分析。其余的属性类似于图 2 中的属性。
其他测试在 LFR 网络中进行。针对网络的节点数量和混合参数测试了执行时间。在这两种情况下,验证了近似线性关系(图 18 和图 19)。在 GN 网络中,一旦每个社区和社区的元素数量固定,就不考虑节点的数量。尽管只分析了混合参数和节点数量,用户仍然可以选择调整节点最大程度、节点程度分布和社区大小分布的指数、每个社区的最大/最小节点数量、每个社区的重叠节点数量(在项目中不考虑覆盖)以及重叠节点的成员数量[22]。
图 18 根据节点数量生成 LFR 网络所需的时间。在平均节点度为 15、20 和 25 的网络中进行的分析。其余的属性类似于图 3 中的属性。
图 19 根据混合参数生成 LFR 网络所需的时间。在平均节点度为 15、20 和 25 的网络中进行的分析。其余的属性类似于图 3 中的属性。
每一个社区的发现,基准网络生成器,准确性措施和相关算法都在 NPM(表 2)。这样,它们可以用于任何特定于用户的应用程序。
表 2 项目中实现的算法在 NPM 可用。
可视化框架
D3.js (SVG)允许用户可视化 web 应用程序中使用的所有网络,并以可能的最高分辨率表示基准图(受用户设备中像素密度的限制)。从性能上来说,是第二快的。
D3.js (Canvas)是分析过的框架中最快的。然而,与之前的相比,网络中的节点和链路失去了分辨率。
当网络分析及其表示一起执行时,Cytoscape.js 是可取的。有相当多的算法,包括社区发现算法,可以促进图的研究。工作实例的缺乏和网络数据处理的缓慢使得 Cytoscape.js 的效率较低。金黄色葡萄球菌 SLV 网络和其他具有相似或更多节点/边的网络的表示溢出了 web 应用程序。
金黄色葡萄球菌
在从金黄色葡萄球菌的 MLST 数据集获得的 SLV 图的最大组成部分中执行了卢万、信息图和 LLP 算法。
使用三个模块性变化阈值:0.1、0.02 和 0.01 来执行 Louvain 算法。如简介中所预测的,可以验证快速实现高模块化分区(需要 2 次通过才能获得最终结果)。这个特性使得基于模块的算法速度更快。
使用信息图算法,考虑了几个最小描述长度阈值:0.1、0.02 和 0.01。对于所有值,从同一祖先传下来的每一个小的密集节点组都被分离到不同的社区中。因此,没有进一步审议。
LLP 算法考虑分辨率参数的三个值:0、0.5 和 1。与 Infomap 类似,它将网络划分为几个小而密集的社区。
根据 Louvain、Infomap 和 LLP 对 GN network 的基准分析(图 9 和图 10 ),可以预测,一旦 Louvain 在具有明确定义的社区的网络中运行,它将比其他网络表现得更好。在调整了 Louvain 的停止参数之后,使得所识别的社区是最相关的——来自属于同一社区的同一祖先的密集连接区域的节点,获得了最终的分区(图 20)。值得注意的是,一些类似于用正方形突出显示的聚类被算法细分。一种解释是不同亚群的菌株之间存在高重组率。
通过将生成的元数据文件上传到 PHYLOViZ Online,其中的节点被相应地标记为推断的群落,可以观察到具有相同群落的原始系统发生树。
图 20 在金黄色葡萄球菌 MLST SLV 网络的克隆复合体 0(如 PHYLOViZ 2 中所标识的)上运行卢万算法(考虑模块性变化的不同阈值)后获得的分区。用 D3.js 和 Canvas 表示。
结论
鲁汶、信息地图和 LLP 算法都是用 JavaScript 实现的。除非另有说明,以下结论适用于 GN 和 LFR 网络。就速度而言,卢万胜过所有其他人。就准确性而言,在有明确社区的网络中,卢万是最准确的。对于更高的混合,LLP 是最好的。与弱混合相反,在高度混合的 GN 网络中,增加分辨率参数是有利的。在 LFR 网络中,较高的分辨率降低了检测的准确性,而与混合参数无关。平均节点度的增加提高了分割精度,表明偶然检测被最小化。使用项目中开发的算法或 LFR 实现来生成具有更高混合度或平均度的 GN 网络在计算上更加密集。在金黄色葡萄球菌 MLST SLV 网络中,鲁汶在检测由同一个祖先进化而来的 7 组菌株的聚类中是最快最准确的。模块化变化阈值越低,检测越好。
除了三种社区发现算法之外,GN 基准网络生成器、NMI 和汉明距离算法也在 NPM 推出。web 应用程序托管在 Heroku 云平台中。可以从 Docker Hub 下载包含在本地机器上运行应用程序所需的所有模块的映像。GitHub 提供了该项目的所有实现和完整路线图。使用 Zenodo 将数字对象标识符(DOI)归属于这个存储库。
遇到的主要困难之一是在用户端运行社区发现算法。这个功能不会引起太多的隐私问题。必须在服务器上运行算法,这样应用程序才不会崩溃。在 Infomap 实现中,不可能达到其时间复杂度所预测的计算效率。拥有超过 10 000 个节点的网络的在线可视化是不可行的,即使使用表现出最佳性能的框架— D3.js(使用 Canvas 元素)。
在接下来的几个月里,这个项目中开发的一些工具将在 INSaFLU 中实施。用于显示系统进化树的新可视化框架,以及上传的元数据,将增强流感毒株进化路径的可追溯性。随时间动态变化的地理地图将允许用户精确获取每个样本的时间和位置,并在关键季节加强流感监测。交互式直方图显示了从流感病毒样品中获得的属于共有序列的每个核苷酸,突出显示了 SNPs,以及它们在指定毒株表型中的作用。新的流感监测动态报告将允许以最小化的方式可视化增加的数据量。
作为未来的改进,实现的算法可以在内存和速度方面进行优化。在更高效的实现中,网络和图表可视化可以运行得更快。之前的更新将使 web 应用程序加载更快,运行更流畅。用一组更具代表性的网络(涵盖更广泛的拓扑属性)对社区发现算法进行基准测试,将有可能以更高的确定性来推断任何社区发现算法是否在系统发育网络中始终表现更好。将项目中开发的社区发现功能整合到系统发育/面向监测的分析网络应用程序中,如 PHYLOViZ Online 或 INSaFLU,将是有益的。
论文 | 扩展摘要 | GitHub 知识库
参考
[1]“世界上最有价值的资源不再是石油,而是数据”《经济学人》,2017 年 5 月 6 日。【在线】。可用:https://www . economist . com/leaders/2017/05/06/the-worlds-most-valued-resource-is-again-oil-but-data。【2019 年 5 月 28 日获取】。
[2] A.-L. Barabási,《网络科学书》,[在线]。可用:http://networksciencebook.com。【2019 年 5 月 15 日获取】。
[3] H. G. S. Patil、A. N. Babu 和 P. S. Ramkumar,“生物医疗技术中的非侵入式数据采集和测量:概述”,载于通过技术集成最大化医疗服务提供和管理,IGI 全球,2016 年。
[4]“健康”,欧洲数据保护主管,[在线]。可用:https://edps . Europa . eu/data-protection/our-work/subjects/health _ en。【2019 年 6 月 9 日获取】。
[5]《2019 年全球健康十大威胁》,世界卫生组织,[在线]。可用:https://www . who . int/emergencies/ten-threats-to-global-health-in-2019。【2019 年 6 月 4 日获取】。
[6]“抗生素耐药性”,世界卫生组织,2018 年 2 月 15 日。【在线】。可用:https://www . who . int/en/news-room/fact-sheets/detail/antimicrobial-resistance。【2019 年 5 月 29 日访问】。
[7] Z. A. Memish、S. Venkatesh 和 A. M. Shibl,“旅行对抗生素耐药性国际传播的影响”,《国际抗微生物剂杂志》,,第 21 卷,第 2 期,第 135-142 页,2003 年。
[8]“全球十大死亡原因”,[在线]。可用:【https://www.theatlas.com/charts/HkLaDreuW. 【2019 年 5 月 12 日访问】。
[9]b . Ribeiro-gon alves,A. P. Francisco,C. Vaz,M. Ramirez 和 j . a . carri co,“PHYLOViZ Online:基于网络的可视化、系统发育推断、分析和共享最小生成树的工具”,*核酸研究,*第 44 卷,第 1 期,第 246–251 页,2016 年。
[10] Y. Motro 和 J. Moran-Gilad,“下一代测序在临床细菌学中的应用”,*生物分子检测和定量,*第 14 卷,第 1–6 页,2017。
[11] V. D. Blondel,J.-L. Guillaume,R. Lambiotte 和 E. Lefebvre,“大型网络中社区的快速展开”, *J. Stat .机甲战士。(2008) P10008,*2008 年第 12 页。
[12] M. Rosvall、D. Axelsson 和 C. T .博格斯特伦,“地图方程”,*《欧洲物理杂志专题》,*第 178 卷,第 1 期,第 13-23 页,2009 年。
[13] N. Raghavan,R. Albert 和 s .鸠摩罗王,“检测大规模网络中社区结构的近线性时间算法”,*物理评论 E 25th 周年里程碑,*第 76 卷第 3 期,2007 年。
[14] P. Boldi,M. Rosa,M. Santini 和 S. Vigna,“分层标签传播:用于压缩社交网络的多分辨率无坐标排序”,载于 WWW '11 第 20 届国际万维网会议论文集,2011 年。
[15]l . subelj,“聚类的标签传播”,载于网络聚类和块建模的进展,纽约,威利,2018 年。
[16] M .格文和 M. E. J .纽曼,“社会和生物网络中的社区结构”,*《美国国家科学院院刊》,*第 99 卷,第 12 期,第 7821-7826 页,2002 年。
[17] A. Lancichinetti,S. Fortunato 和 F. Radicchi,“测试社区检测算法的基准图”, *Physical review。统计、非线性和软物质物理学。,*第 78 卷,2008 年第 4 期。
[18] A. Lancichinetti,S. Fortunato 和 J. Kertesz,“检测复杂网络的重叠和分层社区结构”,*新物理学杂志,*第 11 卷,2009 年。
[19] J. Yang 和 J. Leskovec,“基于地面事实的网络社区定义和评估”,载于 2012 年 IEEE 数据挖掘国际会议(ICDM) 的会议录,2012 年。
[20] W. Zachary,“小群体中冲突和裂变的信息流模型”,*《人类学研究杂志》,*第 33 卷,1976 年。
[21]“金黄色葡萄球菌 MLST 数据库”,公共公报,2019 年 6 月 5 日。【在线】。可用:【https://pubmlst.org/saureus/. 【2019 年 6 月 5 日接入】。
[22]a . lanchi netti 和 S. Fortunato,“在具有重叠社区的有向和加权图上测试社区检测算法的基准”, *Physical review。统计、非线性和软物质物理学。,*2009 年第 80 卷。
防止数据帧的死亡
数据帧系列
数据框架正在失去统计计算和机器学习的根基
数据框架产生于特定的需求,但是因为如此多不同的系统现在都自称为数据框架,这个术语几乎没有任何意义。为了保存数据框架,我们在最近的预印本[2]中基于原始数据模型对定义进行了形式化。
在我们进入细节之前,我想先概述一些我将在下面回答的问题:
- 什么是数据帧,它来自哪里?
- 数据帧和表格有什么不同?矩阵?
- 数据框架系统的爆炸是如何杀死数据框架的?
- 用户为什么要关心?
既不是表格也不是矩阵
最早的“dataframe”,最初是“data frame”,出现在贝尔实验室的 S 编程语言中。“数据框架”于 1990 年首次发布,并在约翰·m·钱伯斯和特雷弗·j·哈斯蒂于 1992 年撰写的《S 中的统计模型》一书的第三章中进行了详细描述[1]。在那一章中,Chambers 和 Hastie 多次提到数据帧有一个矩阵原点。
数据帧明显有矩阵的味道;特别是,在思考和计算时,变量可以被视为矩阵结构的列,而观察值可以被视为矩阵结构的行…
整本书要记住的基本概念是数据框架支持类似矩阵的计算 …
Chambers 和 Hastie 继续描述了一种类似于关系表的结构,但保留了它的矩阵属性。S 的开源版本 R 于 2000 年发布了第一个稳定版本,并继承了 dataframe。2009 年, pandas 发布,为 Python 带来了 R dataframe 语义。所有这些 dataframe 实现都来自单一来源,继承了相同的语义和数据模型。
在我们最近的 arxiv 预印本[2]中,我们描述了 dataframe 数据模型的第一个形式定义和第一组 dataframe 代数。这个 dataframe 数据模型保留了最初在 S 中定义的数据模型,并且代数支持在 S、R 或 pandas 中所做的一切。
数据框架数据模型
数据框架产生于将数据视为矩阵和表格的需要。单一类型矩阵限制性太强,而关系表要求数据首先定义为模式。在数据帧中,可以在运行时推断列的类型,不需要事先知道,也不需要列中的所有行都是相同的类型。数据框架实际上是关系系统、矩阵以及电子表格的组合。
数据帧支持来自线性代数(矩阵)、关系代数(表格)和一些电子表格公式的运算符。
与关系系统相比,数据帧有许多有趣和独特的属性:
- 一个直观的数据模型,包含了行和列的隐式排序,并像在矩阵中一样对称地对待它们;
- 支持跨越关系(例如,过滤、连接)、线性代数(例如,转置)和类似电子表格(例如,透视)运算符的运算符的 API
- 一种直观的查询语法,鼓励快速验证简单表达式、重用查询结果以及组合成复杂查询
- 一种类型系统,允许在一列中异构类型化的行,例如在一列的 int 中的字符串作为脏数据。
data frame 数据模型如下所示:
数据框架数据模型。
数据帧由数据的二维混合型数组(矩阵)、一组行标签、一组列标签和每列的类型(域)组成。每列的类型都是可选的,如果需要,系统可以在运行时在列上引入一个类型。这种惰性模式允许将数据帧视为关系表,而不会牺牲其他类似矩阵的数据帧属性,这对于重用关系数据库的优化非常有用。
**与矩阵比较。**所有矩阵都可以表示为数据帧(带空标签)。然而,并不是所有的数据帧都是矩阵,即使我们去掉它们的标签。矩阵在模式中是同构的,但是数据帧允许多种类型的模式。在特殊情况下,没有标签的数据帧是一个矩阵,但是所有的数据必须是同类类型,并且属于 int 或 float 或其他满足字段代数定义的类型。我们将这些矩阵 称为数据帧,它们对于数据帧和机器学习管道之间的关系尤为重要。
**与关系表的比较。**一个关系由一个声明的模式定义,一个关系有许多可能的实例——满足该模式的元组集。一个实例可以被认为是一个固定的关系表。数据帧有点像关系实例:它们代表一组固定的数据。然而,它们的模式可以是未指定的,因此由模式归纳函数基于它们的内容来归纳。这种灵活性对数据帧至关重要,并有助于解释为什么数据帧出现在 R 和 Python 等具有动态类型和运行时类型错误的语言中。
数据帧可以有效地以两种等效的方式来查看。从关系的角度来看,数据帧是有序的关系,具有指定的行、列和行的等价性,以及一个延迟诱导的模式。从线性代数的角度来看,数据帧是添加了行和列标签的异构矩阵。在预印本中,我们继续通过利用这两种观点来描述数据框架代数,但是我将把代数的细节留给感兴趣的人。
系统正在定义不存在的数据框架
许多系统对缩放数据帧采取的方法是移除难以缩放的属性,或者将数据帧等同于关系表。这种行为可能会带来重新定义数据帧的副作用。这与系统本身无关,系统通常都构建得很好,用户也很满意。然而,随着越来越多的系统继续被贴上“数据框架系统”的标签,我们面临着失去数据科学家工作流程中一个重要部分的风险。术语“数据框架”几乎没有任何意义。对于用户来说,仅仅根据术语来衡量自称为数据框架的系统的效用已经很困难了。
数据框架本质上是交互式的,它们是数据科学家工具箱的重要组成部分,因为我们仍然需要系统来进行人在回路中的数据清理和对结构不良的数据进行争论。也许更重要的是,数据框架在与机器学习工具链交互时也有明显的优势,因为数据框架的线性代数和统计计算根。在非数据框架系统中,您可能会发现自己在清理/争论之后存储数据,只是为了将数据加载回您最喜欢的机器学习库中。相反,如上定义的矩阵数据帧通常可以直接用于机器学习库。
为什么这很重要。保留来自 S、R 和 pandas 的数据帧属性和语义的正式定义很重要,因为这些系统所具有的属性使得数据帧有用且独特。隐式排序很重要,因为 dataframe 用户希望在检查查询结果时看到一致的顺序,并希望系统保持首选顺序。行/列对称性和其他类似矩阵的属性使得重塑数据和与机器学习工具的交互操作更加容易。惰性类型很有用,因为数据经常处于不同的清理阶段。在预印本中,我们基于原始实现的独特属性形式化了一个狭义的定义。
正如我们在预印本中所描述的,我们使用这个正式定义来指导一个更具可伸缩性的数据帧系统的实现。我们不改变数据帧的定义,而是试图理解它,在不改变语义的情况下扩展语义。这是我们所知的传统数据框架得以生存的唯一方式。
参考
[1]约翰·钱伯斯和特雷弗·哈斯蒂合编。年代的统计模型。第 251 卷。太平洋格罗夫,加利福尼亚州:沃兹沃斯&布鲁克斯/科尔高级图书&软件,1992 年。
[2] Petersohn,Devin 等人,“可伸缩数据框架系统”arXiv 预印本 arXiv:2001.00888 (2020)。
使用 Panoply 预览地球系统模型模拟
当处理整个地球系统的模拟时,模型输出通常由数十到数百千兆字节的数据和数百个独立变量组成。使用 python 脚本等传统方法处理这个问题可能是一个漫长的过程,尤其是如果您想要快速浏览或比较数据的话。这就是 Panoply 发挥作用的地方。本文描述了这是什么,以及如何在其中加载您自己的数据集。
什么是 Panoply?
Panoply 是 NASA 的一个轻量级(~40 MB) netCDF 查看器,可用于快速了解地球模型模拟中的变量。Panoply 设计用于来自 Goddard 地球观测系统(GEOS) 的输出,但在使用 AerVis 代码转换为 netCDFs 后,也可用于 Met Office Unified Model .pp
输出。
装置
安装很简单,所需的文件很紧凑,可以在大多数操作系统上运行,不应该涉及任何管理权限。可以从以下页面找到下载链接:
[## 美国宇航局 GISS:全景 4 netCDF,HDF 和 GRIB 数据浏览器
panoply \PAN-uh-plee\,名词:1。壮观或令人印象深刻的阵列。…全景图地理参考和其他阵列来自…
www.giss.nasa.gov](https://www.giss.nasa.gov/tools/panoply/)
加载地块
要加载一个图,我们首先打开 Panoply(双击),我们得到一个文件浏览器窗口。使用它导航到您的数据目录并选择相关文件:
选择您的变量
接下来,我们将看到所选文件的内容。从这里,我们可以点击我们希望进一步探索的变量。
选择您的绘图风格
接下来,选择绘图样式。对于 AerVis 代码,这应该是地理参考经度数据。
查看情节
最后,我们创建绘图——从中我们可以使用下面的选项来更改时间步长和绘图属性。
比较不同的图/变量
最后,如果我们愿意,可以返回 netCDF 变量(上方的选择您的变量 部分)选择一个不同的值,并点击合并图按钮而不是进行创建。这在我们的可视化中创建了两个绘图数组,并允许一系列的组合选项。
结论
当前的方法涉及大量的数据混合和脚本来提供模型比较。现有的工具很冗长,要么需要花时间编写,要么非程序员无法使用。Panoply 提供了一种在任何计算机架构上探索 ESM 模型输出的交互式方法。因此,如果您有 netCDF 格式的全球数据,请尝试一下!
抽样资料
为了生成数据,可以从 github 库下载 GEOS Chem,尽管建议至少在 16 个内核上运行。然而,英国气象局模型需要安全检查和许可才能获得。
定价和营销分析——组织增长引擎的关键
定价是推动组织收入并帮助确定盈利能力的关键因素之一。一些顶级组织,尤其是亚马逊,利用了基于客户需求和行为的动态定价。你可能已经注意到,像优步,Careem 这样的公司在高峰时间收取更高的价格,这些价格是根据管理供求的算法建模的。像沃尔玛这样的零售公司之所以取得成功,是因为他们每天都采取低价策略,向顾客提供低价;这帮助他们提高了销售额和客户忠诚度。如今,随着大数据技术的出现,许多企业正在利用数据的力量来优化定价决策。即使价格上涨 1%,也能使组织的运营利润提高 10%。
因此,定价在一个组织的发展中起着关键作用,并帮助他们实现四个主要目标:
图 1:定价的四个主要目标(图片由作者提供)
这篇文章将讨论不同的定价策略,尤其将详细讨论三种重要的定价技术。所有这些技术对营销人员、业务分析师和数据科学家都很重要。
1.奶油/撇奶油
图 2:价格撇除策略从溢价开始(图片由作者提供)
这种定价策略包括设定初始溢价,预期随着竞争对手的进入价格会降低。这里的目标是在需求很高,并且没有竞争对手介入的情况下,获得尽可能多的收入。
示例:
最新的 iPhone/iPad/Mac
苹果在开始时通常会保持很高的价格。
图 3:由于这三个因素,苹果在开始时通常保持很高的价格。(图片由作者提供)
首先,苹果拥有庞大的客户群;鉴于其品牌,许多人认为苹果的产品极具吸引力。
高初始价格意味着其产品的质量。
苹果预计其销量将非常高,因此未来降低价格对其整体销量影响很小或没有影响。
请注意,当产品是创新的或豪华的时,这通常很有效。
2.渗透定价
图 4:渗透定价策略从低价开始(图片由作者提供)
这种定价通常被市场中的后来者使用,以便他们可以通过在开始时设定较低的价格来从竞争对手那里吸引客户。
智能手机市场
正如我们已经看到的例子,苹果收取非常高的价格/撇除价格。另一方面,三星以低价为工具,利用渗透定价来吸引顾客。
苹果仍然向小市场销售昂贵的手机,并能够在消费者中建立品牌忠诚度,而三星则向更广泛的客户群销售大量手机。
图 5:渗透定价策略与价格撇除策略(图片由作者提供)
3.日常低价
图 6:https://en . Wikipedia . org/wiki/daily _ low _ price #/media/File:EDLP _ 1。JPG
沃尔马特广告
这种策略包括对产品设定持续的低价,而不必等待销售活动。沃尔玛获得了巨大的成功,因为每天的低价帮助它建立了提供低价的声誉。如今,该公司拥有 8500 多家门店,为超过 2 亿名客户提供服务。虽然这种策略导致低利润,但沃尔玛能够从其巨大的销售额中获得可观的利润。
5.成本加成定价
对于大多数零售商来说,这是一种非常常见的定价技巧,在这种情况下,销售价格是由成本和成本之外的百分比决定的。
第一步:确定固定成本和可变成本
**固定成本:**无论你做不做生意,这个成本都保持不变。例如月租金。
可变成本:这是原材料、劳动力或任何其他与每单位产品或服务相关的价格。这随输出水平而变化。
第二步:确定单位成本
单位成本包括公司生产一单位产品或服务的总成本。这包括固定成本和可变成本。
(图片由作者提供)
第三步:确定加价
(图片由作者提供)
因此,我们将收取 8.75 美元/台。
6.目标回报定价
这种定价模式根据投资者希望从投资于公司的资本中获得的收益来定价。从上面的表格中,我们可以看到投资者期望 20%的投资回报。
(图片由作者提供)
7.需求定价
既然我们已经看到了包括传统定价策略在内的不同定价策略,现在让我们将注意力转向动态或基于需求的定价方法。你可能每次访问都看到不同的电商网站收取不同的价格。另一个例子包括航空业;7 月份买的票和 1 月份买的票价格不一样。为什么?这是因为需求;更高的需求推高了价格。7 月份的假期可能是提振 7 月份需求的因素之一。
另一个例子包括酒店业,周末预订的房间将比工作日期间租赁的相同房间更贵。此外,你可能已经注意到,booking.com 价格每天都在根据需求而变化。
为了更好地理解这个概念,让我们来说明需求曲线的概念。
图 7:需求曲线说明了需求量和价格之间的反比关系(图片由作者提供)
这条典型的需求曲线说明了需求量和价格之间的反比关系。随着价格上涨,需求量下降。现在的问题是价格的变化会对需求量产生多大的影响?为此,经济学家计算了所谓的需求价格弹性。
需求数量的变化对价格变化的反应就是所谓的需求价格弹性。在这里,我们将探讨需求的价格弹性以及现实世界的应用。
图 8:https://sites . Google . com/site/frameworks mkt 622/home/5-创造价值-主张/定价/价格-需求弹性
PED=%需求数量变化/ %价格变化
解释弹性
当价格的微小变化导致需求量的较大变化时,需求被认为是有弹性的。这是强替代产品的情况,价格的微小变化可能导致客户购买替代产品。
当价格的变化引起需求的巨大变化时,需求被认为是无弹性的。
图 9:【https://hbr.org/2015/08/a-refresher-on-price-elasticity
说明最优定价的简单示例
图 10a:最优定价(作者图片)
请注意,在 40 美元,我们赚取最大利润。在 50 美元时,需求量减少,导致总利润减少。这里的重点是解释弹性和数量需求根据价格变化的方式如何影响我们的整体收入和利润。
图 10b:最优定价(作者图片)
非常欢迎你计算需求的价格弹性
企业如何利用价格弹性?
这是营销经理/分析师最重要的指标之一。根据《哈佛商业评论》中提到的一次采访,营销人员的目标是将他或她的产品从相对弹性转变为非弹性,这是通过创造差异化产品、品牌形象和忠诚度来实现的。目标是提高支付意愿,不管价格如何。
根据麦肯锡的见解,动态定价应该包括五个模块。
- 长尾模块:这有助于通过识别相似的产品来设定初始价格或介绍价格。根据其中一篇文章,拥有 100 万种产品的美国零售商通过收集 10 万种最畅销产品的丰富数据集建立了长尾模块,包括竞争对手的价格、客户行为和产品属性。这些产品然后被智能地匹配以确定价格。这个试点帮助零售商增加了 3%的收入。
- 弹性模块:这已经在上面说明了。这可以通过使用时间序列方法和回归模型来实现,这些方法和模型考虑了季节性、同类相食和竞争因素。
- 键值商品模块:这些商品的价格比其他商品更容易被消费者记住。这对于不销售产品的经销商尤为重要。例如食品杂货公司。在这种情况下,公司可以建立一个模型来评估每个项目对消费者价格感知的重要性。
- 竞争响应模块:利用数据科学和机器学习模型的力量,公司可以实时利用竞争对手的定价数据以及这些价格对客户的影响。
- 全渠道模块:该模块确保一个项目或产品的价格在所有渠道都得到反映。这里的公司可以利用价格歧视策略。
定价评估
既然我们已经看到了不同的定价策略,对一个组织来说,评估其产品价格也是非常重要的,因为投资者/股东对 ROI、盈利能力、IRR 和现金流感兴趣。我们可以用不同的方法来评估价格
- 收支平衡法
在这种方法中,我们计算要达到收支平衡需要销售的单位数量。这考虑了固定成本、单位成本和价格。换句话说,盈亏平衡点是一种产品的总收入等于总支出的点。
2。NPV 资本预算
资本预算有许多不同的方法,包括净现值、内部收益率、回收期、盈利能力指数。
净现值(NPV)考虑了资金的账户时间价值,并将未来现金流转化为今天的美元或货币。
参考
[1]https://small business . chron . com/penetration-pricing-examples-18365 . html
[2]https://www.marketing91.com/penetration-pricing/
[3]https://doc player . net/36830320-Chapter-8-price-analytics . html
https://research.aimultiple.com/dynamic-pricing/
https://hbr.org/2015/08/a-refresher-on-price-elasticity
Mercari 中的定价对象—机器学习和深度学习视角
作为开创性自我案例研究的一部分,解决 Kaggle 的 Mercari 数据集的基于 NLP 的回归的补充工作——应用人工智能课程
这是我的第一篇博客,我很高兴分享我在解密 Kaggle 比赛的解决方案时的机器学习和深度学习经验。尽管这是提交给 Kaggle 的一篇迟到的文章,但我在分析过程中的学习之旅也非常直观、有趣且富有挑战性。希望这个博客最终能给读者一些有用的学习资料。
目录
- 商业问题
- 误差度量
- 机器学习和深度学习在我们问题上的应用
- 数据来源
- 探索性数据分析— EDA
- 现有方法
- 数据准备
- 模型解释
- 结果
- 我改进 RMSLE 的尝试
- 未来的工作
- LinkedIn 和 GitHub 知识库
- 参考
图片来源:https://unsplash.com/photos/1M4wYTqVD4o
1.商业问题
Mercari,Inc .是一家在日本和美国运营的电子商务公司,其主要产品是 Mercari marketplace 应用程序。人们可以使用智能手机轻松销售或购买商品。该应用程序的用户可以在列出商品时自由选择价格。然而,这里存在更高的风险,因为如果价目表与市场价格相比过高或过低,消费者和顾客都会不知所措。上述问题的解决方案是自动推荐商品价格,因此,最大的社区购物应用程序希望向其卖家提供价格建议。
眼前的问题是预测任何给定产品的价格,这说明这是一个回归问题。我们在训练数据中的特征是物品的训练标识、名称、物品条件标识、类别名称、品牌名称、价格、运输、物品描述。除了作为我们目标变量的价格之外,我们在测试数据中拥有所有其他特征。这些特征不仅是分类的和数字的,而且也是卖方提供的商品的文本描述。例如,女性配饰产品的文字描述如下:
作者图片
我们可以看到两者售价不同;第一个 16 美元,第二个 9 美元。
因此,这里的挑战是,我们需要建立一个模型,该模型应该根据上图所示的描述,以及产品名称、类别名称、品牌名称、物品状况等来预测产品的正确价格。
2.误差度量
这个问题的误差度量是均方根对数误差(RMSLE)。请参考这篇博客了解更多关于该指标的信息。下图显示了度量计算的公式:
图片来源:https://cs 230 . Stanford . edu/files _ winter _ 2018/projects/6940447 . pdf
计算 RMSLE 的代码如下所示:
计算 RMSLE 的函数
3.机器学习和深度学习在我们问题上的应用
在这个人工智能(AI)的时代,我们想到 AI 的那一刻,接下来的两个流行语就是机器学习和深度学习。我们发现人工智能无处不在,事实上它们是人类生活的一部分。无论是通勤(如出租车预订),医疗诊断,个人助理(如 Siri,Alexa),欺诈检测,犯罪检测,在线客户支持,产品推荐,无人驾驶汽车,等等。有了尖端的机器学习和深度学习算法,任何类型的预测问题都可以解决。
我们的问题是独特的,因为它是基于 NLP(自然语言处理)的回归任务。NLP 中的第一步是将文本表示为数字,即将文本转换为数字向量表示,以构建回归变量。
作者图片
解决价格预测问题的一种方法是利用 TF-IDF、BoW 等矢量化技术,并构建将由经典机器学习算法(例如简单线性回归器、基于树的回归器等)使用的固定大小的稀疏向量表示。).另一种方式是使用深度 NLP 架构(例如 CNN、LSTM、GRU 或它们的组合),这些架构可以自己学习特征,并且需要来自嵌入的密集向量。在当前的分析中,我们正在研究这两种方法。
4.数据来源
本次分析的数据集来自 Kaggle,这是一个面向数据科学家的流行在线社区或数据平台。
图片来源:【https://www.kaggle.com/c/mercari-price-suggestion-challenge
理解数据
训练集包含超过 140 万个产品,而阶段 2 测试集包含超过 340 万个产品。
列出训练/测试数据中的字段名:
- train_id 或 test_id —列表的唯一 id
- 名称 —卖方提供的产品名称。请注意,为了避免数据泄露,该字段中的价格被删除并表示为[rm]
- item _ condition _ id—此处卖方提供物品条件
- 【类别名称】 —每件商品的类别列表
- brand_name —每件商品所属的对应品牌
- 价格 —这是我们的目标变量,以美元表示(test.tsv 中不存在该列)
- 运费 — 1,如果运费由卖家支付,0,否则
- item _ description—此处给出了每个项目的描述,价格被移除并表示为[rm]
下面显示的数据一瞥:
列车样本数据的屏幕截图
5.探索性数据分析— EDA
EDA 是数据科学过程中的一个重要步骤,是一种统计方法,通常使用可视化方法从数据集获得更多见解。在我们深入研究 EDA 之前,让我们快速浏览一下数据以了解更多信息。下面是检查空值的代码片段:
print(train.isnull().sum())
代码输出的屏幕截图
从上面的输出中,我们认识到有三列,即类别名称、品牌名称和项目描述包含空值。其中,品牌名称包含了大量缺失值(~632k)。列类别名称包含大约 6.3k 个空值,而项目描述只有 4 个空值。让我们稍后在创建模型时处理它们,现在我们逐个特性地深入研究 EDA。
5.1 类别名称的单变量分析
训练数据集中总共有 1287 个类别。下面显示的是计数的代码片段:
计数类别的代码
类别计数的输出
category_count 的曲线图如下所示:
上面的条形图显示了出现频率最高的 10 个类别。人们会注意到,女装是所有服装中最引人注目的。
每个类别名称由 3 个以’/'分隔的子类组成,并具有主类别/子类 1 /子类 2 名称。重要的是将它们分开,并作为新的特征包括进来,这将使我们的模型做出更好的预测。
拆分类别
在我们的分析中,我们使用下面的函数将每个 category_name 拆分为 main_category 、 sub_cat_1 、 sub_cat_2 。
用于拆分的效用函数
此外,这三列中每一列的类别数是使用下面的代码行计算的:
main_category_count_te = test['main_category'].value_counts()
sub_1_count_te = test['sub_cat_1'].value_counts()
sub_2_count_te = test['sub_cat_2'].value_counts()
上述分析表明,有 11 个主要类别,这些类别又分为 114 个子类别(子类别 1),这些子类别又被分配到列车数据中的 865 个特定类别(子类别 2)。绘制类别的代码如下所示:
绘图代码
绘图类别
拆分后该类别每列中前 10 项的条形图如下:
5.2 品牌名称的单变量分析
共有 4807 个独特品牌,其中出现频率最高的 10 个品牌如下图所示:
绘图代码在这里:
#https://www.datacamp.com/community/tutorials/categorical-data
sns.barplot(x=brand_count[:10].values, y=brand_count[:10].index)
plt.title('Frequency Distribution of top 10 brand names')
plt.xlabel('Counts', fontsize=12)
plt.show()
值得注意的是,粉红色和耐克品牌,其次是维多利亚的秘密,是占据领先地位的品牌。
5.3 价格的单变量分析
由于价格是数字,我们使用 describe()函数来感知统计汇总。下面是代码片段:
train.price.describe()
汇总统计数据
describe()函数解释了任何产品的最高价格是$2009,最低价格是 0。还注意到,75%的产品价格范围出现在 29 美元以下,50%的产品价格低于 17 美元,而 25%的产品价格低于 10 美元。平均价格范围是 26.7 美元。
价格变量的分布
绘制目标的分布图
plt.title("Distribution of Price variable")
plt.xlabel("Price(USD)")
plt.ylabel("No. of products")
plt.hist(train['price'],bins=30)
从上图可以明显看出,价格特征遵循右偏分布。正如这里讨论的,由于分布另一侧的点,偏斜分布会导致高均方差(MSE)值,如果数据是正态分布,MSE 是有限的。因此,价格特性的对数转换是不可避免的,如果数据是正态分布的,模型也可以表现得更好。这是通过以下代码片段完成的:
日志转换代码
下面显示的是对数变换后的价格变量的曲线图。
5.4 项目 _ 描述的单变量分析
我们绘制单词 cloud 是为了了解描述中的流行单词。对应的代码片段与下面显示的图对应:
单词 cloud 的代码
从上面的词云中,人们可以注意到在我们的 item_description 中频繁出现的词。
描述字数
单独的文本描述可能是该问题的重要特征(参考),即对于机器学习模型,并且将有助于深度学习模型的嵌入过程。
用于计算描述中字数的代码
为了进一步研究该特性,我们绘制了如下所示的箱线图和分布图以及代码:
箱形图
sns.boxplot(train['description_wc'],orient='v')
plt.title("Box plot of description word count")
plt.xlabel("item_description")
plt.ylabel("No. of words")
plt.show()
人们可以注意到大多数描述包含大约不到 40 个单词。
密度图
plt.figure(figsize=(10,3))
sns.distplot(train['description_wc'], hist=False)
plt.title('Plot of the word count for each item description')
plt.xlabel('Number of words in each description')
plt.show()
描述字数的密度图是右偏的。
汇总统计数据
汇总统计表明 item_description 的最小长度为 1,而最大长度为 245。平均长度约为 25 个单词。越少的描述越长,而它们中的大多数包含不到 40 个单词,正如我们在方框图中看到的。
5.5 项目 _ 条件 _ 标识的单变量分析
有序分类特征 item_condition_id 的范围是 1 到 5。普通性是条件 1 的项目是“最好的”,条件 5 的项目是“最差的”(基于这个参考)。 item_condition_id 的条形图如下所示:
正如上面的柱状图所示,大多数待售商品状况良好。因此,条件 1 的项目较高,其次是条件 3 和条件 2,而条件 4 和条件 5 的项目较低。
5.6 双变量分析
了解目标变量与其他特征的关联类型将有助于特征工程。因此,价格变量与其他两个变量进行比较。
价格与运输
和往常一样,绘图会帮助我们更好地理解事物。代码和图形如下所示:
箱线图的代码
人们可以观察到,当物品的价格较高时,卖家支付运费,反之亦然。
价格与描述长度
这两个变量的绘图和我们上面做的一样。
plt.scatter(train['description_wc'],train['price'],alpha=0.4)
plt.title("Price Vs Description length")
plt.xlabel("Description length")
plt.ylabel("Price(USD)")
plt.show()
值得注意的是,描述长度较短的商品往往比描述长度较长的商品价格更高。
6.现有方法
有几个内核、博客和论文使用简单的机器学习或深度学习方法做出了解决方案。我们将向他们中的一些人简要介绍:
MLP
Pawel 和 Konstantin 凭借他们惊人的解决方案赢得了这场竞赛。他们使用了一种基于前馈神经网络的方法,即一种对稀疏特征有效的简单 MLP。执行的一些预处理技巧包括对分类列进行一次性编码,使用标准 PorterStemmer、BoW-1,2-grams(有/没有 TF-IDF)对文本进行词干分析,将不同的字段(如 name、brand name 和 item_description )连接成一个字段。他们获得了 0.37 的优异成绩。
美国有线新闻网;卷积神经网络
在这项研究中,作者使用 CNN 架构和最大池分别对名称和项目描述进行矢量化。他使用预先训练的手套向量进行单词嵌入,嵌入是对姓名和项目 _ 描述的共享搜索。使用的一些有用的技巧是在最后一个完全连接的层和单词嵌入的平均池层之前跳过数字和低基数分类特征的连接。作者从单一深度学习模型(链接)取得了 0.41 的惊人成绩。
LGBM +山脊
在这里,作者应用了一个名为 LightGBM 的基于树的梯度推进框架,以实现更快的训练和更高的效率。该研究还使用了一个简单、快速的岭模型进行训练。一些特征技术包括:使用 CountVectorizer 对 name 和 category_name 列进行矢量化,使用 TfidfVectorizer 对 item_description 进行矢量化,使用虚拟变量对 item_condition_id 和 *shipping_id 进行创建,*使用 LabelBinarizer 对 brand_name 进行矢量化。两个模型合二为一后,作者取得了 0.44 的成绩。(链接)
GRU + 2 山脊
在这项研究中,作者使用 RNN、里奇和里奇 CV 构建了一个关联模型,其均方根误差约为 0.427。这里应用的一些有用的技巧包括对文本特征使用 GRU 层,对 RNN 训练使用预定的学习衰减,使用具有 2 个时期的小批量大小,以及对脊模型使用超过 300k 的特征(链接)。
7.数据准备
数据清理
根据这份参考资料,Mercari 网站上的最低价格为 3 美元。因此,在我们的训练数据中,我们保留价格高于$3 的项目。下面显示的是相同的代码片段:
#https://www.kaggle.com/valkling/mercari-rnn-2ridge-models-with-notes-0-42755
train = train.drop(train[(train.price < 3.0)].index)
处理空值/缺失值
从 EDA 中,我们了解到 3 列即。、类别名称、品牌名称、和项目描述为空值。因此,我们用适当的值替换它们。我们用下面的函数来做这件事:
处理缺失值的代码
列车测试分离
在我们的分析中,价格字段是目标变量“y”。根据误差函数评估我们的回归模型的适合度是很重要的,我们需要观察“y”并预测“y”。不幸的是,我们没有观察到的目标值来评估我们的模型。因此,训练数据被分成训练集和测试集。
对于基本的线性回归模型,测试集由 10%的数据组成,对于深度学习模型,测试集包括总数据的 20%。
缩放目标
使用 sklearn.preprocessing 中的 StandardScaler 函数对目标变量进行标准化,如下所示:
缩放目标变量
因为我们的目标变量是使用上述函数标准化的,所以在我们计算误差度量之前,将预测值按比例缩小(逆变换)是很重要的。这是通过使用以下函数来完成的:
8.模型解释
让我们详细了解一下机器学习和深度学习管道。
8.1 机器学习管道
作者图片
在此分析中,自然语言处理概念如 BoW、TFIDF 等。用于对机器学习回归模型的文本进行矢量化。
特征构造
在执行 EDA 时,我们添加了四个新特性,即通过拆分列类别生成三个新列,并添加来自 item_description 的文本描述的字数。此外,我们根据姓名文本的长度再创建一列。所以,我们有了五个新特性。
我们的数据集由分类特征、数字特征和文本特征组成。必须对分类特征进行编码,并对文本特征进行矢量化,以创建模型使用的特征矩阵。
分类特征编码
我们使用 sci-kit 中的 CountVectorizer 函数对分类特征进行一次性编码,如 category_name 、 main_category 、 sub_cat_1 、 sub_cat_2 、 brand_name ,并使用 get_dummies()函数对 shipping_id 和 item_condition_id 进行编码。下面是相同的代码:
一键编码的代码
文本特征矢量化
我们分别使用 BoW(一元和二元)和 TFIDF(一元、二元和三元)编码文本特征 name 和 item_description 。其功能如下所示:
矢量化代码
下面给出了使用上述函数的代码:
特征矩阵
通过将所有编码特征(分类特征和文本特征)与两个数字特征(即文本描述和姓名的字数)连接起来,生成最终的矩阵。以下代码供参考:
产生的最终矩阵由超过 206k 个特征组成。的确,这是一个很大的特点。
最终矩阵形状的屏幕截图
首次切割模型
为这个问题选择一个合适的算法可能是一项艰巨的任务,尤其是对初学者来说。
图片来源:https://unsplash.com/photos/bmrGgKXz_xU
我们学习根据数据维度、线性、训练时间、问题类型等选择算法。正如这里讨论的。
在我们的分析中,我们首先使用简单的线性模型进行实验,如线性回归、支持向量回归机,对于这两个模型,我们都选择 sci-kit learn 中的 SGDRegressor。在此之后,我们训练岭回归。
我们使用 GridSearchCV 通过以下函数对所有模型的参数进行超参数调整:
线性回归
线性回归旨在减少预测和实际数据之间的误差。我们使用具有“平方损失”的 SGDregressor 和针对不同 alphas 的超参数调整来训练简单的线性回归。下面显示的代码是相同的:
根据我们的测试数据,这个简单的模型得出的最佳 alpha = 0.001 的 RMSLE 为 0.513。
支持向量回归机:
支持向量回归机(SVR)旨在预测偏离实际数据不超过ε的函数(参见)。我们使用具有“epsilon_insensitive”的 SGDRegressor 作为 alphas 的损失和超参数调谐来训练 SVR。在我们的测试数据中,当 alpha = 0.0001 时,该模型产生的 RMSLE 为 0.632。在我们的例子中,简单的线性回归比支持向量机表现得好得多。SVR 的代码如下:
里脊回归
岭回归是线性回归的近亲,具有由 L2 范数给出的一些正则化以防止过度拟合。我们在线性模型下使用 sci-kit learn Ridge 库获得了良好的拟合,对于 alpha = 6.0,RMSLE 为 0.490。下面是具有超参数调整的岭模型的代码:
里奇夫
它是一种交叉验证估计器,可以自动选择最佳超参数(这里指的是:)。换句话说,带有内置交叉验证的岭回归。正如在这个内核中所述,对于我们的问题,它比 Ridge 表现得更好,我们构建了 RidgeCV 模型,在我们的测试数据上,它为 alpha 6.0 产生了 0.442 的 RMSLE。相同的代码如下所示:
我们还可以观察到 RidgeCV 在我们的第一次切割模型中表现更好。为了进一步提高分数,我们正在探索使用 RNN 解决这个问题的神经网络。
8.2 深度学习管道
递归神经网络(RNN)擅长处理序列数据信息(更多信息请参见参考)。我们使用门控递归单元(GRUs)来构建使用神经网络的回归器,这是一种训练速度更快的新型 RNN。从 GRU,我们获得嵌入后的名称、item_description 列的文本特征向量,对于其他分类字段,我们使用嵌入,然后进行展平。所有这些共同构成了我们深度学习模型的 80 维特征向量。
作者图片
嵌入
深度学习(DL)管道的数据准备遵循与 ML 管道相同的程序,除了训练-测试分离。这里我们认为 20%的训练数据是测试数据。如前所述,DL 管道需要密集的向量,而神经网络嵌入是将我们的离散变量表示为连续向量的有效手段(参见)。
标记化和填充
嵌入层要求输入为整数编码(参考)。因此,我们使用 Tokenizer API 对文本数据进行编码,下面是相同的代码片段:
标记化后的示例描述数据截图如下所示:
标记化后描述文本的屏幕截图
在标记化之后,我们填充序列。名称和描述文本长度不同,Keras 倾向于输入序列长度相同。我们计算超出特定单词范围的数据点的百分比,以确定填充长度。
下面显示的代码用于选择描述文本的填充长度:
print("% of sequence containing len > 160 is")
len(X_train[X_train.wc_desc > 160])/X_train.shape[0]*100
上面代码的输出
从上图中我们看到长度超过 160 的点的百分比是 0.7,这是< 1%. Hence we pad description text with 160. Similarly, we calculate for the 名称列,我们选择 10。相应的代码片段如下所示:
print("% of name containing len > 10 is")
len(X_train[X_train.wc_name > 10])/X_train.shape[0]*100
以上代码的输出
此外,我们根据排名对分类变量进行编码,代码片段如下所示:
注意:所有的嵌入都是和模型一起学习的。
整个数据准备管道以及编码、标记化和填充由以下函数执行:
网络体系结构
当前分析的网络设计受这个内核的启发。此外,我们在这个框架中试验了额外的丢弃层和批量规范化层。下面是构建我们的网络的代码片段:
看看我们下面的网络:
GRU 神经网络
深度学习模型
总共训练了四个模型,它们的退出率和学习率不同。每个网络由四个退出层组成,对于这些层中的每一层,我们对所有模型尝试不同的退出率(详见结果)。
培养
对于训练模型 1 和模型 2,我们使用具有默认学习速率的 Adam 优化器。此外,这些模型用两倍的批量大小训练 3 个时期。模型 1 和模型 2 的测试数据的均方根误差分别为 0.436 和 0.441。模型训练如下图所示:
具有默认学习率的模型 2 训练
模型 3 和 4 使用 Adam optimizer 以预定的学习速率进行训练。共 3 个时期,批量为 1024。对于时段 1 和 2,学习率是 0.005,对于最后一个时段,我们将其降低到 0.001。模型 4 的培训如下所示:
模式 4 以预定的学习率进行培训
模型平均集合
模型平均是一种集成学习技术,用于减少神经网络中的高方差。在目前的分析中,我们集成了在不同退出正则化条件下训练的模型(参考)。每个模型大约需要 30 到 35 分钟来训练。考虑到训练时间,我们只包括两个模型进行组合。因此,在四个模型中,创建了两个系综,即,一个来自实现 RMSLE 0.433 的模型 1 & 2,另一个来自 RMSLE 0.429 的模型 3 & 4
组装模型 1 和 2 的代码如下所示:
我们观察到来自模型 3 & 4 的集合在我们的训练测试分割数据中表现优于。
最终模型
为了获得 Kaggle 的最终分数,我们现在在 Kaggle 内核中训练。我们建立了两个与模型 3 和 4 相同的模型用于组装。下面展示的是在 Kaggle 接受训练的模特的截图:
这两个模型的模型平均组合在包含约 340 万个产品的最终测试数据上的 Kaggle 公开评分中得到 0.428 分。因此,我们获得了排名榜前 10%的分数。
排行榜截图
9.结果
下面显示的是模型输出的摘要屏幕截图:
机器学习流水线的输出:
深度学习管道输出快照:
4 个模型的输出
组装输出截图:
2 个系综的输出
Kaggle 投稿评分:
10.我改进 RMSLE 的尝试
一般来说,Kaggle 的竞争对手的核心是说教的来源。本分析的灵感来自于这个、这个以及一些来自于赢家的解决方案。提高我们分数的策略如下:
- **包含停用词进行分析:**这个问题的一个关键是,删除文本描述/名称中的停用词会影响 RMSLE 分数。停用词的保留提高了分数。
- **考虑更多的文本特征用于建模:**总共得到 206k 个特征,其中包括仅来自文本数据的 200k 个特征。在这个问题中,更加重视文本信息可以提高分数。
- **二元语法和三元语法:**在自然语言处理中,习惯上包括二元语法、三元语法等 n 元语法。如果我们打算在矢量化过程中添加一些语义。在我们的研究中,我们使用单词包对名称应用一元和二元语法,使用 TFIDF 对项目描述应用一元、二元和三元语法。
- 根据价格 过滤数据:Mercari 不允许发布低于 3 美元的商品。因此,那些产品价格低于$3 的行将是错误点。移除它们将有助于模型更好地运行。
- **小批量模型训练&少时段:**使用 1024 的批量和预定的学习提高了分数
- **集成两个神经网络模型:**这种策略是当前研究独有的,它将分数推高到了排行榜上。我们通过训练两个神经网络模型并构建它们的集成来执行我们的预测分析。
11.未来的工作
可以通过探索以下选项来提高分数:
- 使用多层感知器,因为它是解决这个问题的流行方法
- 使用 CNN 结合 RNN 处理文本数据
- 添加更多的 GRU 层或进一步微调
这些是我们随后想要探索的一些可能性。
12.LinkedIn 和 GitHub 知识库
这是我在 LinkedIn 上的简介。请参考我的 GitHub 库查看完整代码。
13.参考
https://github . com/pjankiewicz/mercari solution/blob/master/presentation/build/yandex . pdf
使用 Kaggle 笔记本探索和运行机器学习代码|使用来自 Mercari 价格建议挑战赛的数据
www.kaggle.com](https://www.kaggle.com/valkling/mercari-rnn-2ridge-models-with-notes-0-42755) [## Mercari 价格建议挑战
这是我在 Kaggle 环境中的第一次数据科学体验。虽然挑战本身很有趣,经历也…
medium.com](https://medium.com/analytics-vidhya/mercari-price-suggestion-challenge-66500ac1f88a) [## Mercari 价格建议挑战——机器学习回归案例研究
这是我的第一个媒介故事。希望你读它的时候开心,就像我喜欢为观众写它一样
medium.com](https://medium.com/analytics-vidhya/mercari-price-suggestion-challenge-a-machine-learning-regression-case-study-9d776d5293a0) [## 深度学习神经网络的集成学习方法——机器学习掌握
如何通过组合多个模型的预测来提高性能?深度学习神经网络是非线性的…
machinelearningmastery.com](https://machinelearningmastery.com/ensemble-methods-for-deep-learning-neural-networks/) [## 如何在 Keras 中开发一套深度学习模型——机器学习掌握
深度学习神经网络模型是高度灵活的非线性算法,能够学习近乎无限数量的…
machinelearningmastery.com](https://machinelearningmastery.com/model-averaging-ensemble-for-deep-learning-neural-networks/) [## 我如何在使用 CNN 和 Tensorflow 的 Kaggle 的 Mercari 价格建议挑战中失去银牌
2018 年 1 月,我参加了一个名为 Mercari 价格建议的 Kaggle 比赛。比赛持续了三…
medium.com](https://medium.com/unstructured/how-i-lost-a-silver-medal-in-kaggles-mercari-price-suggestion-challenge-using-cnns-and-tensorflow-4013660fcded) [## 应用课程
应用机器学习课程 GATE CS 混合课程面试准备课程 AI Workshop AI 案例研究
www.appliedaicourse.com](https://www.appliedaicourse.com/course/11/Applied-Machine-learning-course)
危机期间的定价优化:数据是关键
疫情引发的危机给客户需求带来了前所未有的压力。如果没有受到这些环境的巨大影响,组织必须相应地快速适应市场变化,以维持甚至发展业务并超越竞争对手。因此,优化他们的定价策略对于驾驭这种新常态至关重要。
然而,即使在平时,也很少有公司在第一次尝试时就能正确定价,第二次、第三次或第四次尝试时也是如此。根据贝恩公司的一项研究,高达 85%的 B2B 公司“在定价上有很大的提升空间”但是,即使一家公司碰巧接近目标,继续向最优定价前进也会产生巨大的差异。
我最近一直在与来自顶级实验室的同事一起研究这个特定的主题,特别是他的创始人 Manu Carricano ,我们一起编写了 这份白皮书 ,并举办了一场 网络研讨会 ,分享了我们的一些发现,以及如何构建一个可靠而灵活的框架来监控和调整定价策略。
数据是关键
定价优化的一个基本要素是数据。传统上,公司通过选择少量相关数据流来确定固定价格,从而锚定其定价策略。然而,如今的组织越来越多地将越来越多的元素纳入动态而非固定的定价模型。随着定价成为差异化的核心杠杆,在这个不可能的时代,组织需要能够比以往任何时候都更快地响应市场变化,这意味着根据环境(如本地化或特殊场合)优化一系列决策(如定价或促销),以服务于多个业务目标的功能(如净收入增长或交叉销售)。
公共数据模型:走向可信定价模型的第一步
动态定价模型有可能极大地改变组织的底线。但是它也可以测试特定数据架构和数据管理实践的限制。在整合越来越多的数据流时,由于缺乏数据质量、跨多个地理位置或业务部门的数据治理不力,以及部署时间过长且效率低下,许多组织都面临着重复的“信任”问题。
解决这些问题的第一步是开发一个公共数据模型(CDM)。为所有需要的数据建立一个集中的模型对于确保用户拥有单一的真实来源是至关重要的。CDM 为影响定价优化的关键部分定义了标准,包括交易、产品、价格和客户。从那里,标准通过混合数据流应用,并服务于下游系统以利用定价数据(业务应用、仪表板、微服务等)。)与一个同构数据模型。CDM 还为参与该计划的各个团队提供了一种一致的方式,以便使用一种共同的语言更好地协作。
为定价优化准备适当的数据
拥有一个通用的数据模型是一个重要的基础组件。但定价优化效率的真正考验将取决于定价和收入专家对这些数据采取行动的速度。换句话说,用户准备数据有多容易,这是数据分析的第一步,也是最关键的一步。从历史上看,答案一直是“不太”。准备数据可能包括从处理空值到标准化不匹配的数据到拆分或连接列的所有内容,这是一个非常耗时的过程,需要非常注意细节,并且通常需要大量的技术技能。它可能会占用整个分析过程的 80%。但总的来说,花费额外的时间是值得的——适当准备的数据可以在错误和准确的最终分析之间产生差异。
为了解决定价优化的数据方面,我非常喜欢谷歌云用 Cloud Dataprep 解决数据准备问题,这是一种完全托管的服务,利用 Trifacta 技术,允许用户访问、标准化和统一所需的数据源。其机器学习驱动的引擎和可视化界面将整体数据准备过程的速度加快了 90%。利用云数据准备进行定价优化的一些关键步骤包括:
- 评估数据源:在获取定价优化所需的数据后,必须对其内容进行评估。每个源系统都有自己描述和存储数据的方式。每个源系统也将具有不同的精确度。在这第一步,建立一个清单是必要的,以便清楚地了解每个来源的质量,这将在以后通知他们应该如何清洗和标准化。
- 标准化数据:在确定源系统并评估其数据质量之后,下一步实际上是解决那些数据质量问题,以实现数据的准确性、完整性、一致性和完整性。最终,这些数据将被标准化并映射到其相应的 CDM 数据类别。
- 在一个结构中统一:将这些数据统一到一个单一的结构中包括将所有单独的 CDM 数据类与每个单独的类在最细的粒度级别上的属性连接起来。这是一个关键步骤,因为它为所有定价优化工作创建了一个可靠的数据源。
- 交付分析& ML/AI :一旦数据干净并准备好进行分析,分析师就可以开始运行用例及场景,探索价格变化的影响。正是在这个阶段,组织将开始看到其底线的巨大变化,但不是没有准备这些数据的所有艰苦的前期工作。
了解更多信息
想要了解在云中成功实施定价分析框架的关键因素吗?你可以阅读我们与致力于价格优化实践的团队共同撰写的白皮书。您还可以观看我们就该特定主题举办的网络研讨会,这样您就可以找出贵组织的弱点,更好地了解您的定价优化之旅所需的数据源,并查看 Google Cloud 智能分析套件如何实现基本公共数据模型(CDM)的分步演示。
定价,包装和产品:如何使用联合和 maxdiff 得到它的权利
摄影:rinke dohmen 资料来源:联合国人类住区规划署
我打赌你们公司的定价都是错的。
好吧,也许不是错了,让我们只说明显是次优。我不认识你或你的公司,但我知道在这方面我胜算很大——我也知道很少有产品经理或公司高管相信他们的定价是正确的。
你的产品和营销策略中最重要的一个方面(也许是最重要的)很少被关注,很可能是自动运行的。事实上,我最近对产品经理进行了一项调查,平均而言,他们最近一次对材料定价进行审查/更改是在 26 个月前。我明白为什么会这样——定价和包装决策是困难的、有风险的,而且是跨职能的。但这并不意味着什么都不做是明智的做法。
很多公司都做对了。一些行业有效地强迫它(想想消费品/零售)。但这是一个 B2B、技术和服务型公司特别努力的领域。好消息是,通过一些工作,它可以被修复,您可以立即对您公司的运营经济产生重大影响。
框架
我组织定价/包装分析的方式分为四个相关主题:
1.打包:在这种情况下,这意味着将各种功能或属性组合成不同的产品(SKU)。
2.价格模型和价格指标:这是您组织 SKU 和向客户收取产品费用的方式。例如,您可能有一组好-更好-最好的分层产品(模型),您对其收取一次性安装费,并按用户每月订阅费(指标)。
3.价格点:这些是您收取的实际价格(例如 3.99 美元),不同的 SKU 会有所不同。价格点可以基于期限承诺和数量以及细分市场和地理位置。在大多数地方,价目表实际上是一个价格矩阵。
4.商业条款:在 B2B 环境中,你将有一个协议来管理双方之间的业务关系。这是升级、降级、取消、产品寿命终止和未来价格上涨的定义。
在分析和改变这四个定价/包装领域时,需要一点艺术和科学。科学方面是关于使用正确的方法获得做出明智决策所需的数据。艺术维度是你如何将这些洞察力应用到市场中。例如,您可能故意优化包装/定价,因为从营销信息或定位的角度来看,“正确”的答案不起作用。
接下来的部分主要集中在寻找合适的定价和包装的科学方面。
该方法
步骤 1-创建基线
假设你有一个现有的企业,首先你应该把你的客户的交易规模和他们支付的平均单价放在一起绘图。在 x 轴上,您按照 ACV 从最小到最大对客户订单进行排序,在 y 轴上是支付的单价。如果您没有单一的可变价格指标(例如,每用户),而是只有一个固定费用类型模型或使用各种不同的指标,那么您应该使用适用于您产品的最接近的使用指标。这使你的顾客群支付的实际价格标准化了。
来源:pricekit.io
这告诉你什么?首先,这给了你一个要超越的底线。其次,如果您看到大量分散的数据点,这表明不一致的销售折扣做法(如果得到解决,这本身可能是一个快速的胜利)。
另一个值得获取的数据点来自你失去的交易(你应该密切跟踪)。如果你有细节,把它们画在上面的同一张图上。毫无疑问,由于价格和其他因素而被归类为失败的交易将被夸大。如果你看到这些拟议中的交易与成功的交易没有什么不同,这强烈表明价格以外的因素也在起作用。
第二项活动是审查你现有的商业条款。如果您对某些客户有最惠价格、价格上涨的数量和频率限制或其他此类承诺,您需要将它们记录下来并跟踪到受影响的客户。当您计划推出任何新模型时,您将需要它。
此外,这项工作的一部分应该是让你评估将现有客户群货币化的机会。
你的账户级别变动是多少?这衡量的是不续订和完全离开你的客户。如果价格很低,并且存在退出障碍(比如,向新供应商迁移的成本很高),那么你就拥有定价权。你的商业条款会告诉你,你可以在这些现有客户身上提高价格的程度和过程。其次,看看 NRR(我在这里谈论这个话题),如果它不是强大(比如说< 110%),这表明你应该寻找包装机会,以推动更强的向上销售和交叉销售运动进入现有基础。
最后,*记录竞争对手的报价。*这既包括直接竞争对手,也包括客户可能采用的替代产品,而不是购买您的产品。获得全面的报价菜单、标价和有效折扣需要努力,但这是值得的。询问客户、失去的交易、行业专家、渠道合作伙伴的见解。详细的机密数据不是目标,但了解他们的主要产品、他们使用的指标和粗略的价格点才是。
第二步—编译离散选择选项
找到合适的定价和包装的最佳方法是 AB 测试你的方式。没有什么比实际购买数据更真实、更确凿的了。但是除非你有很大的耐心,很大的预算,并且只通过数字和直接的方式销售——那么你需要找到另一种方式。
另一种方法是获取数据,以便对客户购买行为进行建模。做到这一点的方法是离散选择分析。
离散选择分析是一种统计建模,涉及在给定一组选项的情况下,个人选择特定选项的概率。它还与逻辑回归相关,逻辑回归是预测二元(例如是/否)结果的常用方法。
您可能在过去见过或使用过联合分析或最大差异分析——这两种分析都可以被视为离散选择模型的形式,至少它们通常是如何应用于产品定价/包装问题的。
离散选择的本质是给顾客提供一套现实的备选方案,然后看他们选择什么。轻松点。
来源:pricekit.io
嗯,概念上很容易,但要做好它需要相当多的工作。特别是,您需要解决两个挑战:
许多可能的选项。假设你销售的产品有 5 个特点或功能。在这 5 个包中,你可以创建 31 个不同的包。(如果你对这个怎么算感兴趣,查一下组合公式。它是:组合 5 选 5,加 5 选 4,以此类推)的和。
让我们进一步说,你想测试不同的价格点,一个高,一个基线和一个低端,以确定其对所选套餐的影响。您可能想知道每用户销售额、每笔交易销售额或其他指标之间是否存在差异。您需要将它们与市场上已有的竞争对手和替代产品进行比较。你可以看到这些选项是如何叠加的。
没有办法在一次调查中显示所有的问题。下一节将讨论如何缩小这些选项的范围。
第二个问题是恰当地传达以可消费的方式构成备选方案的特性。商品可以通过简单的描述(屏幕尺寸 48 英寸、50 英寸、55 英寸)来理解。然而,大多数产品需要更多的细节和背景——尤其是那些具有新/新奇功能的产品。有时候详细描述一下就够了。有时候你需要一个截屏,或者视频或者 gif。有时,在你询问受访者之前,你需要用中间内容来解释概念(即一元方法)。如果你不能给顾客提供一个现实的和可理解的选择,你就不会得到好的数据。
为了做好这项工作,你需要努力确保你的措辞是正确的,你的功能命名是清晰的,并且你有很好的资产来实现你的概念。这不是那种你可以忽略深思熟虑的内容和谨慎措辞的项目。
第三步:缩小选项并形成假设
实际上,你需要将所有可能的特性缩小到一个更小、更集中的列表中,只列出重要的和不同的特性。大多数产品特性,即使它们是必需的,也属于我称之为“预期填充物”的范畴。查看您的产品使用情况,询问销售工程师他们总是演示什么,与行业分析师交谈,询问一些客户为什么选择您而不是竞争对手——这些功能往往会真正影响选择。
您还应该尽早建立您的基准定价模型和指标。这样做的原因是,如果可以的话,减少一整类变量在你的选择练习中进行测试。如果有一个分类标准,可能很难偏离它,除非你是一个提供独特产品的新进入者。此外,您的价格指标需要与您的业务模式、销售方式以及企业的运营经济直接保持一致。例如,如果你有直销团队或高 CAC,免费增值或基于使用量的现收现付模式就不适合你,除非你准备采用批发业务模式和 GTM 改革,以适应新的目标市场和定价/包装。这对大多数公司来说都太难了。
一个好的经验法则是,尽可能直接根据产品的使用方式和客户从中获得的价值来确定价格。(但从客户的角度来看,它需要简单、事先可知并且可预测)。而且,理想情况下,它也是线性可扩展的——因此更大的客户、更大的使用量和更大的价值相应地支付更多。
如果您没有足够的背景数据来缩小功能并自己建立度量假设,并且没有保留,一个好的方法是对功能和价格模型运行 maxdiff 分析。
Maxdiff 是一种直接获得排序偏好的方法。您向回答者展示一个选项列表,并要求给出最佳/最差的评分,通常会对选项子集重复这一过程,以生成足够的回答来计算整个集合的排序偏好。这种方法避免了让人们在一个尺度上单独评价项目时“一切都是高优先级”的问题。因此,maxdiff 是区分重要事物和不重要事物的特别有用的方法。像这样做一个快速的预先调查会增加你的整体定价/包装工作的时间和成本,但是洞察力和信心可能会抵消它。
你的准备工作的结果应该是一组,比如说 5 个不同的包装选项,每个选项由 3-6 个你的关键功能的组合组成,围绕一个价格指标构建。该填充符可以被卷成一个“基本能力”描述,描述所有产品做什么或者从分析中一起删除。假设有另外 3 个竞争对手的报价要测试,并且您想在 3 个不同的价位测试每一个,这就给了您 25 个要担心的包(24 + a“不要选择这些”)。这个数字是可以控制的。
第四步:构建并运行分析
要进行基于选择的联合或最大差异调查,您需要一个能够胜任这项任务的调查工具。如果你有一个复杂的产品,你需要一些能给你提供格式和媒体选项的东西来以一种真实的方式展示项目。您还需要一个工具,能够在您的调查受访者中智能地分配选择范围,并且能够显示相同套餐的不同价位。有一些专门从事离散选择类型调查的工具,也允许产品经理、数据科学家、金融分析师和营销人员直接做这项工作。如果你了解你的业务,并想了解和解释结果,这是适合你的路线。如果没有,你当然可以外包给咨询或市场研究公司,他们有自己的工具,可以为你做这项工作。
这个分析的核心应该是基于选择的联合**。这是一种离散选择练习,您向调查受访者提供一组选项,通常每次 4 或 5 个,并让他们选择最有可能购买的一个。这个练习重复几次,显示不同价位的不同选项。**
一旦掌握了调查数据,建模和分析就有 4 个关键领域:
Preferences and share :最重要的数据是包选择频率(选择的时间百分比)。这将告诉你哪些包装在市场上有最强和最弱的需求——假设你的调查对象是市场的代表。所有软件包(您的和竞争对手的)的选择频率分布提供了一种市场份额视图,如果所有软件包都在市场上,将会发生什么。由于你测试的可能是你自己的替代品,而不是最终提供给客户的,所以这不是一个精确的衡量标准(但是像商业中的大多数事情一样,指示性/方向性数据通常就足够了)。
来源:pricekit.io
价格敏感度。数据还应该告诉你一个包的价格变化如何影响该包的选择频率。我通常使用 3 个价格点——基线和基线的+/- 50%。这造成了足够大的差异,因此它将显示在偏好数据中,但仍然是市场现实的价格。
由此,您可以计算每个包的平均选择价格,并由此计算价格调整频率(即频率 x 平均选择价格)。将各种套餐的原始频率视为客户市场份额的衡量标准,将价格调整后的频率视为收入市场份额的衡量标准。
此外,我还关注我所谓的价格敏感度指数。我的计算方法是,查看展示给回答者的每组选项,然后用所选价格点除以所展示的套餐的平均价格点。当在每个集合中绘制这些价格敏感度指标时:如果它们聚集在 1 附近,这就是如果价格完全不影响决策时你所期望的。大于 1 的选项群是那些选择更贵的套餐的人,小于 1 意味着他们倾向于选择更便宜的选项。这将告诉你顾客在多大程度上被便宜的选择所吸引(或者不是)。请注意,这是假设所有的包都在相同的大致价格范围内。如果有一家公司的价格是其他公司的 10 倍,结果就会有偏差。
效用(又名部分价值):一些联合调查软件包首先被设计用来计算特征效用。这种分析量化了每个单独特性对整个产品包的价值水平。例如,这种分析可以告诉您,“单点登录功能”的感知价值比“双因素身份认证功能”高 30%。
我发现效用值更有助于解释为什么一些包比其他的好,而不是首先作为构建包的指南。在解释为什么客户可能会从入门级产品选择或升级到高级/更贵的产品时,尤其如此。但是——小心调查软件,它迫使你仅仅为了获得统计上有效和全面的功能效用而构建简单化或人为限制的选择。依我看,这不是这项工作的主要目的。
细分:查看每个回答者选择的最优方案,你可以将他们分成相似的行为细分(例如,使用 K-means )。找到不同但相当大的群体,他们喜欢不同的套餐和不同的价位,这是你整个 SKU 系列的基础。
您不是在寻找一个合适的套餐和价格,而是寻找一个小的套餐和价位集合—每个都针对有价值的细分市场进行了优化。
您的调查中的人口统计问题应设计为允许您估计集群的真实市场规模,并使您能够将这些偏好反馈到现有客户群中(例如,用于追加销售和续订评分)。在调查中添加态度和认知问题,可用于告知围绕每个集群及其独特包装的营销信息。
到目前为止所做的分析旨在获得试图模拟现实世界行为的数据。你应该了解(a)什么功能重要,什么型号更受欢迎,( b)什么套餐需求最大,( c)这些套餐的价格敏感度如何,( d)你的套餐相对于竞争对手的表现如何,( e)这些项目在不同客户群中有何不同。
但是没有算法可以返回唯一确定的最佳答案。这就是你的判断和实际情况发挥作用的地方。最重要的指导应该是:你的报价需要适销对路。
这可能意味着您将特性聚合到更高的类别中,并将它们作为一个组附加到一个或另一个包中。我会避免你说这样的话:“这个 SKU 有很好的(安全、分析、报告、集成等),但另一个 SKU 有更好的(这些)”。如果你在你的价值主张中引入了很多细微差别,你已经给你的营销和销售团队造成了很大的伤害。一般来说,我认为最好保持价值主张的强势,即使牺牲一些(潜在的)功能驱动的追加销售活动。
一旦模型和定价到位,实现工作就开始了。根据变化的程度,这可能是一个简单的更新,也可能是一个公司范围内的重大举措。
你将不得不修改你的价目表,你的账单应用程序,并把这个模型应用到你的标准商业条款中。通常,由于过去的条款协议,您可能必须在维护旧 SKU 的同时维护旧 SKU,直到客户可以迁移。对于你在第一步中发现的条款与新模式或价格点有冲突的客户,你必须制定一个沟通和行动计划。
如果有大的结构变化,你需要更新营销材料、支持内容、销售宣传材料、演示、价目表、培训合作伙伴等等。这最好与主要新功能或新产品功能的引入或新的消息传递/定位/营销活动一起进行,以便所有这些变化可以同时进行,并相互加强。
从开始到结束,这项工作可能需要几个星期到几个月。一旦你有了数据,你就知道了方法,为新产品发布或年度战略计划节奏更新数据的工作就少多了。这是在你的公司内部建立一种能力,而不仅仅是做一个一次性的项目。你投入在正确定价和包装上的精力可能比你能做的几乎任何事情都有更大的回报。
定价研究——Van westen dorp 的 Python 价格敏感度表
Python 中的测量设计和分析
调查
范·韦斯滕多尔普的价格敏感度测量仪是用来调查顾客价格偏好的。价格敏感度量表调查包括四个问题,要求每个调查参与者提供产品的四个价格点,当产品是:
- 太便宜(即太便宜)
- 便宜(即便宜货)
- 昂贵的
- 太贵了
这四个问题的示例如下所示:
资料来源:Qualtrics
数据
假设有 20 名参与者填写了调查问卷。让我们首先导入所需的库,并从这 20 个参与者那里读入数据。
数据框的前五行如下所示:
分析
这里我写了一个函数,计算四个问题(太便宜,便宜,贵,太贵)的累计百分比,绘制四条线,并报告最优价格和可接受的价格范围。
这是我们运行price_sensitivity_meter(df)
时的输出。x 轴显示价格点,y 轴显示价格累计频率的百分比。如果你想让剧情更流畅,可以设置interpolate=True
。在我们的函数中,interpolate
的默认值是 False。
主要结果是:
- 最优价格点(OPP)
“太便宜”和“太贵”的交集。在我们的例子中,最优价格是 300 美元。
- 可接受的价格范围
下界(又名。边际便宜点或 PMC)是“太便宜”和“太贵”的交集。上限(又名。边际成本点或 PME。)是“便宜”和“太贵”的交集。在我们的例子中,可接受的价格范围是 280 美元到 350 美元。
用质量验证结果
为了确保我们的结果是正确的,我用 Qualtrics 验证了我的结果。使用相同的数据,Qualtrics 生成以下报告:
资料来源:Qualtrics
最优价格与我们的结果相同。两者都显示 300 美元是最佳价格。可接受的价格范围不同,但非常接近。我们得到了 280 到 350 美元,而 Qualtrics 报告了 280.1 到 350.0 美元。(我不确定为什么 Qualtrics 在剧情中每一步都有一个斜率。台阶之间的垂直线不直。我怀疑数据量大了,竖线会变直。)
现在,您可以用 Python 设计自己的价格敏感度测量仪调查并分析结果。尽情享受吧!
参考
[## Van Westendorp 价格敏感度量表研究| Qualtrics
许可证包含的产品体验 Van Westendorp 定价模型 Van Westendorp 方法使用一系列…
www.qualtrics.com](https://www.qualtrics.com/marketplace/vanwesterndorp-pricing-sensitivity-study/)
在 Jupyter 笔记本中开发可再生神经网络的初级读本
探索美国宇航局的涡轮风扇数据集
这不仅仅是为迭代开发播下种子
在准备我的下一篇文章时,在训练神经网络(NN)之前,我在一个 Jupyter 笔记本上尝试了不同的预处理设置。在尝试了一些设置后,我决定恢复到以前的设置,因为它的性能更好。然而,在执行细胞时,神经网络的结果并不相同,即使我已经设置了种子…
因此,在 Jupyter Notebook 中开发 NNs 时,我开始寻求可重复的结果。专门针对您在处理单元和训练单元之间来回切换的场景。
可重复和可比较的结果引物
开发神经网络模型时,比较模型性能并验证改进模型的尝试的有效性非常重要。编译 NN 为网络中的每个连接生成/初始化随机权重。这些权重在训练期间更新。然而,随机初始化的影响大到足以影响模型性能到小数点后第十位,妨碍了模型的比较。因此,我们需要“控制”这种随机初始化,以便在迭代、预处理或特征工程之间来回切换时,结果仍然具有可比性。
为了实现这一点,我发现我需要在两个层面上控制这种随机性:
- 会话间再现性(始终适用)—您需要设置各种随机样本生成器的种子,以确保您在笔记本中训练的神经网络在每次启动笔记本时返回相同的结果(无论是在不同的一天还是在重新启动内核后)。甚至必须在导入其他包之前【1,2】设置原生 Python 种子。这将确保每次执行代码时生成的权重是相同的,例如,第一组随机生成的值将总是相同的,第二组随机生成的值将总是相同的,等等。
- 在会话比较中(特别适用于笔记本电脑)—在编译 NN(首次抽取随机权重)后,您可能想要尝试不同的预处理方法。你必须在神经网络编译后保存权重,并在(重新)训练前重新加载这些权重。否则,重新编译你的神经网络会根据你设置的种子生成新的权重(随机权重的第二次抽取)。将其与没有替换的操纵概率进行比较[3]。你的第一次抽签将总是产生相同的结果,你的第二次抽签也将总是产生相同的结果,但它不等于第一次抽签。因此,您必须保存并恢复首次初始化的权重。此外,您还必须重置优化器,以避免从它的最后状态继续学习。
这两个步骤对于获得可比较和可重现的结果至关重要。
最后一点:当设置你的随机种子时,它可能会产生可怕的初始权重。因此,建议尝试一些随机的种子,以确保你不会吃亏。
让我们看看这是什么样子的
示例实现
对于这个示例实现,我将使用 NASAs CMAPSS 关于涡扇发动机退化的数据集。目标是预测剩余使用寿命。
首先,导入库,并为 python 环境、其内置随机库 numpy 和 tensorflow 设置随机种子。
接下来,您读入数据。
train.head()的结果
我就走个捷径,跳过解释几个数据准备步骤。你可以通过底部的链接查看完整的代码。让我们定义一个简单的 MLP。
定义模型后,我们编译它。通过编译模型,权重根据我们在开始时设置的种子进行初始化。这是我们第一次在时段再现性之间随机抽取重量和支持*。此外,初始化的权重被保存以供以后(重新)使用。保存权重是在会话比较中支持的重要步骤。*
对于下面的代码块,最重要的一行是第 1 行。Alpha 值决定了平滑滤波器的强度。
因为我是在笔记本上工作,所以在数据准备单元格和模型拟合单元格之间来来回回。出于这个原因,我在拟合之前重新编译模型以重置优化器,否则它会继续从它的最后状态学习。但是,重新编译模型会生成一组新的权重(随机权重的第二次抽取)。因此,我重新加载初始权重,以获得可比较和可重复的结果。
我现在可以试验不同的过滤强度来了解它的效果。一旦超过了α的值,我就可以回到之前的值,确保得到相同的结果。
alpha = 1.0, train_rmse = 18.00
alpha = 0.4, train_rmse = 17.43
alpha = 0.2, train_rmse = 17.82
alpha = 0.4, train_rmse = 17.43!
不幸的是,平滑 FD001 的数据会使测试集上的预测变得更糟,但这与这个玩具示例无关。要点是:通过实现这两个步骤,您可以在会话内和会话间获得可重复的结果。您可以在预处理和训练单元格之间来回切换,尝试不同的设置。当您想要使用以前的设置时,您可以确信结果是相同的。此外,当您第二天返回(或重启内核)时,您可以确信结果仍然是一样的。
希望这能让你的生活更轻松!完整的代码,请查看我的 github 页面这里。我要感谢迈克尔·格罗贝和杰弗里·卢普斯对我的文章的评论。
对于关注我的系列“探索 NASA 的涡扇数据集”的读者来说,这里开发的初始 NN(没有平滑或特征选择)已经在 FD001 数据集上具有 18.50 的测试 RMSE。比基线模型提高了 42%,比之前最好的模型有了很大的提高,后者是一个 RMSE 为 20.54 的支持向量回归模型(在基线上总体提高了 35.7%)。在我的下一篇文章中,我们将深入研究 FD002,其中涡扇发动机在不同的运行条件下运行,指数平滑确实具有有益的效果。
参考资料:
【1】https://keras . io/getting _ started/FAQ/# how-can-I-obtain-reproducible-results-using-keras-during-development
【2】https://stack overflow . com/questions/32419510/how-to-get-reproducible-results-in-keras/59076062 # 59076062
【3
主成分分析
(2021 年 9 月更新)无监督学习中最重要的算法之一背后的逐步直觉、数学原理和 python 代码片段
这个动画展示了当旋转方向到达两个特殊方向时,投影点(A…E)的协方差矩阵如何对角化(是下面的特征分解方程的解)。为了验证这一点,当方向与橙色虚线(第一主成分)或第二粉红色虚线(第二主成分)重叠时,请减慢视频速度或尝试暂停视频。您还会注意到矩阵的对角线元素达到了它们的最大值(最大方差)。(视频由作者使用 Geogebra6 sw 制作)
这个公式叫做特征分解方程。最初的工作始于 1800 年底,但由于(个人)计算能力的进步,直到最近才在大数据分析中得到实际应用。
大家好,我是意大利米兰的 Andrea Grianti。在阅读了许多关于这个主题的书籍和论文后,我写了这篇文章来分享我的想法。这不是一本教科书,而是进一步理解该主题的起点。因为后面的数学/代数很难,我把它分成了四部分:
- PCA 直觉
- 数学/代数(简单)
- 数学/代数(难)
- Python 片段
1。PCA 直觉
如果您有一个包含数千/数百万个观察值(行)和数百个不同变量(列)的大型数据集,首要目标之一是验证是否有可能简化和缩减数据集,以便于对原始数据的一个小得多的子集进行分析。
直接消除变量是显而易见的方法,但它显然会影响数据集的信息内容。过多或错误的剔除会使你的数据集变得无用,过少的剔除会使数据集变得庞大和难以分析。
术语’信息’是一个非常通用的主题,很难定义它。这取决于数据集。对我来说,一个数据集可能包含信息,而对其他人来说,可能什么也不包含,反之亦然。
我们可以尝试使用“信息内容”这样的概念来定义数据集中的信息量,这是一个与特定值发生的概率相关的概念,在数据集变量的所有可能值中。
根据这一概念,变量 x 的可能结果越多,预测其值的概率越低,因此信息含量越高。
这是我们要牢记的第一个假设:更高的方差= >更高的信息含量。只有上下文和我们的数据能告诉我们这个假设是否成立。
在关注高方差数据概念之前我们必须处理一个第二个假设: 变量之间的相关性是数据冗余的一种形式 。如果两个变量之间有明确定义的关系,例如以度为单位的角度和以弧度为单位的角度,或者以厘格或法拉为单位的温度,那么这两个变量中的一个是无用的,可以直接从数据集中消除。相反,当相关性不太明确时,不推荐直接排除,可以通过第三个假设尝试管理相关性证据的缺乏。
第三个假设是: 我们假设无论变量之间的相关性是什么,这都是线性的 。
概括一下,我们假设:
- 方差与信息内容有关,应该最大化
- 冗余变量和变量之间的高度相关性是一种应该最小化的噪声形式
- 变量之间的相关性是线性的
这两个假设为什么重要?因为要降低数据集的维度,我们应该评估每个变量对数据集总体方差(=信息)的贡献,以便选择贡献最大的变量,丢弃贡献最小的变量。
这种运算的战场是协方差矩阵或它的兄弟相关矩阵。因为相关矩阵通过绑定两个变量之间的简单协方差和相关性的相同关系严格绑定到协方差矩阵,所以我将使用协方差矩阵,因为选择与后面的数学原理的理解无关。
在任何情况下,对于那些喜欢简单刷新协方差和相关性概念以便理解这里的关系的人来说,它是:
我们知道,对于两个变量,协方差公式为(如果我们的数据集是从总体中抽取的数据样本):
不集中在平均样本数据的协方差(来源:作者)
协方差代表一种离差度量,它包括两个变量之间的线性“同步性的概念,这两个变量与它们各自的均值相关。
也就是说,对于每个点,测量一个点的 x 坐标>与所有 x 点的平均值>、之间的差值如何与同步的、、<与同一点的 y 坐标>和<之间的差值,然后对所有 y 点的平均值>求平均值。****
画在笛卡尔坐标系上的点。注意红色的平均点。还要注意,当重新定义轴时,将(0,0)点设置在平均值上,离散度不受影响。这实际上简化了理解协方差和相关性之间关系的推理。(来源:作者)
有趣的是,在上面的示例图片中,平均值的左右两边有相同数量的点(对于维度 x),平均值的上下两边也有相同数量的点(对于维度 y)。
这个简单的例子让你明白了协方差对于 符号的重要性。当结果给你一个正号或负号时,它给你一个关于象限的概念,其中同步的方向在哪里。还要注意,具有相同的符号并不能告诉你任何关于方向的斜率的信息,只能告诉你方向所在的象限。
如果点不是从左下到右上,而是从左上到右下(沿平均值的水平线反映),协方差将是相同的值,但前面有一个负号。
协方差如何绑定到相关性
如果你暂时不考虑上述协方差公式中的常数,等式的其余部分就是乘积与差值的和,这应该让你想起(至少对于乘积的和)代数中的点积概念。
为了使它看起来像两个向量之间的点积,我们可以将数据点居中(从每个变量值中减去该变量的平均值),并且我们两个的平均值都= 0,而方差保持不变(因为移动所有点不会改变点之间的距离,并且离差保持不变)。
在这种情况下,中心数据的协方差公式简化为:
以平均数据为中心的的协方差公式(来源:作者)
其中包含点积的定义我们知道是:
点积(来源:作者)
但是我们从几何学中知道,点积也可以写成:
点积:几何版(来源:作者)
考虑到当数据居中时,方差公式也简化为:
居中变量 X 和 y 的方差公式。
中心数据相对于向量 X 和 Y 长度的标准偏差
对于中心数据,协方差公式用标准差和向量 X 和 Y 之间的夹角余弦表示,即相关系数。(来源:作者)
两个中心变量情况下协方差和相关性(rho)之间的关系。当变量为 n 时,相同概念可以根据矩阵进行转置(来源:作者)
两个中心变量的简单协方差和相关性之间的关系:σ是它们的标准偏差,ρ是相关系数。在相关性为零的极端情况下,协方差为零。另一方面,当相关性最大时,ρ= 1 或ρ=-1,协方差是每个变量的标准偏差的乘积。
从协方差到协方差矩阵
考虑到上面的协方差公式仅适用于 2 个变量,我们可以将协方差矩阵视为数据集中所有变量之间所有协方差的“大图”。
协方差矩阵(在文献中也称为方差/协方差矩阵)(来源:作者)
当我们计算原始数据集(我们称原始数据集 X 为许多列向量 X1,X2…Xn 的集合)的方差/协方差矩阵时,我们会看到沿对角线的方差,但我们也会看到非对角线元素中的联合协方差,这是一个变量与其他变量之间的**‘同步性’**大小的(难以解释)度量。
因为我们当然不能为了消除变量之间的相关性而修改我们的原始数据 X(除非我们完全删除一个变量,这总是可能的,但有风险,因为我们可能不情愿地删除重要信息), 我们可以尝试找到一种方法,将 X“转换”成不同的数据集 Y,该数据集 Y 具有与 X 相关联的 special (德语中 special =eigen……)但是以这样的方式构建,Y(Y1,Y2…Yn)的新变量将具有不同的协方差矩阵 Cy,其中这些变量的方差将与其他变量的区间(相关性)隔离开来(= >协方差= 0)。
这是我们想降落的地方。具有对角化的新协方差矩阵 Cy。意味着我们要解决一个问题,找到一个新的数据集 Y,绑定到原始数据集 X(我们将看到绑定是如何工作的),其中 Y 的所有新变量在它们之间是不相关的。这样,Cy 矩阵中的协方差将为零,方差被隔离在对角线中。这将清楚地允许我们将数据集的总方差定义为对角线元素的总和(Trace)(来源:作者)。
这个操作就是特征分解方程的魔力。通过求解该方程,我们将找到一种方法来转换原始 X 数据集,从而将协方差矩阵 Cx 转换为新的数据集 Y,并使用一种称为 B 的特殊转换矩阵将对角化协方差矩阵 Cy 转换为新的数据集 Y。B 将是我们用来从 X 到 Y 来回转换的方式,反之亦然。
假设我们能够解出那个特征分解方程 …那又怎样?如果使用原始数据 X,我不得不使用 Cx 来解决无法根据相关性隔离单个变量对总体方差的贡献的问题,现在,通过求解该方程,我可以使用一个新的 Y 数据集,其协方差矩阵为 Cy,并且是对角的,因此 Y 的每个变量的方差贡献都有明确的定义,并且不受联合交互/相关性的影响。这样,我们就可以根据它们的方差值来决定在哪里切割 Y 数据集,以根据它们相对于 Cy 中的方差值总和的贡献权重来保留最重要的 Y 变量的子集。
当我们用 Y 而不是 X 工作时,要付出什么样的代价?因为新的 Y 数据集的变量(我们将会看到)是原始 X 数据的线性组合,它们的含义是不确定的。因此,要给 y 变量赋予上下文意义,就需要一定的敏锐度,让它们从实用的角度“说话”。当然,事情有点复杂,关于如何命名新发现的变量,我们在这里跳过了许多细节,但要框定问题和解决方案,就这样。
在任何情况下,要从 X 到 Y(以及从 Cx 到 Cy),我们需要理解我们可以对原始数据进行的转换的逻辑(通过一个仍待定义的 B 矩阵)。所以我们需要讨论(研究)一下投影和线性变换,因为它们是严格联系的。
2。PCA 后面的数学/代数(更容易)
投影概念:简而言之,我们在图表中绘制的内容取决于我们用来表示数据的坐标系统。艺术中的透视思考:它是现实生活中的投影按照特定的规则变换。
例如,在三维中,我们使用笛卡尔系统,其由正交向量(长度=1 的正交/垂直向量)的矩阵(E)表示。
卡茨安基地(来源:作者)
例如,当我们有一个数据集 X(测量值是三维的:列向量 x1、x2、x3)时,为了计算变量 x1 的方差,我们应用通常的方差公式。但是我们实际做的是通过点积将 X 的每个数据点的坐标投影到 E 的 3 个方向上。
因此,一般来说,我们有**X \u E = X。**这个投影操作在笛卡尔坐标系中是“透明的”,我们甚至没有意识到要进行投影,因为它在我们的头脑中是“自动的”。
但是,如果我们决定离开笛卡尔系统,我们可以建立一组类似的正交 向量,它们定义了由 b1、b2、b3(而不是由 e1、e2、e3 组成的 E)组成的新基 B,如下所示:
(来源:作者)
如果我们点乘(例如)数据集 X 的一个通用行向量,该向量包含单个数据点的坐标(如果您喜欢,可以是单个观察值):
通用数据行,(1,3)表示 1 行 3 列
对于 B (=3 行,3 列),我们获得新的行向量(=1 行,3 列):
Y 中相应的新行,使用 X 的一般行,用 B 转换
其中该向量的每个元素具有新的“度量”/“坐标”,由下式给出:
这里的三个方程是上图易方程的展开。因此,在维度上,形状仍然是(1 行,3 列),但行中的每个元素都是 xi 和 B 的线性组合。
在代数术语中,一般来说,在我们的例子中,我们简单地用以下公式改变基:
首先是矩阵形式。在我们的例子中,Y 由 3 列向量(y1,y2,y3,m 行)组成,B 由 3 列向量(b1,b2,b3,3 行)组成。所以 Y 是(m,n) = X (m,n)。B (n,n)。当找到 B 的解时,这 3 个方程将代表所谓的“主分量”。
其中 X 是基 E 中的原始数据矩阵(m 行乘 n 列),B 是新的标准正交基(n 乘 n),Y (m 乘 n)是在新基 B 中测量值被转置的结果新数据矩阵。
注意,y1、y2、y3 是新变量,其含义与 x1、x2、x3 无关,但是它们是新的,并且新含义必须在语义上定义,因为它们是分别由 B 的 b1、b2、b3 向量的相应分量加权的所有X 变量的线性组合。那就是:
- y1 由向量 b1=[b11,b12,b13]加权的 X 的每个变量组成
- y2 由向量 b2=[b21,b22,b23]加权的 X 的每个变量组成
- y3 由向量 b3=[b31,b32,b33]加权的 X 的每个变量组成。
所以如果我们找到 B 的解,我们可以用 y 的新变量。但是,我们应该使用什么 B 来达到我们的目标,找到 Y 与 Cy 对角矩阵?
b 可以逐步建立如果我们知道它存在一个唯一的方向,使沿该方向投影的 X 的方差最大化。我们可以沿着笛卡尔坐标系的 X 轴投影数据集 X,但在这种情况下,方差可能没有最大化。因此,我们需要找到那个特定的方向。一旦我们找到了最大方差的方向和值,我们就知道找到了第一个本征向量和第一个本征值。一般来说,我们可以说已经找到了第一主分量 PC1: y1=X.b1,其强度是特征值 1(λ1)。
为了根据方差解释了多少来评估 y1,在这个阶段,可以将λ1 除以协方差矩阵 Cx 的对角元素之和,因为总方差之和不会从 Cx 变为 Cy。
然后,利用相同的原理,我们可以找到第二方向B2(第二特征向量)作为最大化 X 沿着单位长度且正交于 b1 的第二方向的所有可能投影之间的方差(第二特征向量)的方向。当发现这是第二个主成分时:PC2: y2=X.b2
然后第三方向 b3 使 X 沿第三方向的方差最大化,该第三方向再次由单位向量定义,该单位向量也必须与 b2 和 b1 都正交。当发现这是第三个主成分:PC3: y3=X.b3
在这些迭代结束时,我们将建立一个特殊的 B=b1*,b2*,b3*,一个新的数据集 Y =[y1=X.b1,y2=X.b2,y3=X.b3]由 3 个“主分量”组成,按方差强度排序,一个特殊的特征值向量λs =(λ1,λ2,λ3),其中每个λ是每个 y1,y2,y3 的方差。
当然,迭代可以进行到 n 维。最后,根据总方差和(λ1,λ2,λ3)上的每个方差的相应权重,我们有所有元素来减少 Y。希望用 Y 的几个变量,我们可以“解释”总方差的大部分,我们可以减少变量的数量,但不能减少它包含的信息量…
3.PCA 后面的数学/代数(难)
以上是解释手动迭代程序以求解特征分解方程的冗长部分。现在我们来看看从开始到本征分解方程的解的整个数学过程。
我们称 X 为原始数据集(m 行 x n 列),其中列中的每个变量已经围绕它们各自的平均值“居中”, Cx 是 X 的协方差矩阵,b1 是 n 个元素的未知向量和未来 B 变换矩阵的第一列,y1 是 X 沿未知向量 b1 的投影:y1=X.b1
- Y 的第一个新变量的方差我们称之为 y1:
- 为了最大化 Var(y1)并找到相应的第一方向,我们使用带有拉格朗日乘数的拉普拉斯方程。符号是梯度,f 是最大化的函数,在我们的情况下是 y1 的方差 X.b1,g 是我们设置约束的函数,b 向量的长度必须是 1:
图一
- f 对 B1(n 分量的未知向量)的偏导数为:
图 2 如果你想知道为什么你可以用一个简单的未知向量 B1(b11,b12),一个给定的 simmetric Cx like ([4,2][2,1]),以及未知项 B1(b11,b12)。你将有一个二次多项式和两个偏导数(b11 和 b12)组成一个矩阵,它是 Cx 乘以 b1 的 2 倍
- g 对 b1 的偏导数是:
图 3
- 本征分解方程变为:
图 4
- 为了找到某个 b1 向量<> 0,我们必须找到括号中的项的行列式,并将其设置为 0。只有在这种情况下,b1 的 n 个未知分量中的 n 个方程组才有非平凡解:
图 5
- 这个含有 n 个未知数的 n 方程系统的解可能会给出(我保持简单,因为可能有例外)n 个不同的λ。这些λ代表 X 的投影点沿待定方向的 n 个不同方差。
- 现在我们应该指定找到的 lambdas(姑且称之为 lambda1)的最大值,并通过求解由下式给出的系统来找到 b1:
图 6
- 在我们找到 b1 之后,我们取λ2,并且我们在上面的等式中再次求解该系统以找到 b2,等等,直到对于所有的λ,我们有了所有的 b 向量。
- 正如你所想象的,计算对于 3 个变量来说是非常长的,这就是为什么我们需要 Python 和数字算法来完成这些脏活。
- 最后我们将(例外情况除外)得到:作为 b1,b2,b3 … bn 列向量序列的特征向量 B 矩阵;特征值向量 L(ambda)表示 Y(作为主分量)的每个 Y 的方差或特征向量的幅度。
4.Python 片段:
这里有很多例子和库,但在这里我想用 Python 来展示,只需用 numpy 代码片段,你就可以快速尝试你的样本小数据集,并通过查看结果来理解发生了什么。我跳过了 print 语句,因为您可以在控制台上工作并自己检查变量的内容。甚至图表都被省略了,但是你可以用 matplotlib 或者类似的工具做你想做的事情。这里的重点不是构建一个应用程序,而是展示对逻辑的理解。
import numpy as np
import pandas as pdpd.options.display.max_columns = 200
pd.options.display.width=200
pd.options.display.max_rows=200def fcenter(X): #function to center data
data_mean=np.mean(X,axis=0) #calc mean by column
X=X-data_mean #centered data
return Xdef fcov(X): #function to find the covariance matrix
covx=(X.T.dot(X))/X.shape[0] #calc cov matrix of X
#alternative to: covx=np.cov(X,rowvar=False,ddof=0)
return covxdef feigen_decomp(Cx): #this is the core to solve the eigen decomposition equation
eigval,eigvec=np.linalg.eig(Cx) #solve eigen equation
return eigval,eigvecX=np.array([[1,1,6],[4,2,9],[2,-2,3],[-3,3,1],[-5,1,7]]) #some data
Xc=fcenter(X) #centered data
#----------------------
#this shows that variance does not change when centering data
vX=np.var(X,axis=0) #variance of columns of X
vXc=np.var(Xc,axis=0) #variance of columns of Xc
#----------------------
Cx=fcov(Xc) #Cx=covariance of Xc
L,B=feigen_decomp(Cx) #L=eigenvalues vector, B=eigenvectors matrix
Lw=L/L.sum() #weight in % of every PC (not cumulative)
Y=Xc.dot(B) #Y=Principal Components matrix (in columns = y
scores)
Cy=fcov(Y) #shows that diagonal of covariance matrix of Y
coincides con L
#----------------------
#Loadings analysis
Loadings=np.sqrt(L)*B #see comments
Loadings_sq=Loadings**2 #see comments
Loadings_sq.sum(axis=0) #see comments
Loadings_sq.sum(axis=1) #see comments
关于加载的一个注意事项:当您想要了解结果时,加载是有用的。回想一下,Y 的每个新变量都是所有 X 变量的线性组合。负载矩阵垂直表示每个 PC 的方差有多少是由 X 的每个变量 X 解释的:事实上,每列的总和等于 L,水平表示每个 PC 解释了每个 X 的方差有多少:事实上,行的总和等于 X 的方差。如果你想的话,可以去看看。最后比这个还好玩:-)。
对于试图定义与对主成分的值(分数)有贡献的最相关的 x 的名称相关的 PCs 的名称,加载是重要的。
…跟我来
大家好,我叫 Andrea Grianti,我的职业生涯是在 IT 和数据仓库方面度过的,但后来我对数据科学和分析主题越来越有热情。
请考虑跟随我,以使我达到追随者数量的阈值,以便 Medium platform 将我纳入他们的合作伙伴计划。