使用 Spark 预测客户流失
Image by author
在面向客户的业务中,预测客户流失是一个具有挑战性且常见的问题。由于预测通常是根据大量的用户活动日志做出的,我们需要一种分布式的方法来有效地处理大型数据集,而不必一次将它放入我们的内存中。
该项目将探索如何使用 Spark 建立客户流失预测模型,包括以下步骤:
- 探索和操作我们的数据集
- 为我们的问题设计相关功能
- 通过抽样流失将数据分为训练集和测试集
- 使用 Spark 的基于数据帧的 MLlib 构建二进制分类器模型
- 使用 Spark 的 ML 管道和stratifiedcrossfvalidator选择并微调最终模型
关于数据集
我们将使用名为 Sparkify 的音乐流媒体服务的用户事件日志(持续时间约为 2 个月)作为我们的数据集。通过这些日志,我们可以预测该用户是更有可能留下来还是更有可能流失。
A log(row) will be appended whenever a user interacts with the service: play the next song, add a song to playlist, thumb up/down a song, etc.
这里,我们仅使用一个数据子集(128MB)来训练我们的带有本地 Spark 的流失预测模型。为了使用完整数据集(12GB)进行模型训练,您可能需要在云服务上部署一个集群。
加载和清理数据
加载数据
user_log = spark.read.json('mini_sparkify_event_data.json')
user_log.count()
# 286500
user_log.printSchema()
# root
|-- artist: string (nullable = true)
|-- auth: string (nullable = true)
|-- firstName: string (nullable = true)
|-- gender: string (nullable = true)
|-- itemInSession: long (nullable = true)
|-- lastName: string (nullable = true)
|-- length: double (nullable = true)
|-- level: string (nullable = true)
|-- location: string (nullable = true)
|-- method: string (nullable = true)
|-- page: string (nullable = true)
|-- registration: long (nullable = true)
|-- sessionId: long (nullable = true)
|-- song: string (nullable = true)
|-- status: long (nullable = true)
|-- ts: long (nullable = true)
|-- userAgent: string (nullable = true)
|-- userId: string (nullable = true)
干净的数据
在我们的数据集中检查缺失/null/空值之后,我们发现了一些事实:
- 所有列中没有缺失(NaN)值。
- 在与用户信息和歌曲信息相关的列中发现空值。
- 不是
NextSong
的页面的artist
、length
和song
将为空值。
- 仅在列
userId
中发现空值。
- 空
userId
的用户是没有注册登录的用户。
由于空userId
的日志无法帮助我们识别后面的用户,所以我们无法对他们进行预测。所以我们把它们从分析中去掉了。
# drop rows with empty userId
df = user_log.filter(F.col('userId')!='')
df.count()
# 278154
探索性数据分析
定义流失
我们将使用Cancellation Confirmation
事件来定义客户流失。搅动的用户将在列churn
中有一个1
,这是我们模型的标签列。
flag_churn = F.udf(lambda x: 1 if x == 'Cancellation Confirmation' else 0, T.IntegerType())
df = df.withColumn('churn', flag_churn('page'))
转换数据
在这一步中,我们将把原始数据集(每个日志一行)转换成具有用户级信息或统计数据的数据集(每个用户一行)。在进行聚合之前,我们将首先执行以下步骤:
- 将时间戳(毫秒)转换为日期时间
ts
和registration
列是使用以毫秒为单位的时间戳记录的,非常难以读取,因此我们将它们转换为日期时间对象,并保存为两个新列:dt
和reg_dt
。 - 推断每个用户的观察开始日期
a .对于reg_dt
比min_dt
(整个分析的开始日期)早的用户:使用min_dt
作为obs_start
(观察开始日期)列的值;b*。对于reg_dt``min_dt
first_dt
(用户第一个日志的dt
)之间有的用户:使用reg_dt
作为obs_start
列的值;
c .对于reg_dt
*晚于first_dt
的用户:使用first_dt
作为obs_start
列的值。
奇怪的是注册日期在第一次登录日期之后,但有些用户(如 userId=154)会发生这种情况。 - 推断每个用户的观察结束日期
a .对于流失用户,使用他们最后的日志dt
(他们流失的日期)作为列obs_end
(观察结束日期)的值;对于非流失用户,使用max_dt
(整个分析的结束日期)作为obs_end
栏的值。 - 获取每个用户的最后订阅级别
将用户的最后订阅级别保存到新列last_level
。
然后,我们按用户聚合所有必需的列:
在聚合之后,我们还提取了一些与事件相关的基于时长的特征。
并添加一些会话级特性。
最后,我们只选择这些对于以后的探索和分析是必要的列。
*root
|-- userId: string (nullable = true)
|-- churn: integer (nullable = true)
|-- gender: string (nullable = true)
|-- last_level: string (nullable = true)
|-- sum_length: double (nullable = true)
|-- n_session: long (nullable = false)
|-- reg_days: integer (nullable = true)
|-- obs_hours: double (nullable = true)
|-- n_act_per_hour: double (nullable = true)
|-- n_about_per_hour: double (nullable = true)
|-- n_addFriend_per_hour: double (nullable = true)
|-- n_addToPlaylist_per_hour: double (nullable = true)
|-- n_cancel: long (nullable = true)
|-- n_downgrade_per_hour: double (nullable = true)
|-- n_error_per_hour: double (nullable = true)
|-- n_help_per_hour: double (nullable = true)
|-- n_home_per_hour: double (nullable = true)
|-- n_logout_per_hour: double (nullable = true)
|-- n_song_per_hour: double (nullable = true)
|-- n_rollAdvert_per_hour: double (nullable = true)
|-- n_saveSettings_per_hour: double (nullable = true)
|-- n_settings_per_hour: double (nullable = true)
|-- n_submitDowngrade_per_hour: double (nullable = true)
|-- n_submitUpgrade_per_hour: double (nullable = true)
|-- n_thumbsDown_per_hour: double (nullable = true)
|-- n_thumbsUp_per_hour: double (nullable = true)
|-- n_upgrade_per_hour: double (nullable = true)
|-- avg_session_items: double (nullable = true)
|-- avg_session_mins: double (nullable = true)
|-- avg_session_songs: double (nullable = true)*
浏览数据
在这里,我们将重点比较留下来的用户和离开的用户之间的行为。
Distribution of Churn
Distributions of Categorical Features
Correlations among Numerical Features
根据上述相关性,我们发现这些高度相关(> 0.8)的变量对(组):
- 流失,obs_hours,n_cancel
- 总和 _ 长度,n _ 会话
- 平均会话项目数,平均会话分钟数,平均会话歌曲数
- n_act_per_hour,n_addFriend_per_hour,n_addToPlaylist_per_hour,n_downgrade_per_hour,
n_help_per_hour,n_home_per_hour,n_song_per_hour,n_thumbsUp_per_hour
Correlations among ‘n_help_per_hour’ Numerical Features
删除高度相关的列后,相关性看起来好得多。
Correlations between Numerical Features (after removing highly correlated columns)
Distributions of Numerical Features
特征工程
现在我们总共有 16 个特性(不包括userId
和label(churn)
列)。
*root
|-- userId: string (nullable = true)
|-- label: integer (nullable = true)
|-- gender: string (nullable = true)
|-- last_level: string (nullable = true)
|-- n_session: long (nullable = false)
|-- reg_days: integer (nullable = true)
|-- n_about_per_hour: double (nullable = true)
|-- n_error_per_hour: double (nullable = true)
|-- n_logout_per_hour: double (nullable = true)
|-- n_song_per_hour: double (nullable = true)
|-- n_rollAdvert_per_hour: double (nullable = true)
|-- n_saveSettings_per_hour: double (nullable = true)
|-- n_settings_per_hour: double (nullable = true)
|-- n_submitDowngrade_per_hour: double (nullable = true)
|-- n_submitUpgrade_per_hour: double (nullable = true)
|-- n_thumbsDown_per_hour: double (nullable = true)
|-- n_upgrade_per_hour: double (nullable = true)
|-- avg_session_items: double (nullable = true)*
在特征工程的正常情况下,在将它们输入到我们的模型之前,我们必须将它们编码、缩放和组合成一个特征向量。但是由于我们这次使用 pipeline 来构建模型,而不是现在就处理它们,我们只是将它们准备为一些数据处理阶段。
建模
将数据分成训练集和测试集
从上一节展示的流失分布来看,我们知道这是一个不平衡的数据集,只有 1/4 的用户被标记为流失。为了避免随机分裂中的不平衡结果,我们首先用标签抽样建立一个训练集,然后从整个数据集中减去它们得到测试集。
型号选择
为了选择一个好的模型进行最终调整,我们在 Spark 的 MLlib 中比较了三个不同的分类器模型。
- 因为 Spark 提供的评估器不太适合我们的使用,所以我们定制了一个评估方法,以便在测试原型时查看分数。
**
*<class 'pyspark.ml.classification.LogisticRegression'>
train time: 30s
----------pred_train----------
f1-score:0.88
precision:0.91
recall:0.84
accuracy:0.94
confusion matrix:
TP:32.0 | FP:3.0
FN:6.0 | TN:122.0
----------pred_test----------
f1-score:0.75
precision:0.90
recall:0.64
accuracy:0.90
confusion matrix:
TP:9.0 | FP:1.0
FN:5.0 | TN:47.0
<class 'pyspark.ml.classification.DecisionTreeClassifier'>
train time: 23s
----------pred_train----------
f1-score:0.93
precision:1.00
recall:0.87
accuracy:0.97
confusion matrix:
TP:33.0 | FP:0.0
FN:5.0 | TN:125.0
----------pred_test----------
f1-score:0.62
precision:0.60
recall:0.64
accuracy:0.82
confusion matrix:
TP:9.0 | FP:6.0
FN:5.0 | TN:42.0
<class 'pyspark.ml.classification.RandomForestClassifier'>
train time: 27s
----------pred_train----------
f1-score:0.88
precision:1.00
recall:0.79
accuracy:0.95
confusion matrix:
TP:30.0 | FP:0.0
FN:8.0 | TN:125.0
----------pred_test----------
f1-score:0.64
precision:0.64
recall:0.64
accuracy:0.84
confusion matrix:
TP:9.0 | FP:5.0
FN:5.0 | TN:43.0*
基于测试集的 f1 分数,我们决定使用LogisticRegression
进行最终调整。
分层交叉验证的模型调整
由于被搅动的用户是一个相当小的子集,我们将使用 f1 分数作为优化的主要指标。如上所述,Spark 的评估器不太适合我们的使用,所以我们需要构建一个FbetaScore
评估器类来使用交叉验证器。
因为 PySpark 的[CrossValidator](https://spark.apache.org/docs/latest/api/python/pyspark.ml.html#pyspark.ml.tuning.CrossValidator)
目前只支持简单的 K-fold CV,我们将使用 pip 库[spark-stratifier](https://github.com/interviewstreet/spark-stratifier)
为不平衡数据集执行分层 K-fold CV。
explainParams()
中maxIter
参数的解释:
- maxIter:最大迭代次数(> = 0)。(默认值:100)
现在让我们发动引擎!我们的目标是超过原型的 0.75 分。
*train time: 3113s
----------pred_train----------
f1-score:0.88
precision:0.91
recall:0.84
accuracy:0.94
confusion matrix:
TP:32.0 | FP:3.0
FN:6.0 | TN:122.0
----------pred_test----------
f1-score:0.75
precision:0.90
recall:0.64
accuracy:0.90
confusion matrix:
TP:9.0 | FP:1.0
FN:5.0 | TN:47.0*
与原型相比没有观察到改进…也许原型LogisticRegression
已经是最好的了;)
特征重要性
让我们检查一下最重要的特性是否有意义。
- *reg_days(注册后天数)
注册天数越短的用户越容易流失。
从数据探索会话中数字特征的分布可以得出相同的结论。 - 设置-每小时检查事件
用户检查设置越频繁,他们就越有可能流失! - 每小时升级相关的事件
- 每小时观看的广告
- 每小时播放的歌曲
对我来说,这一切似乎都是合理的…尽管我期望在查看数字特征的分布时,否定事件具有更高的重要性:(
结论
到目前为止,我们已经完成了这些步骤:
- 探索和操作我们的数据集
- 为我们的问题设计相关功能
- 通过抽样流失将数据分为训练集和测试集
- 用 Spark 的基于数据帧的 MLlib 构建二进制分类器模型
- 用 Spark 的 ML 管道和stratifiedcrossfvalidator选择并微调最终模型
关于这个项目有趣或困难的方面:
- 如何使用 Spark 的数据框架进行聚合
- 如何处理不平衡数据集
改进:
- 给定一些日志作为输入,实际预测哪些用户更有可能流失
- 在模型优化会话中探索更多参数
- 一种更加自动化的方法来过滤掉高度相关的特征
感谢阅读!完整的代码可以在这个报告中找到。
使用 SQL 检测异常值
An outlying black cat (photo by author)
SQL 不具备像 R 或 Python 这样的语言的特性,但这并不意味着你不能用它来通过寻找异常点或异常值来对数据进行初步清理。
许多数据科学家习惯于这样的工作流,即在用 R 或 Python 进行真正的工作之前,他们从符合 SQL 的数据库中吸取数据。这显然是低效的,因为这意味着有缺陷的、不正确的或由于某些其他原因最终不可用的数据需要移动到真正的分析环境中。
虽然 SQL 确实缺乏统计工具和分布库,但这并不意味着它不能用于进行有用的数据分析,只是工具集仅限于您可以动态编码的工具。
一旦知道如何检测异常值和其他数据异常,您就可以在 SQL 中轻松完成一项重要任务。我们将看看两种对 SQL 代码友好的方式。
一种这样的方法是使用中间绝对偏差来检测数据异常值。这种方法实际上比人们通常使用的 z 分数更可靠,因为它没有对数据的分布进行假设。
从概念上讲,这种方法的优点是非常简单。任意观测值的中位数绝对偏差是该观测值的绝对差与所有观测值的中位数以及所有观测值的中位数绝对差与所有观测值的中位数的比值。
在非参数统计中,工作人员已经提出了中位数绝对偏差的临界值 5,因为中位数绝对偏差值略低于 1.5 相当于一个标准偏差,所以 MAD = 5 大约等于 3 个标准偏差。
这是 SQL 编码,缺点是您需要使用一个公共表表达式或其他解决方法,因为这种方法需要对聚合函数的结果进行进一步的操作。另一个有点挑战性的部分是,在主要的 SQL 实现中还没有一个简单的“中间值”函数。在下面的代码中,我使用了’ Percentile_Cont '函数,我将在下面对其进行更多的注释。
有三个步骤,需要两个 cte 来捕捉中间步骤—
- 求感兴趣的变量的中值。您需要这个值来进行进一步的计算,如果没有 CTE,作为一个集合是很困难的。
- 计算中值偏差。偏差就是上面发现的中值体重和个体体重之间的绝对差值。求这个值的中间值。
- 最后找到…的偏差
此处的代码创建了一个学龄前儿童的体重(以千克为单位)和年龄(以岁为单位)的表格,可能会在儿科诊所或市婴儿健康服务中心记录。最后一张表显示了每个学龄前儿童的体重及其与表中所有学龄前儿童体重中位数的偏差:
CREATE TABLE KidWts (Name nvarchar(20),Age int ,Weight float);INSERT INTO KidWts VALUES(‘Lily’,3,15), (‘Muhammad’,30,98), (‘Daphne’, 3, 16), (‘Max’, 2, 12),(‘Chloe’,1,11),(‘Jackie’,2,14),(‘Albert’,3,17);WITH MedianTab (MedianWt)AS(SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY Weight)OVER () as MedianWeightFROM KidWts),DispersionTab (AbsDispersion)AS(SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY (Abs(Weight-MedianWt)))OVER () as AbsDispersionFROM MedianTab JOIN KidWts on 1=1)Select DISTINCT *,ABS((Weight-MedianWt)/AbsDispersion) FROM KidWts Join DispersionTab on 1=1JOIN MedianTab on 1=1
该查询返回以下结果:
The results of the query — Muhammad looks a little out of place
看着表格,一个结果突出了出来——穆罕默德的 98 公斤没有,与中位数的偏差为 41.5,是非常不合适的。经过一点调查,我们意识到,原因是穆罕默德的问题,是穆罕默德阿里,98 公斤是他在 1974 年与乔治·福尔曼比赛前的体重。对一个重量级拳击手来说并不算特别重,但对一个学龄前儿童来说却不合适。可能是一个装箱悲剧 DBA 在输入真实数据之前使用装箱统计数据来测试数据库功能,并且没有完全清除测试数据—这是一个极端的例子,但并非不可能。
MAD 值 41.5 可能比您在现实生活中看到的要大,并且旨在说明只是错误地出现在数据集中的数据类型,这是现实生活数据集中出现异常或异常值的一种方式。
更详细地看代码,Percentile_Cont 函数在最新的 ANSI SQL 标准中,但这并不意味着它在 SQL 的每一个实现中都有效——我在 SQL Server 上测试过,我的信息是这个函数在 Oracle 和 PostgreSQL 中也可用。MySQL 没有这个功能,所以你需要使用另一种方法来寻找中间值。
您可能想从下面的文章中选择您最喜欢的——Joe Celko 的关于 SQL 中值的文章——但是请注意,它是在 cte 之前写的,因此可以通过用 cte 替换临时表或视图来改进这些解决方案。正如 Joe Celko 的文章所示,在 SQL 中有很多计算中位数的方法——找到适合您的方法,并相应地更新代码。
罗伯特·德格拉夫是《管理你的数据科学项目 》一书的作者,该书将于 2019 年 7 月中旬由 Apress 出版社出版。
你可以在推特上关注罗伯特:https://twitter.com/RobertdeGraaf2
使用 SQL 改进缺失值
Missing data points can sometimes feel like missing jigsaw pieces (photo: author)
几个月来,我们一直在研究在数据科学中使用 SQL 的不同方式——最近一次是在中使用 SQL 检测异常值(与检测异常不同,尽管那是将来的事情)。
在这个零星系列的第一篇文章中,我们看到了如何通过计算一列中空值的比例来评估缺失数据。然而,简单地查找和计算丢失的数据点用处有限——如果您找到这些丢失的数据点,您需要有工具可以使用。
所有数据科学家都应该熟悉处理缺失值的核心方法,这句话的意思是“如果你不熟悉,你应该记住下面的列表”:
- 列表式删除:如果一个变量丢失了太多的事例,以至于看起来毫无用处,那就删除它。
- 逐例删除:如果某个特定的观察结果缺少太多因素,则将其删除。
- 虚拟变量调整:如果变量在特定情况下缺失,使用一个假设值代替它。根据问题的不同,中间值可能是直观的选择或代表“中性”设置的值。
- 插补:使用一种算法来填充值,从最基本端的简单随机数到更复杂端的由自己的模型插补的值。
很明显,SQL 在其中一些方面比其他方面更好。列表式删除就像在 SELECT 语句中去掉列名一样简单;逐例删除可能像 WHERE 子句测试空值一样简单。
使用这两种方法会带来风险——如果数据不是完全随机缺失的,您很容易引入偏差。不难想到由于数据中反映的特定情况而没有收集度量的情况。Frank Harrell 在他的书《回归建模策略》中给出的一个例子是血压测量,这些测量并不针对预期不久就会死亡的患者——因此排除了患者观察值。在患者结果模型中遗漏的血压测量值可能会选择性地排除已经死亡的患者,从而造成明显的偏差。
虽然上面的例子说明了按病例删除的风险,但从同一个例子中也可以看出按列表删除的缺点——如果你忽略所有血压信息,你就忽略了许多对解释医疗结果有用的信息。
一种更复杂的方法是使用 Paul Allison,他是 Sage 系列统计学短评《缺失数据》的作者,他称之为“虚拟变量调整”,其中包括创建一个变量来指示缺失信息的存在。您还需要更新原始变量,以包含一个常数值来代替空值。在 SQL 中,这可能是一个简单的 CASE 语句。我们假设数值常数为 2:
SELECT CASE Missing_var WHEN var is null THEN 1 ELSE 0 END, coalesce(var, 2)FROM Table
上面的代码是与实现无关的——例如,如果使用 MySQL,您可以用“ifnull”替换“isnull”函数,因为“isnull”是 null 的一个快捷逻辑测试(它可以替换上面的 CASE 语句),其他实现可能也有类似的快捷方式。
这些处理缺失值的技术是最基本的。虽然公平地说,一些最先进的技术很难在 SQL 中实现,但这些仍然可以在您的旅程中带您走一段合理的距离。
在数据库中使用执行这些步骤的一个原因是,您可以通过延迟选择建模环境(如 R、Python、Rapidminer 等)来将数据准备和探索步骤与建模步骤分离。,直到你有一个稍微准备好的数据集。您甚至可以在多个环境中执行分析以检查结果。
因此,通过使用 SQL“就地”分析数据并做一些基本的数据准备,您可以通过在第一时间转移到建模环境来保持比可能的更高程度的灵活性。
罗伯特·德格拉夫的书《管理你的数据科学项目》》已经通过出版社出版。
在 Python 中使用标准差
Standard Deviation in Practice
Python 中的均值、标准差和误差线
正如这篇文章提到的,通过标准差,你可以了解你的数据是接近平均值还是分布在很大范围内。例如,如果一个雇主想确定他的一个部门的工资对所有员工是否公平,或者是否有很大的差异,他可以使用标准差。为此,他可以找到该部门的平均工资,然后计算标准偏差。一个场景可能如下所示:他发现标准差略高于他的预期,他进一步检查了数据,发现虽然大多数员工都属于类似的薪酬等级,但有四名忠诚的员工在该部门工作了 15 年或更长时间,比其他人长得多,由于他们在该公司工作的时间长,他们的薪酬也高得多。
一般来说,低标准偏差意味着数据与平均值非常接近,因此非常可靠,而高标准偏差意味着数据与统计平均值之间存在较大差异,因此不太可靠。
标准差最重要的应用之一是比较两个数据集。如果两个数据集有相同的平均值,这并不意味着它们一定完全相同,对吗?例如,数据集199, 200, 201
和0, 200, 400
都具有相同的平均值(200
,但是第一个数据集与第二个数据集( s =200)相比具有非常小的标准差( s =1)。
样本或总体的标准偏差
群体 数据集包含指定组的所有成员(可能数据值的完整列表)。例如,人口可能是“居住在加拿大的所有人”。 样本 数据集包含群体的一部分或子集。样本的大小总是小于被抽取样本的总体大小。例如,样本可能是“生活在加拿大的一些人”。
我们通常对了解 总体 标准差 感兴趣,因为它包含了我们想要分析的所有值。如果我们有整个人口或更大人口的样本,我们通常会计算人口标准差,但我们不想将我们的发现推广到整个人口。在许多情况下,我们有一个样本数据,我们想从中归纳出一个群体的分析。一般来说,如果您只有一个数据样本,并且想要对从中抽取样本的总体标准偏差进行陈述,则需要使用样本标准偏差。在本文中,我将重点讨论总体标准差。总体标准差公式如下:
formula from here
例如,如果我们的数据集是[13, 22, 26, 38, 36, 42,49, 50, 77, 81, 98, 110]
,总体均值或平均值将是:数据集中所有单个项目的总和除以项目数,结果将是53.5
。
现在,从集合中的每个项目中减去平均值,并计算每个数字与平均值之间的差值的平方。例如,对于第一个项目13
,我们有:
(13–53.5)=(-40.5) and square of (-40.5) will be: 1,640.25
然后,对所有的平方差求和(10,581
),并将这个和除以项目数。结果将是881.75
。这个数字被称为方差。求方差的平方根,求出标准差。所以这个数据集的标准差会是29.69
。
Python 中的标准差
可以使用 Python 中的Numpy
库计算数据集的总体均值和标准差。下面的代码展示了这项工作:
import numpy as np
dataset=[13, 22, 26, 38, 36, 42,49, 50, 77, 81, 98, 110]print('Mean:', np.mean(dataset))
print('Standard Deviation:', np.std(dataset))Mean:53.5
Standard Deviation: 29.694275542602483
在 Python 中使用标准差比较两个数据集
下面的两个数据集显示了两个城市在 15 天内的高温(以华氏度为单位)。我们想比较这两个城市气温的平均值和标准偏差。
City A= 36, 37, 36, 34, 39, 33, 30, 30, 32, 31, 31, 32, 32, 33, 35City B= 41, 35, 28, 29, 25, 36, 36, 32, 38, 40, 40, 34, 31, 28, 30
两个城市的平均气温分别是 A 市的33.4
和 B 市的33.5
,非常接近。两个城市都有点冷。但是 A 市的标准差2.60
远小于有4.83
的 B 市。这表明城市 A 的温度比城市 B 更稳定。换句话说,城市 B 的温度每天变化更大。
为了在图中显示这种变化,我在 Python 中使用了误差线。误差线对问题解决者很有用,因为它们显示了一组计算值的置信度或精度。可以使用 Python 中的matlibplot
库来描述平均值和标准偏差。如下图所示,与城市 b 相比,城市 A 的变化较小。
Error bar with mean and standard deviation
用于创建误差线的 Python 代码
现在我将解释我是如何创建误差线的。
首先,在导入库之后,我计算了两个数据集的平均值和标准差:
City_A=[36,37,36,34,39,33,30,30,32,31,31,32,32,33,35]
City_B=[41,35,28,29,25,36,36,32,38,40,40,34,31,28,30] Mean_City_A=np.mean(City_A)
Mean_City_B=np.mean(City_B) STDV_City_A=np.std(City_A)
STDV_City_B=np.std(City_B)
然后,我创建一个图形对象并设置轴标签:
# Create a figure with customized size
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111) # Set the axis lables
ax.set_xlabel('Day', fontsize = 18)
ax.set_ylabel('Temperature', fontsize = 18)
我们需要给 X 轴分配天数(15 天的周期):
# X axis is day numbers from 1 to 15
xaxis = np.array(range(1,16))
当我们在线上创建误差线时,我们也可以设置该图形的颜色和线条设置:
# Line color for error bar
color_City_A = 'red'
color_City_B = 'darkgreen' # Line style for each dataset
lineStyle_City_A={"linestyle":"--", "linewidth":2, "markeredgewidth":2, "elinewidth":2, "capsize":3}lineStyle_City_B={"linestyle":"-", "linewidth":2, "markeredgewidth":2, "elinewidth":2, "capsize":3}
现在,我们为每个数据集项、标准偏差和我们设置的线条样式创建两个误差线。
# Create an error bar for each datasetline_City_A=ax.errorbar(xaxis, City_A, yerr=STDV_City_A, **lineStyle_City_A, color=color_City_A, label='City A')line_City_B=ax.errorbar(xaxis, City_B, yerr=STDV_City_B, **lineStyle_City_B, color=color_City_B, label='City B')
我想在每一行上显示数字,这就是为什么,我使用annotate
函数给点添加文本。功能中的xy
显示数字在图形上的位置,xytext
显示标签的位置。
# Label each dataset on the graph, xytext is the label's position for i, txt in enumerate(City_A):
ax.annotate(txt, xy=(xaxis[i], City_A[i]),
xytext=(xaxis[i]+0.03,City_A[i]+0.3),
color=color_City_A) for i, txt in enumerate(City_B):
ax.annotate(txt, xy=(xaxis[i], City_B[i]),
xytext=(xaxis[i]+0.03,
City_B[i]+0.3),color=color_City_B)
此外,我将为图形绘制一个图例,以及关于图例样式、轴字体样式的其他设置,并向图形添加一个灰色网格:
# Customize the legend font and handle
lengthparams = {'legend.fontsize': 13, 'legend.handlelength': 2}
plt.rcParams.update(params) # Customize the font
font = {'family' : 'Arial', 'weight':'bold','size': 12}
matplotlib.rc('font', **font) # Draw a grid for the graph and set face color to the graph
ax.grid(color='lightgrey', linestyle='-')
ax.set_facecolor('w')
最后,使用plt.show()
命令显示误差栏。
在下面找到 Python 代码:
感谢您阅读本文!
使用统计过滤器提高盈利能力
DAX30 波动率/均值回归策略的应用实例
Every analysis must begin with a profitable equity curve backtested over different market regimes and during several years (analysis can be split by periods, in this example just one year will be covered).
找到合理的盈利策略并不是一件容易的事情,因此任何提高盈利能力和减少亏损的资源都值得探索。
在本文中,我将通过使用统计过滤器来讨论策略优化。
统计过滤器和参数蒙特卡罗分析是优化交易策略最简单的方法。
统计数据更易于应用,可用于改变尺寸或在某些操作中过滤掉。这种方法只需要来自回测的结果数据集,尽管推荐通过自动化回测引擎进行最终检查。
值得一提的是,这一过程并不专门针对盈利能力和/或权益曲线,但它也可能具有减少执行交易数量的积极副作用,这有效地消除了一些费用并减轻了操作者的心理压力——如果策略被设计为任意执行的话。
在这个例子中,正在分析一个自主策略。该策略是围绕 DAX30 工具(期货或差价合约)的风险控制和情境化均值回归/波动率设置而设计的。
任何策略和任何工具/资产都可以以类似的方式进行分析,因为过滤器可以应用于任何设置。
设计滤波器与其说是一门科学,不如说是一门艺术,需要大量的数据观察。数据观察包括读取实际数据和图表以及创建辅助图表,以识别和隔离可能对盈利能力或权益曲线产生影响的因素。
在解释数据时,必须考虑每个参数或设置在市场环境中的意义。这意味着在阅读数据时发挥积极的作用,因为最初的假设需要建立,然后通过进一步的统计分析来证实或反驳。
优化不一定涉及盈利能力。可能会有优化目标来平滑权益曲线。一条友好的权益曲线更容易通过金融杠杆或信用来衡量,而一条起伏不定的权益曲线的严重下降很容易使我们的资本面临严重风险。
输入数据
分析从来自策略的人工回溯测试的数据开始,这同样适用于自动交易。这些数据作为 CSV 文件合并到分析中。
本文专门讨论回测结果的统计分析。尽管将包含一些命令来展示如何添加键列,但我假设任何对应用这些概念感兴趣的人都知道如何编写统计分析代码。
Figure 1. CSV File incorporated to Analysis.
股票曲线与市场条件
回溯测试可以让你重新发现你的策略,并且经常会揭示策略中隐藏的行为和偏见,这些行为和偏见不是最初定义的一部分。
根据基础资产/工具直观检查权益曲线,以确定特定市场条件和权益曲线行为之间是否存在潜在相关性,这一点极其重要。可以检查大量项目:潜在趋势、趋势强度/斜率、超买和超卖条件、波动性、成交量、较高时间框架市场条件等。—仅举几个例子–。
有些项目可以量化(波动性可以通过 ATR 量化),但其他项目则不能(趋势、趋势强度或市场机制可能是主观的)。对于那些无法量化的数据,可以手动将操作员注释或代码合并到数据集中。
绘制权益曲线
对数据的第一个操作是合并策略中的累计权益列。假设您的数据已经合并到一个名为 data 的 Pandas 数据框架中,下面的命令将添加一个新的列来提供股票曲线。
数据[‘权益’] =数据[‘利润’]。累计和(轴=0)
虽然利润栏预计是以欧元表示的利润/损失,但回溯测试已经调整,因此 1€对应一个点,因此该栏实际上代表每次交易的工具点利润。实际利润可以很容易地通过定义正在操作的期货合约的数量来获得,或者如果策略是使用 CFD 工具操作的,则通过定义批量来获得。
Figure 2. Equity Curve vs. Trades
该战略的权益曲线如图 2 所示。x 轴代表已执行的交易,y 轴以点数显示累计利润。这条曲线有助于直观地理解策略的机制,包括连续亏损和盈利的次数,以及每次交易的相对盈利/亏损点数。这种表现与时间上的权益曲线相关,但并不匹配,因为交易并不是随时间线性分布的。
Figure 3. Equity Curve vs. Time
这两张图都很有意义,第一张图关注的是交易的顺序,第二张图给出了股票随时间发展的思路。
Figure 4. DAX Futures for the backtested period.
从图表中可以看出,在上半年,该战略的执行是有利可图的。市场已经下跌了 2000 点左右,该策略已经能够从该运动中捕捉到 1250 点。在下一季度,该战略已经能够纳入额外的 500/600 个利润点。
相反,今年下半年根本没有盈利。它已经累积了 500/600 点的跌幅。分析图表和最大交易的进场设置,我们推断试图捕捉更大波动的交易可能会亏损。从回溯测试的执行来看,我也有这样的主观感觉,持久的交易往往是输家,而最好的交易会很快达到盈利目标。这实际上符合策略中使用的基本原则:波动性和均值回归。
主观感觉不能重新定义策略,但它们构成了进一步分析的合法线索,因为它们是经营者市场知识的一部分(通常是启发性的)。
交易盈亏点与交易持续时间
如前所述,出现的一个问题是交易持续时间是否与交易的盈亏概率相关。一个简单的分析方法是使用散点图,在 y 轴上画交易规模,在 x 轴上画交易持续时间。在这个例子中,我用蓝色标出了盈利的交易,用红色标出了亏损的交易。额外的色谱柱易于整合:
data[’ trade size ‘]= ABS(data[’ S/L ‘]—data[’ T/P ‘])
data[’ trade duration ‘]= data[’ close time ‘]—data[’ open time ']
交易规模的定义有点武断,带止损或带盈利点同样有效。
对盈亏交易进行分类很容易,你只需要为每个图表系列生成一个新的过滤熊猫数据框。
子数据=数据[数据[‘利润’]。astype(float) < 0]
Figure 5. Scatter plot to analyse trade size/trade duration relation
可以清楚地看到,大多数交易都集中在大约 400 点/ 5000 分钟的区域。在这个范围之外,只有 13 个观察值。
我将从离群值开始,因为它们更容易分析。将它们过滤掉将有助于关注交易集中的区域。
我之前提到过,我们所关注的主观感受指向:
- 大额交易可能无利可图。
- 长期交易是亏损的。
从图表上看,我注意到在这个特定的案例中,大额交易是有利可图的。只有 6 笔交易的目标是交易规模大于 400,而在这 6 笔交易中,有 2 笔是亏损的。然而,值得一提的是,六次观察可能是一个太小的样本量,无法获得统计相关的结果。
关于第二个假设,在持续时间大于 5000 分钟的所有交易中,7 个是亏损交易,3 个是盈利交易。同样,10 次交易可能不足以提取信息,所以如果可以重新定义策略来避免这些交易,会更安全。
即使利用这种简单的分析,第一滤波器也可以被选作假设。我将分析在密度更大的区域会发生什么。事实上,我们会选择持续 180 分钟以上的平仓交易——这个值是经过反复选择的。就差价合约而言,它还将取消互换费用。
在期货交易中,如果交易持续时间超过结算期,它可以避免对合约进行展期。我们也可以评估其他过滤器,如对交易止损设置 100 点的限制。这将有助于满怀信心地增加杠杆,因为我们不接受止损较大的交易。
缺乏对大额交易和较长交易持续时间的观察可能会导致嘈杂的结果,因为这些领域的观察数量很少,因此更容易发生变化。
我将最初纳入贸易超时过滤器。这个分析并不准确,因为我不知道在交易结束时会发生什么。它们可能会导致部分盈利或亏损。我会先假设赢和输是有补偿的。缩短交易持续时间的想法很有吸引力。直觉上,这可能会产生更平滑的权益曲线,降低费用(尤其是差价合约中的掉期费用)并简化操作。
这些结果可以在以后用新的回溯测试来验证,最好是运行一组新的数据,即不同的年份,以避免过度拟合。
Figure 6. Scatter plot to analyse trade size/trade duration relation for <5000 minutes
Figure 7. Scatter plot to analyse trade size/trade duration relation for <180 minutes
图 6 和图 7 显示了两个条件子集,分别进一步限制了持续时间小于 5000 分钟和 180 分钟的交易。在这两种情况下,但特别是在第二种情况下,可以看到蓝色占主导地位的红色。
这个说法可以通过绘制 180 分钟时限权益曲线来证明。
Figure 8. Equity curve removing the trades longer than 180 minutes: Too good to be true.
在这条股票曲线中,我假设当你在 180 分钟后平仓时,亏损和盈利的交易互相补偿。这可能是真的,也可能不是,但可以肯定的是,他们可能会对下降产生影响。
这一简化方法的关键信息是,当遵循这一途径时,公平似乎会得到改善。因此,花时间进一步回测这个场景是值得的。
回溯测试超时交易场景
现在看来,在 180 分钟后过早平仓可能会改善股票曲线,我们分析一下实际情况。为了做到这一点,我们编写了一个定制的回溯测试引擎(实际上只是 Python 中的几行代码),在实际交易时输入交易,并对 SL、TP 或超时的结果进行回溯测试。
这个新的回溯测试给出了一个更真实但仍有改进的结果,如图 9 所示。
Figure 9. Actual equity curve closing trades that last longer than 180 minutes.
虽然曲线不如理论曲线好,但已经实现了显著的改善。在最初的策略中,最大减仓是持续 30/40 交易的 750 点的大减仓。
通过应用这个简单的过滤器,我们将下降减少到 300 点,减少了 60%。过滤器提供了与原始策略相似的利润水平。
有限的提款,有效地提供更平滑的权益曲线,是策略中最重要的方面。即使实现了较小的利润。利润可以很容易地通过财务杠杆或具有更安全权益曲线的信贷来扩大。
Figure 10. Same strategy without and with filter applied. Improvement on drawdown is evident.
图 10 显示了两种策略之间的比较(有无滤波器)。虽然将计算两种策略的夏普比率,以量化哪种策略更好,但在这种情况下,很明显,滤波器改善了策略。
夏普比率将风险(标准差)与盈利能力联系起来,它可以用作给定策略或基金经理的品质因数。虽然我不会在本文中讨论夏普比率,但它值得一提,因为它适用于正确量化两种策略并与其他策略进行比较。
摘要
在这篇文章中,使用简单的统计过滤器的概念,以改善盈利能力和/或下降已经提出。整个逻辑过程包括分析数据、绘制权益曲线、做出一些初始假设和反复测试数据。
已经强调了提款和权益曲线的相关性,并提供了已实现结果的最终比较。
虽然该示例非常简单,并且没有探索进一步的过滤器或更数学的方法来优化结果,但它实现了以一种易于理解的方式说明统计过滤器的使用的目标。它还显示了有时简单的数据观察和有限的一组图和迭代如何能够在没有复杂数学的情况下改进策略。
[1]https://www.investopedia.com/terms/s/sharperatio.asp
使用生存分析来衡量资产表现
用概率解决现实世界的问题
介绍
生存分析顾名思义就是对某个人或某件事的生存分析。可以想象,我们需要一些数据来进行分析。最初,这些数据是人们从出生到死亡的存活时间。
它起源于 17 世纪,是一种决策支持工具。首先,用于确定特定区域的人口增长是否证明开设商店是合理的,其次,用于帮助确定年金。
负责这些创新的两位早期数据科学先驱是约翰·格兰特和埃德蒙多·哈雷。这个事实对哈雷的改进版本尤其有效,因为它有效地标志着精算科学的诞生。
Early Life Table by John Graunt — https://circa-navigate.corsairs.network/
Improved Edmond Halley Life Table — https://papp.iussp.org/
生存分析起源于精算界和医学界。然而,它可以用于研究关于同一受试者的任何两对相关事件,其中所述受试者是群体的一部分。例如,从我们委托资产到特定故障模式发生的时间,或者订户第一次加入服务的时间和她最终取消或退出的日期。
因此,当我们说资产时,我们指的是需要维护或保留的为组织产生收入的任何东西,包括但不限于物理资产、员工和订户。
在本文中,我们查看一些理论背景,以及对一些现实世界中的资产故障数据的分析,以获得具体的见解。
生存分析背景。
生存函数 S 的定义由下式给出,
有了时间, T 感兴趣的事件的时间。就资产可靠性而言,我们称之为可靠性函数。
可靠性定义为在特定条件下,资产或零件在给定时间内执行其预期功能的概率。
在可靠性工程、其故障和订阅取消的情况下,生存函数是感兴趣事件的累积分布的补充。
由于根据定义,整个群体或队列将存活至所选研究、分析或报告间隔开始,并且没有一个将无限期持续,因此我们通常可以声称:
特定人群作为时间函数的确切生存概率可以从数据中得知。这可以使用参数化和非参数化方法来完成,正如我们在后面的真实例子中看到的。
不可靠度 F(t)是累积失效分布,是可靠度函数的补函数,即 *F(t) = P (T < = t) = 1-R(t),*本次 F(t) = P (T < = t) = 1-R(t)。
根据定义,故障时间的密度函数为
危险率或瞬时故障率函数描述如下:
还记得我们之前说过,根据定义 f(t)=-R’(t)。
关于生存函数的更多背景,请访问维基百科,这里是 https://en.wikipedia.org/wiki/Survival_function,关于失败率和风险函数,这里是 https://en.wikipedia.org/wiki/Failure_rate。
事件时间数据分析
要将生存分析技术应用于事件数据,如委托和失败的资产或订户加入和取消,需要将数据转换成通常的格式。
在这个实际的例子中,我们考虑资产被安装在特定的功能位置,并在出现故障时被替换。个人的“寿命”定义为从第一次安装到第一次更换的时间,或者从一次更换到下一次更换的时间间隔。这个间隔可以用许多不同的方式来定义。对于一个特定的研究或决策支持生产系统来说,清晰和一致的完成是至关重要的。确保数据工程的高效和正确也很关键。
在下图中,每条线代表一个资产位置,每个初始点代表第一次安装,每个后续点代表更换事件。两条垂直线代表“研究”或“实验”的开始和结束。第一行代表初始数据,第二行代表今天。圆点和间隔替换时间代表我们的样本。
Original Time to Event Data distributed in time
我们把我们的数据重新整理成生活或生存数据集。研究/报告结束前的每个更换时间/故障代表一个事件,而在该期间结束时仍在使用的每个资产代表一个“审查观察”
生存数据可以用表格的形式表示,如下表所示。
Table 1 — Event Observations
Event=1 表示发生了故障,即生命线在当前观察期之前结束,Event=0 表示感兴趣的事件将在未来某个未知时间发生。
挑战
我们的客户怀疑他们的一些资产类别受到婴儿死亡率的影响,希望更好地了解情况。他们最近启动了可靠性数据质量计划,可以为我们提供最近的故障观察。数据很可能不足以做全面的生存分析,构建完整的生命表和浴缸曲线。然而,它应该足以获得一些关于婴儿死亡率的见解,并建立一个部分生命表。
分析
通常情况下,我们从 Kaplan Meier 估计量开始。正如几个世纪前的格兰特和哈雷一样,卡普兰和迈耶对生存分析和精算科学做出了根本性的贡献。当他们在 1958 年发表这种创新的非参数方法来估计存在删失数据的生存曲线时,这一成就就发生了。
此示例的工程数据集子集可在此处找到:https://drive . Google . com/OpenID = 1 q w9 b 0 mdjmodppo 9 iib Cl2 xvkiqrjjerm
library(survival)
library(ggfortify)################################################################################### Do Meier Kaplan estimation##################################################################################dfEventsArea <- read.csv(file="D:/ACE/initiatives/APA/DATA/eventDataArea.csv", header=TRUE, sep=",")survRecent=Surv(dfEventsArea$LIFE, dfEventsArea$EVENT)
eventsRecent.km <- survfit(survRecent ~ 1, data=dfEventsArea)
summary(eventsRecent.km, times = seq(0,5,0.5))
autoplot(eventsRecent.km, main="Kaplan Meier estimate of survival")
这里使用的汇总为我们提供了这一资产类别的生命表。
Life Table for Asset Class
时间的单位是年。它表示对其余变量有效的区间。当时的事件数由 n.event 给出,生存概率由生存给出。
ggfortify 库允许我们根据输入数据,仅使用一个命令,就可以绘制出估计的可靠性/存活率函数的可视化图形。
我们还对每个运营区域的资产可靠性感兴趣。
注意,这也可以是每个制造商、安培小时(从物联网或其他仪器获得)、容量等级或任何其他感兴趣的因素。
n_recentOU.km <- survfit(Surv(LIFE, EVENT) ~ OU, data=dfEventsArea)
autoplot(n_recentOU.km,main=”Survival probability of asset per area”)
正如我们在下面看到的,在可靠性方面,有两个区域似乎很突出——一个高于标准,一个低于标准。
对于一般预期持续 15 年或更长时间的资产类别来说,在过去四年中有超过 60%的资产似乎是一个值得关注的原因。从这里,人们可以对问题的根源展开调查。这可能包括设计不足、操作条件或主要向该地区供货的制造商的质量问题。
如前所述,我们还可以用参数方法分析故障事件时间数据集。
这在下面举例说明,不要太深入所涉及的理论。从随后的图表中可以看出,参数威布尔生存曲线与非参数曲线吻合得相当好。
recent.wbs <- survreg( Surv(LIFE, EVENT)~ 1, data=dfEventsArea, dist='weibull', x=TRUE)# There are multiple ways to parameterize a Weibull distribution. The survreg
# function embeds it in a general location-scale family, which is a
# different parameterization than the rweibull function, and often leads
# to confusion.# survreg's scale = 1/(rweibull shape)
# survreg's intercept = log(rweibull scale)
# For the log-likelihood all parameterizations lead to the same value.rweibullShape = 1/recent.wbs$scale
rweibullScale = exp(recent.wbs$coefficients)
weibSurv <- function(x, shape, scale) pweibull(x, shape=shape,
scale=scale, lower.tail=FALSE)# Survivalcurve(weibSurv(x, shape=1/recent.wbs$scale, scale=exp(coef(recent.wbs))), from=0, to=18,
ylim=c(0,1), ylab="Survival probability", xlab="Time")# Survival 2curve(pweibull(x, scale=exp(coef(recent.wbs)), shape=1/recent.wbs$scale,lower.tail=FALSE),from=0, to=5, main="Weibull fit of Survival Probability in first few years of asset class", ylab = "probability", xlab = "time in years", ylim=c(0,1))lines(eventsRecent.km, col="blue")
我们还可以推导并绘制风险函数:
# Hazard
curve( dweibull(x, shape=1/recent.wbs$scale,
scale=exp(coef(recent.wbs)))/pweibull(x, scale=exp(coef(recent.wbs)), shape=1/recent.wbs$scale,lower.tail=FALSE),from=0, to=5,
main="Parametric estimate of failure rate based on recent data"
, ylab = "h(t)", xlab = "time in years")
危险函数和浴缸曲线的左侧
上面绘制的危险函数也是浴盆曲线的左侧。
在这种情况下,没有足够的数据来确定该群体的失败概率是否会随着成员年龄的增长而增加。因此,无法构建完整的浴盆曲线。
然而,我们可以从非常高的初始失败率和随后迅速下降的失败率中得出结论,人口中的婴儿死亡率很高。这给了我们进一步的线索,告诉我们应该如何研究提高资产可靠性的方法。
结论
精算方法在资产管理中的应用是一个广阔而令人兴奋的领域。我希望你喜欢这个简短的介绍。其他应用包括远期风险预测计算、检查优先级排序和剩余使用寿命估计。
利用 TED 演讲进行机器学习
在我第一次尝试建立一个机器学习项目时,我对网上提供的大量资源和教程印象深刻。看看我经历了什么。
Photo by Miguel Henriques on Unsplash
这一切是如何开始的
今年夏天,我在学习一门关于应用机器学习的课程,并被要求寻找一个项目创意。在我研究的时候,我遇到了一个朋友。这个朋友话很多。我说的很多是指从一个句子开始另一个句子,直到不清楚他在说什么。当他继续说的时候,我试着去想他的句子结构会有多复杂。用积极的话来说:它们一定是在我周围摇曳的美丽而复杂的树。因此我有了一个宽泛的想法:分析文本的结构。我的另一个重要目标是掌握机器学习的四个阶段,即:
- 框定问题,
- 准备数据,
- 训练 ML 模型并
- 使用 ML 模型进行预测。
在这个领域做了一些研究(实际上是一整袋新的墨西哥卷饼,我真的很惊讶和不知所措)之后,一个特定的算法引起了我的注意:Word2vec 建模(这里是 Memo Akten 关于单词嵌入及其应用的一篇非常有趣的文章)。
This is a web application where you can look for similar words according to TED talks, the final result of my work
什么是 word2Vec 算法?
作为输入,该算法接收大量文本数据(演讲、书籍内容、字典、从网站抓取的文本等),并将每个单词分配给空间中相应的向量(向量的维度通常在 50–1000 左右,取决于数据集)。这些“单词向量”的位置靠近在类似上下文中使用的其他单词,例如在同一个句子中。例如,单词“sing”可以位于“song”附近。但是作业也取决于数据集(不管你用的是新闻文章还是哲学论文,但我会在最后一个阶段讲到)。
机器学习的四个阶段
The four stages of a machine learning project
下面我将解释我是如何经历机器学习的四个阶段的。
1.框定问题
通常,首先弄清楚你想要解决的问题是什么以及一些成功指标,以及机器学习是否是唯一合适的技术是有帮助的(参见 this) 。但是我没有什么特别的问题,我只是好奇去探索可能性并从中获得灵感。此外,我还想知道如何将机器学习输出用于艺术目的。因此,我的方法是探索各种可能性,并学习建立这样一个项目的基本步骤。
2.准备数据
准备数据是最有见地的部分。首先,问题是我想使用什么样的数据。因为 word2Vec 模型使用大量的文本是有帮助的,所以我决定使用英语的 TED 演讲记录。TED 演讲提供了大量关于各种事物的演讲。从 kaggle 导入数据集后,该准备数据了。从多个教程(像这个或者这个一个)我用了以下方法:
英语中有这种缩写,比如“has not”或“is not”,尤其是在口语中,比如 TED 演讲。为了确保这些短语不会影响训练,我扩展了文本语料库中的每个缩写。
删除特殊字符和数字 这是一个很容易解释的步骤,用来清理模型并专注于单词。
删除停用词 停用词是常用词(如‘a’,‘the’,‘of’,‘in’),对文本的意义没有贡献,可能会严重影响模型的性能。
小写全字
这是我经过几次训练后实施的一个步骤。预先降低所有单词的大小写可以极大地改进模型,因为如果没有它,模型将对“雄心”和“抱负”进行不同的处理。
对文本进行词汇化 词汇化或词干化是自然语言处理中的常用术语。在我们所说的语言中,我们经常使用从另一个词派生出来的词。
因此,有必要将这些特定的单词转换回词根,以避免冗余。
特别是对于 TED talk 抄本:移除抄本注释 前面所有的步骤实际上都可以应用于所有的 nlp 项目。但是看一看个人的语料库也是很重要的,所以我注意到了在我的文本中频繁出现的抄本评论。因为它们总是以括号开始和结束,所以很容易通过正则表达式删除它们。
我对这些方法的顺序掌握得越精确,越能确保它们真正清理了语料库,训练就越好。
我的最后一步是将语料库分成句子,并将每个句子分成模型的单词标记。
3。训练 ML 模型
就在培训之前,我检查了最常用的单词,以确保准备足够精确。如您所见,最常见的单词不包含任何特殊字符,并且几乎没有停用词。
Checking out the most frequent words before training the model
然后我初始化了模型。我在这个项目中使用了 Gensim Word2Vec 模型。经过多次测试,这个数据集的理想维数是 50。超过 100 导致拟合不足,从而导致不显著的结果。
我将最小计数调整为 50,忽略了所有出现频率低于这个最小计数的单词。这导致了总共 4703 个词汇。后来我用 30 个历元训练了这个模型。培训没有像我预期的那样持续很长时间。总共是 3 分钟。
4。使用 ML 模型预测
由于 word2vec 模型是无监督的,所以我所能测试的就是单词根据一个人的上下文直觉映射的有多好。
最有见地的是绘制模型。这意味着将高维向量投射到 2D 画布上(这里是一个很棒的教程,我就是为了这个而遵循的)。由于模型应该理想地将相似的词放置在彼此更近的位置,这些点应该显示某种聚类,因此这个情节非常令人宽慰(你必须想象:在长时间准备数据和训练之后,最终看到这样的结果是值得的)。
Plotting the word2vec model via TSNE
当绘制类似的单词云,如“食物”、“经济”或“爱情”时,你会注意到模型训练得有多好。尤其是看到这些单词云的位置与我将要使用它们的上下文如此接近,令人印象深刻。
我的申请
为了使用这个经过训练的模型,我构建了一个 web 应用程序,它不用深入代码就能提供相似的单词。
这真的很有趣。我用了 node.js 和 p5 。在这里你可以查看我的 github 。
替代文本语料库
我也试过尼采的书和维基百科的电影情节。由于复杂的文本风格,尼采没有像其他模型那样成功。但是它提供了大量不同的词汇。
维基百科的动机图比谈话语料库大 10 倍,所以准备数据需要更长的时间。结果还没有出来,所以等我的下一篇文章吧😉。
结论
总而言之,训练一个模型,而不仅仅是使用一个从模型中派生出来的应用程序,是一次很好的学习经历。现在有如此多的资源,用一些合理的编程知识开始一个项目并不困难。到目前为止,我还不能将这个模型用于艺术目的,但是让我们看看几个月后我会发布什么😉。
使用 TF IDF 通过关键词提取形成描述性章节摘要。
Source: https://pixabay.com/photos/library-books-education-literature-869061/
TF IDF 是一种自然语言处理技术,用于提取一组文档或章节中的重要关键字。首字母缩写代表“*术语频率-逆文档频率”*并描述了该算法如何工作。
数据集
作为我们的数据集,我们将采用玛丽·雪莱的《弗兰肯斯坦》的脚本(由古腾堡项目提供),并基于 TFIDF 算法的输出生成每章中事件的感觉。为了做到这一点,我们首先要理解为什么 TFIDF 算法如此成功:
TFIDF 算法
该算法分两部分工作。首先,我们计算我们的“项频率”。这将根据每个单词在文档中出现的次数对其进行排序(加权)——我们重复的次数越多,它越重要的可能性就越大。
然后,这个术语通过除以整个组中的单词数而被标准化。这是因为出现在一小段中的单词,例如标题,比成千上万行中的同一个单词具有更大的权重。
接下来,我们有’逆文档频率’部分。这很重要,因为它根据单词在特定文本片段中的个性对单词进行排序。在这里,我们将在文本的单个部分中频繁使用的单词与在各处中频繁使用的单词分开。这意味着只有当前文档/章节的本地标识词被标记为重要。我们使用以下公式进行计算:
*1 + log_exp ( number_documents / (document_frequency + 1))*
这两个术语结合起来提供了每个单词相对于其所在部分的重要性的权重。
预处理
与所有数据科学任务一样,我们需要为算法中使用的数据做好准备。将所需文本读入 python 后,我们可以用空格替换所有句点,并用 regex 模块(re)删除所有非单词字符:
text = text.replace('.',' ')
text = re.sub(r'\s+',' ',re.sub(r'[^\w \s]','',text) ).lower()
接下来,我们根据单词’ chapter ’ *(+一个数字)*分割数据集,尽管这可以是 LaTeX 中的\section{.*}
或您选择的任何其他分隔符。
corpus = re.split('chapter \d+',text)
将数据输入算法
最简单的方法是使用 python 中的 scikit-learn 包。在这里,我们可以直接输入我们的数据,并获得它来计算每个组的单词排名:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizervectorizer = TfidfVectorizer()
vectors = vectorizer.fit_transform(corpus)names = vectorizer.get_feature_names()
data = vectors.todense().tolist()# Create a dataframe with the results
df = pd.DataFrame(data, columns=names)
过滤掉停用词
在自然语言处理中,停用词是一组对最终输出没有什么意义的词。这些通常是常用词,如I,a,all, any,and,the,them,it,dont,has
等。使用自然处理库nltk
,我们可以过滤掉包含这些的所有列。
from nltk.corpus import stopwords
nltk.download('stopwords')
st = set(stopwords.words('english'))#remove all columns containing a stop word from the resultant dataframe. df = df[filter(lambda x: x not in list(st) , df.columns)]
打印出每章排名前 N 位的单词
这是通过选择每一行/数据集来完成的——由于我们之前的选择,这些代表不同的章节,并在打印之前选择 N 个排名最高的列。
N = 10;for i in df.iterrows():
print(i[1].sort_values(ascending=False)[:N])
解释结果
现在,这就是我们看到我们的算法在预测关键词方面有多好的地方。因为有 25 个章节,我们在下面只展示了一些随机选择的章节。在该选择中,我们将章节提要与由 TFIDF 算法选择的排名最高的关键字进行比较,并决定它在描述现有事件方面的表现。
第一章弗兰肯斯坦设定了场景,描述了他的母亲如何发现了被一个意大利家庭遗弃的伊丽莎白,并收养了她。
mother 0.084748
beaufort 0.079967
child 0.068566
father 0.062509
orphan 0.054138
daughter 0.053630
poverty 0.049939
italy 0.047980
infant 0.043612
abode 0.043612
Name: 1, dtype: float64
第 3 章维克多(弗兰肯斯坦)离开日内瓦去上大学。在这里,自然哲学教授 Krempe 说服他,他学习炼金术的时间是一种浪费。结果,他参加了沃尔德曼教授举办的化学讲座,教授说服他从事科学研究。
science 0.074137
natural 0.071721
professor 0.065336
philosophy 0.059502
modern 0.054772
krempe 0.049489
waldman 0.049489
lecture 0.043557
chemistry 0.043557
principal 0.036860
Name: 3, dtype: float64
第 4 章维克托斯对他在理解生命科学方面的工作表现出极大的热情。以至于他对创造生命的迷恋成为他唯一的追求,这在很大程度上导致他忽视了生活中的朋友,并秘密进行令人讨厌的实验。
pursuit 0.053314
study 0.050588
life 0.046087
one 0.040652
corruption 0.039524
would 0.036956
science 0.036134
eagerness 0.035029
natural 0.034180
secret 0.034180
Name: 4, dtype: float64
这个怪物一直在学习一个小茅屋里的居民的语言和历史。费利克斯最近在这里遇到了萨菲。萨菲的母亲是一名信奉基督教的阿拉伯人,在嫁给萨菲的父亲之前曾被土耳其人奴役。
felix 0.164026
safie 0.136081
turk 0.112066
paris 0.087163
leghorn 0.084299
daughter 0.083509
deliverer 0.070249
lacey 0.045272
merchant 0.045272
christian 0.042149
Name: 14, dtype: float64
维克多和亨利穿越了英格兰和苏格兰,但是维克多开始迫不及待地想要开始他的工作,摆脱他和怪物的束缚。
oxford 0.060545
city 0.058641
edinburgh 0.048436
lakes 0.042927
scotland 0.042927
might 0.038131
visited 0.037911
matlock 0.036327
cumberland 0.036327
clerval 0.034506
Name: 19, dtype: float64
第二十三章伊丽莎白被怪物杀死。在未能说服裁判官让这个生物负责后,维克多·沃斯让他的生命去摧毁他的创造。
magistrate 0.048762
room 0.047945
exert 0.039379
pistol 0.039379
arms 0.036325
punishment 0.034900
rushed 0.034054
might 0.033068
elizabeth 0.032818
wandered 0.032088
Name: 23, dtype: float64
维克多失去了所有的家人,他追踪怪物来到北方的冰雪之地。在临终前,维克多讲述了他的故事,并恳求沃尔顿在他死后继续他的追求。
yet 0.051022
ice 0.048866
vengeance 0.037918
shall 0.033370
still 0.031682
die 0.030744
frankenstein 0.030744
would 0.027350
death 0.026679
feel 0.026679
Name: 24, dtype: float64
结论
看起来,至少对于这本玛丽·谢利的小说来说,术语频率-逆文档频率算法很容易使用,并作为提取每章描述性关键词的可靠方法。所以为什么不亲自尝试一下,看看你会有什么发现呢?
进一步的工作
这种和平的延续:将情感和情绪分析应用于《弗兰肯斯坦》,可以使用下面的链接找到。
应用两种自然语言处理技术来比较玛丽·雪莱的《弗兰肯斯坦》中的情感和 TF-IDF 关键词分析…
towardsdatascience.com](/using-sentiment-analysis-to-explore-emotions-within-text-ae48e3e93999)
利用生日悖论教授概率基础知识
你的两个朋友同一天生日的可能性有多大?
**简答:**这取决于你有多少朋友…
Here’s the audio version of the article, read for you by the author.
今天是我亲爱的人的生日(万岁!),这是一个神话般的借口来涵盖一些生日语言中的概率基础知识。我们的目标是一起找出这个常年统计作业排行榜榜首:
如果你有 N 个人在一个小组里,至少有一个人是同一天生日的概率是多少?
如果你还没想过这个,花点时间猜猜看。想象一下这个小组有 30 个人,他们是 T2。两个人在同一天得到蛋糕的几率有多大?或者,如果这让人望而生畏,只需回答这个问题:你愿意跟我赌 10 美元有生日副本,还是 10 美元没有?让我们看看你的直觉有多好!
我们将作为笑点到达那里,但是首先我们将开发所有你需要自己解决生日问题的乐高积木(下面的大字体)
最基本的基础
为了纯粹的卖弄学问,让我们抛开这个令人瞠目结舌的启示:概率介于 0 和 1 之间(或者 0%和 100%,如果你愿意)。酷毙了。现在你知道为什么像*“我 1000%确定,他们会迟到”*这样的句子会让数据人员感到恐慌。
概率的三种方法
思考概率有三种主要方法:
- 基于事件的 (包括枚举事件并计数)
- 基于频率的 (涉及分布象征着如果我们在无限平行宇宙中观察我们的现象,事物会如何发生变化)
如果你上了一堂统计学课,而你的教授并没有采用强调显而易见的东西的木槌敲脑袋的方式,那么让我来详细说明这些东西在你的课本中的什么地方。
基于事件的概率
概率=[分子]/[分母]
**前几章倾向于从基于事件的概率 方法 开始。这是因为这是一个简单的游戏场,大多数人可能已经有了这方面的直觉。例子:你的硬币正面朝上的几率有多大?1/2.你在六面骰子上掷出 6 的几率有多大?1/6.这里发生了什么事?
分子:我们感兴趣的事件可能发生的方式的数量。
分母:任何(相关)事件可能发生的方式的数量。
在一枚硬币上,可能的相关事件是正面和反面。这就是 2 的由来。很简单。
A small amusement for you on the topic of heads and tails…
清点东西
要使用 基于事件的概率 ,您需要能够枚举事件并对其进行计数。这就是为什么你的教科书可能会把你拖进 组合学 直到你彻底厌倦为止。组合学给你计算事件发生的方式的数学知识(为了你所追求的分子和分母)。
欢迎来到大量的家庭作业问题,都是关于“我有多少种不同的方法可以从 100 名候选人中选出 10 名委员会成员?”( 17,310,309,456,440 ) 或设置 4 位 PIN 码时,我可以选择多少种不同的选项?”(10000)
在基于事件的世界里,所有的积木事件都是同样可能发生的,这样它们就可以不受层层杂乱修饰词的干扰,进行基本的算术运算。这是一个所有的硬币都是公平的,所有的骰子都是平衡的,所有的牌都是无填料的,所有的生日都是同等概率的世界。
Since we’re on the topic of Counting things.
对于生日问题,我们需要快速了解一下计数:
“和”要求您乘以(x)个计数
“或”要求您添加(+)计数
如果你喜欢的话,去找一个证明…或者用一个简单的例子来满足你自己,然后继续:如果我有两个素食的选择或者三个荤菜的选择,那么我有多少餐的选择?2 + 3 = 5.如果我可以选择一道主菜和两道甜点中的一道呢?我从多少种不同的食物中选择?5 x 2 = 10。用具体的菜品例子试试,不信都写出来。
随机选择的一餐是素食友好型的概率有多大?
分子= 2 x 2 = 4;分母= 10。答案= 4/10 = 40%
The difference between a combination and a permutation is that order matters with permutations but not with combinations. If you think about the image above for a moment, you’ll see why the number of possible options is bigger with permutations than with combinations (if you run the numbers on choosing 7 from 200, you’ll see that the answer on the left is a 13 digit number while on the right it’s a 17 digit number, and if you ogle the formula you’ll see that there’s an extra term in the combination’s denominator that removes the effect of ordering after selection, dumping all the pills into a single container and shaking it). Fun fact: your so-called “combination” bike lock is actually a permutation-with-replacement bike lock. (Technically, a combination lock that opens with 1008 should also open with 0810 because order doesn’t matter.)
超越事件
你已经学了 3 章,突然你的课本把组合学忘得一干二净。就在你掌握所有这些组合和排列的窍门时(见上图),它们再也没有消息了。反而突然到处都是分布。那是怎么回事?
*考虑这个问题:*“你需要等 10 分钟以上公交车的概率有多大?”这将很难计算(以纳秒为单位?)也很痛苦,因为计算事件意味着你不能把时间当作一件连续的事情来处理。更糟糕的是,如果你有一个公共汽车司机,他有可能停下来抽根烟,这取决于公共汽车已经晚点了多长时间。你打算怎么列举呢?你不是。也许计数事件不是你在这里的朋友…
Your bus is here!
输入 基于频率的定义 ,其内容类似于“如果这是发生在无限个平行宇宙中的随机事件(由我指定的规则管理,呃,假设),其中有多少个公共汽车需要超过 10 分钟才能到达?”*(平行宇宙?!是啊,难怪我们统计学家有一双疯狂的眼睛。)*
然后——通常是很久以后——当你在你的贝叶斯统计教科书中碰到 主观定义 时,你就可以根据你感觉有可能发生的事情来编造分布。看看当我们剥夺你列举事件的能力时,你会得到什么样的扭曲的东西?嗯,幸运的是,对于生日问题,如果我们假设所有 366 个生日的概率是相等的 ,我们可以继续计数。咻!*
*什么,你不喜欢我的假设?战胜你自己——所有的统计都是为了简化假设——否则宇宙就是一个可怕的地方。如果你不喜欢我的假设,那么你需要想出一个不同的解决方案,因为我的对你没有好处。统计变得有创造性,因为我们都被允许做任何我们喜欢的假设。引用乔治·博克斯的话:“所有的解决方案都是错的,但是无论哪一个符合你愿意接受的假设,都可能对你有用。”
不是赞美
对于生日问题,我们需要的最后一块积木是补语,也就是通常所说的谦逊的 NOT。
P(非 A) = 1 — P(A)
这被解读为“一个事件(我们将其命名为事件 A,因为我们缺乏创造力)不发生的概率等于 100%减去该事件发生的概率。”**
那么掷骰子得不到 6 的概率有多大呢?1–1/6 = 5/6
好了,就这些。我们准备好解决生日问题了!
Why is the birthday problem also called the birthday paradox? The paradox has to do with the vast number of birthday possibilities in a group of people versus the surprising probably of a match. The paradox resolution is to flip the problem to think about unique birthdays.
生日问题
问题是什么来着?啊,这个:
如果你有 N 个人在一个小组里,至少有一个人是同一天生日的概率是多少?
所以,让我们用我们的乐高积木试一试…
生日分母
一个人有多少生日选择?366(见上文)。
N=30 人我们有多少生日选择?
我们有第一个人 366,第二个人 366,第三个人 366……第 n 个人 366。在所有的 and 处放一个乘法,瞧!
分母= 366 ^ N =天啊,这么多数字!如果 N 是 30,就是 77 个。这可不仅仅是一个 quattuorvigintillion。(给东西命名不是很好玩吗?)
生日分子
准备好头晕吧。我们必须计算所有不同的方式,以使任何两个人中至少有一个匹配。因此,人 1 有 366 个选项,人 29 有 1 个选项,因为他们与人 1 匹配,但也可能是人 2 和人 17 或两对,或者可能是三个人同一天生日,或者…或者…不,这将很快变得混乱,而我们跑来跑去,试图让所有的选项在我们的脑海中保持直线。
…这就是为什么这是一个有趣的家庭作业问题。你应该用你的头去撞它,直到你明白其中的诀窍。(或者求网上的作弊码。你就是这样找到这篇博文的吗?太酷了,我抓住你了。)
解决生日问题的诀窍!
与其数出人们过生日的所有方式,不如把问题重新措辞,数出一个简单得多的事情:相反!
p(至少有一个共同的生日)=
1 — P(所有生日都是唯一的)
所以我们需要找到这个问题的答案:“人群中没有任何生日分享的概率是多少?”换句话说,所有生日都是独一无二的概率有多大?
独特的生日分母
仍然是 366 ^ N。通过处理互补事件,我们转移了对分子的关注。分母毫发无损。
唯一生日分子
这是美丽而光荣的魔法发生的地方!
第一个人有 366 个生日选择(贪婪的猪),第二个人只有 365 个,因为我们强迫他们过不同的生日。如果第一个人出生在 10 月 8 日,我们强迫我们的房间里有 N 个不同的生日,我们会把所有 10 月 8 日生日的人拒之门外。每个申请房间的人都少了一个选择。
因此,第 1 个人有 366 个选项,第 2 个人有 365 个选项,第 3 个人有 364 个选项,第 N 个人有(367— N)个选项。
如果 N 是 30,放入乘号,嘣!
366 x 365 x 364 x 363 x 362 x 361 x 360 x 359 x 357 x 356 x 355 x 354 x 353 x 352 x 351 x 350 x 349 x 348 x 347 x 346 x 345 x 344 x 343 x 342 x 341 x 340 x 339 x 338 x 337 =…
呸,又是一个 77 位数的大数字。
但是,对我们来说幸运的是,如果我们友好地请求,计算机会替我们处理除法。
不是我们想要的可能性
p(所有 30 个生日都是唯一的)=(366 x 365 x 364 x 363 x 362 x 360 x 359 x 358 x 357 x 356 x 355 x 354 x 353 x 352 x 351 x 350 x 349 x 348 x 347 x 346 x 345 x 344 x 343 x 342 x 341 x 340 x 339 x 338 x 338
= 0.3(四舍五入到我的注意力范围)
终于,我们想要的答案来了!
p(30 人一组的共同生日)= 1 — 0.3 = 70%
一个 30 人的团队,有大约 70%的机会是同一天生日。
我希望你没有跟我赌 10 美元,说在一个 30 人的小组中,同一天出生的人比同一天出生的人更少……但是如果你赌了,我不会说不,如果你愿意把它捐给维基百科。
赌错了一方,是因为低估了找到一个没有重复房间的房间有多难——如果你是门口的保镖,负责赶走那些生日被占的人,用不了多久,你就会赶走第一个失望的人。
这取决于你有多少朋友…
在我的笔记本电脑上打开 2 分钟和 R ,我可以为 N 的任何一个选择画出答案。如果你想知道实际的数字(50%发生在 N = 23 时,99%发生在 N = 55 时),继续用在你的浏览器中用我下面丑陋但功能强大的小代码片段玩,不需要安装。
# Initialize settings for N = 1:
probability <- 0
numerator <- 366
denominator <- 366# Loops are terrible in R, let's make one anyways:
for (N in 2:80) {
numerator <- numerator * (366 + 1 - N)
denominator <- denominator * 366
p <- 1 - (numerator / denominator)
probability <- c(probability, p)
}# Look at the answer to 2 decimal places:
paste("For N =", 1:80, "prob is", round(probability, 2))# Plot!
plot(1:80, probability, xlab = "Group Size", ylab = "P(Shared birthdays)", type = "l", col = "red", main = "The Birthday Solution")
我们在这里学到了什么?
除了一些概率基础知识(比如不同的概率方法、补数、分子和分母的取值)和计数原则(求和规则和乘积规则)之外,这篇文章要讲的是为什么你会按照一定的顺序学习某些概念。
为什么数数在你的课本中是一件大事,然后突然消失了,让每个人都不高兴?事实证明,这主要是达到目的的一种手段,因为假设所有基本事件都同样可能发生,对于大多数专业人士来说太业余了,以至于无法接受… 甚至对于生日来说。有趣的是,当非专业人士想到我们这些统计人员整天都在做什么时,他们往往会想到算牌和扔硬币。有些谣言是很难抹杀的!
生日问题之所以成为统计学本科生经历中的一个经典问题,很大程度上是因为它给了你足够的痛苦来抓住一个关键点:初出茅庐的统计学家需要学习如何重新表述问题,以便他们的活动部分更容易处理。蛮力方法往往太难计算,所以我们学会了斜着看事情。如果你选择统计的生活,那就做好大量翻转和反转的准备。
这篇文章有意不同于我平时的文章。有时候尝试新的东西来看看人们是否喜欢它们是很好的——如果你想知道更多这是从哪里来的,喜欢和分享是你让我知道的方式。❤
感谢阅读!人工智能课程怎么样?
如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:
Enjoy the entire course playlist here: bit.ly/machinefriend
利用数据科学谱找到适合您的数据科学职业道路
并非所有的数据科学工作都是一样的
我刚大学毕业的时候,数据科学还没有作为一个职业存在。我受过精算师和统计师的训练,所以我去做了精算分析师。
当我第一次听说数据科学时,我已经从事过各种与数据相关的工作,所以当我申请第一个真正的数据科学职位时,我已经有了多年的相关经验。
虽然研究生数据科学角色确实存在,但这些角色可能很难获得,而且竞争非常激烈。更重要的是,顾名思义,他们通常是针对刚毕业的学生,而不是那些试图在职业生涯中期跳槽的人。
因此,很少有人的第一个数据相关职位是数据科学家。
更常见的情况是,人们从一个非数据科学家但与数据相关的角色开始,然后利用这一经验说服雇主他们有能力担任数据科学家。
与申请数据科学职位相比,针对非数据科学家的数据相关职位通常是更有效的进入数据科学的方法,特别是对于那些来自非数据背景的人来说,因为这些职位通常比数据科学家职位更常见,并且通常对技术和经验的要求更低。
这当然提出了一个问题:哪些与数据相关的角色是进入数据科学的最佳敲门砖?答案取决于你对数据科学的兴趣所在。
数据科学领域
数据科学通常被视为统计学和计算机科学的结合,根据这些学科对特定职位的相对重要性,在该行业中的角色也有所不同。我称之为数据科学光谱。
例如,更接近统计端的数据科学角色可能专注于使用统计和机器学习技术来从数据中获得洞察力,而更接近计算机科学端的角色可能更专注于开发机器学习模型,以作为人工智能系统的一部分进行部署。
在这个范围内,角色可以根据资格水平和所需的技术技能而有所不同。
我还将与数据相关的角色包括在内,这些角色虽然不是实际的数据科学职位,但与数据科学角色有一些重叠,可以为数据科学职业道路提供早期步骤。
因为数据科学角色可以存在于光谱上的任何一点,而非数据科学角色通常被错误地命名为“数据科学家”(反之亦然),所以仅根据职称来判断数据相关角色可能会产生误导。
然而,如果你看了足够多的数据相关职位的招聘广告,模式就开始出现了,使得识别各种类型的数据角色成为可能,而与职位无关。
七种类型的数据角色
在 2019 年 4 月 22 日至 2019 年 5 月 5 日期间,我从 LinkedIn 收集了 200 个数据相关角色的招聘广告(即,标题为数据科学家(100 个广告)、数据分析师(40 个广告)、商业智能分析师(20 个广告)、机器学习工程师(20 个广告)和数据工程师(20 个广告)的角色),横跨四个英语国家(澳大利亚、加拿大、英国和美国)。
我超越了“数据科学家”这个头衔的工作,考虑到了潜在的命名错误,以及不同雇主对一份工作具体要求的差异。
通过将 k-means 聚类应用于这些广告,使用选择标准作为特征,我能够将招聘广告分组为七个聚类或角色类型。这些是:
- 报告和 ETL 分析师;
- 数据科学通才;
- 洞察分析师;
- ML 研究数据科学家;
- ML 软件工程师;
- 平台和仓库工程师;和
- 数据科学专家。
我在我的文章中详细描述了这些角色:你是什么类型的数据科学家?。但是,下表总结了每种角色类型所需的典型学位要求,包括级别和领域以及关键技术。
根据他们在数据科学领域所处的位置,将上面列出的七种角色类型进行分组,并根据所涉及的技术要求级别对他们进行排名,从而出现了两条进入数据科学的职业道路。
职业道路#1:统计和洞察数据科学
对于那些有统计或数学背景的人来说,洞察分析师的角色可以提供进入众所周知的数据科学大门的一只脚,而不需要硕士学位。
Insights analyst 角色通常以“数据分析师”的头衔进行广告宣传,尽管在一些组织中也可能以“数据科学家”的职位进行广告宣传,并且通常专注于提取、争论和分析数据,以便提供见解并生成报告和/或仪表板。这些技能都与在统计领域工作的数据科学家通常需要的技能相重叠。
insights 分析师角色和真正的数据科学职位之间的主要区别在于,insights 分析师通常不需要拥有数据科学家所需的机器学习和统计技术方面的高级知识。然而,这并不意味着这些技能不能在担任这样的角色时得到发展(无论是通过开发涉及这些技能的工作项目,还是通过额外的学习)。
随着相关技能和经验的发展,洞察分析师角色自然会成为数据科学通才,而这又自然会成为数据科学专家角色。
职业道路#2:工程和开发数据科学
另一方面,对于那些更接近计算机科学的人来说,报告和 ETL 分析师、平台和仓库工程师或 ML 软件工程师都可以提供数据科学的切入点,这取决于你的计算机科学和软件开发技能水平。
报告和 ETL 分析师(通常以“数据分析师”或“商业智能分析师”的头衔宣传),以及平台和仓库工程师(通常宣传为“数据工程师”)角色都非常强调能够管理数据库、仓库或平台中的数据。
两者通常只需要计算机科学学士学位,报告和 ETL 分析师倾向于更关注传统(关系型)数据库,平台和仓库工程师更关注更现代的(非关系型)大数据平台。
这些角色通常位于数据科学/分析团队中,让他们有机会与数据科学家合作,尽管在机器学习和统计方面没有相同的技术要求。然而,根据团队经理的态度,这些角色的人可能有能力随着时间的推移提升技能并过渡到数据科学角色。
平台和仓库工程师角色也经常有很强的软件开发方面,导致这些角色自然地进入 ML 软件工程师角色(通常在标题“机器学习工程师”下广告)。
机器学习软件工程师和机器学习研究数据科学家本质上是一枚硬币的两面,前者专注于模型部署,后者专注于模型开发。因此,随着技能和资格的增长,机器学习软件工程师角色可以自然地成为机器学习研究数据科学家。
上面介绍的两条职业道路绝不是进入数据科学的唯一职业道路。仅仅因为你开始是一名报告和 ETL 分析师,并不意味着你不会有一天成为数据科学专家。
这不是捉鬼敢死队,溪流可以穿越。
举例来说,我自己的职业生涯包括在成为数据科学通才之前担任洞察分析师,然后成为 ML Research 数据科学家。
然而,对于那些希望过渡到数据科学,或在数据科学阶梯上向上移动的人来说,这些职业道路提供了一个模板来指导你,并提供了一种方法来确定你的技能在数据科学层次中的位置。
因此,如果你正在寻找数据科学方面的角色,找到你的技能最接近的角色类型,并开始搜索和申请该类型的数据相关角色。一旦你得到了第一个角色,看看接下来是什么角色,然后开始朝那个方向努力。
数据科学是一个旅程,就像任何其他旅程一样,如果你有一张地图,这将容易得多。
Genevieve Hayes 博士是数据科学家、教育家和人工智能及分析专家,拥有Genevieve Hayes Consulting。你可以在LinkedIn或者Twitter上关注她。她还是 价值驱动数据科学 的主持人,这是一个每月两次的播客,面向希望最大化其数据和数据团队价值的企业。
想要发掘企业数据的价值,但不知道从哪里开始?**下载免费的数据科学项目发现指南。
在 R 中使用来自 Google 的新 Turbo 调色板
注意:这是最初发布在我的博客上的。
几天前,一名来自谷歌的人工智能工程师发布了一个他开发的新调色板。这个调色板“Turbo”和广泛使用的“jet”调色板一样丰富多彩,但感觉上是统一的,并且像的绿色调色板一样对色盲友好。他分享了使用调色板的 python 和 C 代码,但 R 中没有任何内容。其他人从开始发布 R 中的代码,尽管还需要一点工作来使其可用于我最喜欢的图形包 ggplot2。这是一篇关于如何快速开始在 ggplots 中使用 Turbo 的短文。
编辑:这篇文章的代码现在也可以在这里找到:https://github.com/dbaranger/turbo_palette_R
首先,我们需要颜色的实际值,这是在 sRGB 给出的。然后我把这些转换成十六进制代码,这就是 ggplot2 使用的。
install.packages("colorspace")
library(colorspace)A<-c((0.18995),(0.19483),(0.19956),(0.20415),(0.2086),(0.21291),(0.21708),(0.22111),(0.225),(0.22875),(0.23236),(0.23582),(0.23915),(0.24234),(0.24539),(0.2483),(0.25107),(0.25369),(0.25618),(0.25853),(0.26074),(0.2628),(0.26473),(0.26652),(0.26816),(0.26967),(0.27103),(0.27226),(0.27334),(0.27429),(0.27509),(0.27576),(0.27628),(0.27667),(0.27691),(0.27701),(0.27698),(0.2768),(0.27648),(0.27603),(0.27543),(0.27469),(0.27381),(0.27273),(0.27106),(0.26878),(0.26592),(0.26252),(0.25862),(0.25425),(0.24946),(0.24427),(0.23874),(0.23288),(0.22676),(0.22039),(0.21382),(0.20708),(0.20021),(0.19326),(0.18625),(0.17923),(0.17223),(0.16529),(0.15844),(0.15173),(0.14519),(0.13886),(0.13278),(0.12698),(0.12151),(0.11639),(0.11167),(0.10738),(0.10357),(0.10026),(0.0975),(0.09532),(0.09377),(0.09287),(0.09267),(0.0932),(0.09451),(0.09662),(0.09958),(0.10342),(0.10815),(0.11374),(0.12014),(0.12733),(0.13526),(0.14391),(0.15323),(0.16319),(0.17377),(0.18491),(0.19659),(0.20877),(0.22142),(0.23449),(0.24797),(0.2618),(0.27597),(0.29042),(0.30513),(0.32006),(0.33517),(0.35043),(0.36581),(0.38127),(0.39678),(0.41229),(0.42778),(0.44321),(0.45854),(0.47375),(0.48879),(0.50362),(0.51822),(0.53255),(0.54658),(0.56026),(0.57357),(0.58646),(0.59891),(0.61088),(0.62233),(0.63323),(0.64362),(0.65394),(0.66428),(0.67462),(0.68494),(0.69525),(0.70553),(0.71577),(0.72596),(0.7361),(0.74617),(0.75617),(0.76608),(0.77591),(0.78563),(0.79524),(0.80473),(0.8141),(0.82333),(0.83241),(0.84133),(0.8501),(0.85868),(0.86709),(0.8753),(0.88331),(0.89112),(0.8987),(0.90605),(0.91317),(0.92004),(0.92666),(0.93301),(0.93909),(0.94489),(0.95039),(0.9556),(0.96049),(0.96507),(0.96931),(0.97323),(0.97679),(0.98),(0.98289),(0.98549),(0.98781),(0.98986),(0.99163),(0.99314),(0.99438),(0.99535),(0.99607),(0.99654),(0.99675),(0.99672),(0.99644),(0.99593),(0.99517),(0.99419),(0.99297),(0.99153),(0.98987),(0.98799),(0.9859),(0.9836),(0.98108),(0.97837),(0.97545),(0.97234),(0.96904),(0.96555),(0.96187),(0.95801),(0.95398),(0.94977),(0.94538),(0.94084),(0.93612),(0.93125),(0.92623),(0.92105),(0.91572),(0.91024),(0.90463),(0.89888),(0.89298),(0.88691),(0.88066),(0.87422),(0.8676),(0.86079),(0.8538),(0.84662),(0.83926),(0.83172),(0.82399),(0.81608),(0.80799),(0.79971),(0.79125),(0.7826),(0.77377),(0.76476),(0.75556),(0.74617),(0.73661),(0.72686),(0.71692),(0.7068),(0.6965),(0.68602),(0.67535),(0.66449),(0.65345),(0.64223),(0.63082),(0.61923),(0.60746),(0.5955),(0.58336),(0.57103),(0.55852),(0.54583),(0.53295),(0.51989),(0.50664),(0.49321),(0.4796))
B<-c((0.07176),(0.08339),(0.09498),(0.10652),(0.11802),(0.12947),(0.14087),(0.15223),(0.16354),(0.17481),(0.18603),(0.1972),(0.20833),(0.21941),(0.23044),(0.24143),(0.25237),(0.26327),(0.27412),(0.28492),(0.29568),(0.30639),(0.31706),(0.32768),(0.33825),(0.34878),(0.35926),(0.3697),(0.38008),(0.39043),(0.40072),(0.41097),(0.42118),(0.43134),(0.44145),(0.45152),(0.46153),(0.47151),(0.48144),(0.49132),(0.50115),(0.51094),(0.52069),(0.5304),(0.54015),(0.54995),(0.55979),(0.56967),(0.57958),(0.5895),(0.59943),(0.60937),(0.61931),(0.62923),(0.63913),(0.64901),(0.65886),(0.66866),(0.67842),(0.68812),(0.69775),(0.70732),(0.7168),(0.7262),(0.73551),(0.74472),(0.75381),(0.76279),(0.77165),(0.78037),(0.78896),(0.7974),(0.80569),(0.81381),(0.82177),(0.82955),(0.83714),(0.84455),(0.85175),(0.85875),(0.86554),(0.87211),(0.87844),(0.88454),(0.8904),(0.896),(0.90142),(0.90673),(0.91193),(0.91701),(0.92197),(0.9268),(0.93151),(0.93609),(0.94053),(0.94484),(0.94901),(0.95304),(0.95692),(0.96065),(0.96423),(0.96765),(0.97092),(0.97403),(0.97697),(0.97974),(0.98234),(0.98477),(0.98702),(0.98909),(0.99098),(0.99268),(0.99419),(0.99551),(0.99663),(0.99755),(0.99828),(0.99879),(0.9991),(0.99919),(0.99907),(0.99873),(0.99817),(0.99739),(0.99638),(0.99514),(0.99366),(0.99195),(0.98999),(0.98775),(0.98524),(0.98246),(0.97941),(0.9761),(0.97255),(0.96875),(0.9647),(0.96043),(0.95593),(0.95121),(0.94627),(0.94113),(0.93579),(0.93025),(0.92452),(0.91861),(0.91253),(0.90627),(0.89986),(0.89328),(0.88655),(0.87968),(0.87267),(0.86553),(0.85826),(0.85087),(0.84337),(0.83576),(0.82806),(0.82025),(0.81236),(0.80439),(0.79634),(0.78823),(0.78005),(0.77181),(0.76352),(0.75519),(0.74682),(0.73842),(0.73),(0.7214),(0.7125),(0.7033),(0.69382),(0.68408),(0.67408),(0.66386),(0.65341),(0.64277),(0.63193),(0.62093),(0.60977),(0.59846),(0.58703),(0.57549),(0.56386),(0.55214),(0.54036),(0.52854),(0.51667),(0.50479),(0.49291),(0.48104),(0.4692),(0.4574),(0.44565),(0.43399),(0.42241),(0.41093),(0.39958),(0.38836),(0.37729),(0.36638),(0.35566),(0.34513),(0.33482),(0.32473),(0.31489),(0.3053),(0.29599),(0.28696),(0.27824),(0.26981),(0.26152),(0.25334),(0.24526),(0.2373),(0.22945),(0.2217),(0.21407),(0.20654),(0.19912),(0.19182),(0.18462),(0.17753),(0.17055),(0.16368),(0.15693),(0.15028),(0.14374),(0.13731),(0.13098),(0.12477),(0.11867),(0.11268),(0.1068),(0.10102),(0.09536),(0.0898),(0.08436),(0.07902),(0.0738),(0.06868),(0.06367),(0.05878),(0.05399),(0.04931),(0.04474),(0.04028),(0.03593),(0.03169),(0.02756),(0.02354),(0.01963),(0.01583))
C<-c((0.23217),(0.26149),(0.29024),(0.31844),(0.34607),(0.37314),(0.39964),(0.42558),(0.45096),(0.47578),(0.50004),(0.52373),(0.54686),(0.56942),(0.59142),(0.61286),(0.63374),(0.65406),(0.67381),(0.693),(0.71162),(0.72968),(0.74718),(0.76412),(0.7805),(0.79631),(0.81156),(0.82624),(0.84037),(0.85393),(0.86692),(0.87936),(0.89123),(0.90254),(0.91328),(0.92347),(0.93309),(0.94214),(0.95064),(0.95857),(0.96594),(0.97275),(0.97899),(0.98461),(0.9893),(0.99303),(0.99583),(0.99773),(0.99876),(0.99896),(0.99835),(0.99697),(0.99485),(0.99202),(0.98851),(0.98436),(0.97959),(0.97423),(0.96833),(0.9619),(0.95498),(0.94761),(0.93981),(0.93161),(0.92305),(0.91416),(0.90496),(0.8955),(0.8858),(0.8759),(0.86581),(0.85559),(0.84525),(0.83484),(0.82437),(0.81389),(0.80342),(0.79299),(0.78264),(0.7724),(0.7623),(0.75237),(0.74265),(0.73316),(0.72393),(0.715),(0.70599),(0.69651),(0.6866),(0.67627),(0.66556),(0.65448),(0.64308),(0.63137),(0.61938),(0.60713),(0.59466),(0.58199),(0.56914),(0.55614),(0.54303),(0.52981),(0.51653),(0.50321),(0.48987),(0.47654),(0.46325),(0.45002),(0.43688),(0.42386),(0.41098),(0.39826),(0.38575),(0.37345),(0.3614),(0.34963),(0.33816),(0.32701),(0.31622),(0.30581),(0.29581),(0.28623),(0.27712),(0.26849),(0.26038),(0.2528),(0.24579),(0.23937),(0.23356),(0.22835),(0.2237),(0.2196),(0.21602),(0.21294),(0.21032),(0.20815),(0.2064),(0.20504),(0.20406),(0.20343),(0.20311),(0.2031),(0.20336),(0.20386),(0.20459),(0.20552),(0.20663),(0.20788),(0.20926),(0.21074),(0.2123),(0.21391),(0.21555),(0.21719),(0.2188),(0.22038),(0.22188),(0.22328),(0.22456),(0.2257),(0.22667),(0.22744),(0.228),(0.22831),(0.22836),(0.22811),(0.22754),(0.22663),(0.22536),(0.22369),(0.22161),(0.21918),(0.2165),(0.21358),(0.21043),(0.20706),(0.20348),(0.19971),(0.19577),(0.19165),(0.18738),(0.18297),(0.17842),(0.17376),(0.16899),(0.16412),(0.15918),(0.15417),(0.1491),(0.14398),(0.13883),(0.13367),(0.12849),(0.12332),(0.11817),(0.11305),(0.10797),(0.10294),(0.09798),(0.0931),(0.08831),(0.08362),(0.07905),(0.07461),(0.07031),(0.06616),(0.06218),(0.05837),(0.05475),(0.05134),(0.04814),(0.04516),(0.04243),(0.03993),(0.03753),(0.03521),(0.03297),(0.03082),(0.02875),(0.02677),(0.02487),(0.02305),(0.02131),(0.01966),(0.01809),(0.0166),(0.0152),(0.01387),(0.01264),(0.01148),(0.01041),(0.00942),(0.00851),(0.00769),(0.00695),(0.00629),(0.00571),(0.00522),(0.00481),(0.00449),(0.00424),(0.00408),(0.00401),(0.00401),(0.0041),(0.00427),(0.00453),(0.00486),(0.00529),(0.00579),(0.00638),(0.00705),(0.0078),(0.00863),(0.00955),(0.01055))
turbo_colormap_data<-cbind(A,B,C)
turbo_colormap_data_sRGB<-sRGB(turbo_colormap_data)
turbo_colormap_data_HEX = hex(turbo_colormap_data_sRGB)
现在让我们来看看调色板:
install.packages("pals")
library(pals)TurboPalette<-colorRampPalette(colors = turbo_colormap_data_HEX ,space="rgb", interpolate = "spline")
pal.test(TurboPalette)
整体 Turbo 表现相当不错。它缺少的一个地方是亮度曲线(灰色线,右下角)。亮度在调色板的中途达到峰值,因此使用整个调色板进行连续缩放可能不是在所有情况下都能很好地工作。
最后,我编写了一个更短的助手函数,以便更容易地将调色板集成到 ggplot2 中:
Turbo <- function(pal.min = 0,pal.max = 1,out.colors = NULL,pal = turbo_colormap_data_HEX,reverse = F) {
# pal.min = lower bound of the palette to use [0,1]
# pal.max = upper bound of the palette [0,1]
# out.colors = specify the number of colors to return if out.colors = 1, will return pal.min color. if unspecified, will return all the colors in the original palette that fall within the min and max boundaries
# pal = vector of colors (HEX) in palette
# reverse = flip palette T/F - performed as last step
if(pal.min == 0){pal.start = 1}
if(pal.min > 0){pal.start = round(length(pal)*pal.min) }
pal.end = round(length(pal)*pal.max )
out = pal[pal.start:pal.end]
if(!is.null(out.colors)){
pal2 = colorRampPalette(colors = out ,space="rgb", interpolate = "linear")
out = pal2(out.colors)
}
if(reverse == T){out = rev(out)}
return(out)
}
该函数将调色板分成子集,如果需要,将输出指定数量的颜色(对分类变量有用)。
现在让我们来兜一圈吧!下面我从 http://r-statistics.co/的中借用分类变量和一个连续变量。
分类变量是一个华夫图。请注意,下面我已经注释掉了原来的颜色,它是一个 colorBrewer 刻度,并替换为一个手动刻度,其颜色由我的新 Turbo()函数提供:
var <- mpg$class # the categorical data
nrows <- 10
df <- expand.grid(y = 1:nrows, x = 1:nrows)
categ_table <- round(table(var) * ((nrows*nrows)/(length(var))))
categ_table
df$category <- factor(rep(names(categ_table), categ_table)) ggplot(df, aes(x = x, y = y, fill = category)) +
geom_tile(color = "black", size = 0.5) +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0), trans = 'reverse') +
####scale_fill_brewer(palette = "Set3") +####
scale_fill_manual(values = Turbo(out.colors = 7))+ ## This is the change
labs(title="Waffle Chart", subtitle="'Class' of vehicles",
caption="Source: mpg") +
theme(panel.border = element_rect(size = 2),
plot.title = element_text(size = rel(1.2)),
axis.text = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
legend.title = element_blank(),
legend.position = "right")
我已经用“scale _ fill _ manual(values = Turbo(out . colors = 7)”替换了“scale _ fill _ brewer(palette = " set 3"”。
如果我们不喜欢它的颜色,两端很暗,我们可以改变调色板的最小值和最大值:“scale _ fill _ manual(values = Turbo(out . colors = 7,pal.min = .1,pal.max = .9)”
好多了!现在让我们用一个连续的尺度来尝试一下,像热图,再次借用 http://r-statistics.co/。
library(ggplot2)
library(plyr)
library(scales)
library(zoo)
df <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/yahoo.csv")
df$date <- as.Date(df$date) # format date
df <- df[df$year >= 2012, ] # filter red years
# Create Month Week
df$yearmonth <- as.yearmon(df$date)
df$yearmonthf <- factor(df$yearmonth)
df <- ddply(df,.(yearmonthf), transform, monthweek=1+week-min(week)) # compute week number of month
df <- df[, c("year", "yearmonthf", "monthf", "week", "monthweek", "weekdayf", "VIX.Close")]
# Plot
ggplot(df, aes(monthweek, weekdayf, fill = VIX.Close)) +
geom_tile(colour = "white") +
facet_grid(year~monthf) +
###scale_fill_gradient(low="red", high="green") +##
scale_fill_gradientn(colours = Turbo()) + ## this is new
labs(x="Week of Month",
y="",
title = "Time-Series Calendar Heatmap",
subtitle="Yahoo Closing Price",
fill="Close")
请注意,我已经将“scale_fill_gradient(low="red ",high="green ")”替换为“scale _ fill _ gradient n(colours = Turbo())”。
上面图的一个问题是绿色“弹出”太多,所以我们注意中间值,而不是最大值。这是因为,正如上面的第一张图所示,亮度曲线在调色板的中间达到峰值,在绿色部分。因此,我们可以用颜色的子集来绘图,并翻转调色板,使较浅的颜色更高。这里我把填充改为:“scale _ fill _ gradient n(colors = Turbo(pal . min = . 6,pal.max = 1,reverse = T))
现在,哪一天包含最高值就清楚多了。
就这些了,祝大家策划愉快!
使用熊猫数据框作为数据库。
让我们了解如何使用熊猫数据框作为数据库。
Image Credits: Usejournal
在开始之前,让我快速介绍一下 pandas data frame:它是一个 python 库,为 python 编程语言的数据分析工具提供了高性能和易于使用的数据结构。下面的文章解释了在熊猫数据框上执行的原始操作。
让我们用 Python 来计算我的熊猫数据框架兼职收入。
towardsdatascience.com](/manipulating-the-data-with-pandas-using-python-be6c5dfabd47)
让我们开始吧,这是一个编程教程,所以我推荐你们和我一起练习。我喜欢使用谷歌笔记本或 T2 笔记本。简而言之,我将教你们如何使用熊猫数据框作为数据库来存储数据并对其执行一些基本操作。与数据库相比,此数据框具有几乎所有的功能。它几乎类似于一个数据库。也要获得完整代码请访问我的 GitHub 资源库 下面:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/Tanu-N-Prabhu/Python/blob/master/Using_the_Pandas_Data_Frame_as_a_Database_.ipynb)
本教程将遵循的步骤有
- 创建熊猫数据框
2.向数据框添加行
3.从数据框中删除行
4.从数据框中访问行的值
5.更改数据框中行的值
让我们看看如何执行上面声明的所有步骤
1。创建熊猫数据框
要创建数据框,首先需要导入数据框,然后必须按如下所示的顺序指定列名和值:
import pandas as pd
让我们创建一个新的数据框。我正在存储公司名称、创始人、创办人和员工人数。您可以在数据框中存储您选择的数据。
df = pd.DataFrame({‘Company Name’:[‘Google’, ‘Microsoft’, ‘SpaceX’,‘Amazon’,‘Samsung’]‘Founders’:[‘Larry Page, Sergey Brin’, ‘Bill Gates, Paul Allen’,’Elon Musk’,’Jeff Bezos’, ‘Lee Byung-chul’],‘Founded’: [1998, 1975, 2002, 1994, 1938],‘Number of Employees’: [‘103,459’, ‘144,106’, ‘6,500’, ‘647,500’, ‘320,671’]})df **# used to print the data frame df, or use print(df) both are same**
不要担心,这里没有什么复杂的,只是价值观可能会让你困惑,因为它们只是公司名称、创始人、创办人等。小心括号,如果你弄乱了它,你的生活会变得很痛苦。
2.向数据框添加新行
假设现在您想要向数据框添加一个新行,您所能做的就是将新行添加到数据框的末尾或您选择的任何特定位置。
情况 1:在数据帧的末尾添加一行:
要将行追加到数据框的末尾,您需要使用“ append 方法,传递您想要追加的值。以下是 append 函数的官方文档:
[## 熊猫。DataFrame.append - pandas 0.25.3 文档
将 other 的行追加到调用方的末尾,返回一个新对象。“其他”中不在调用者中的列是…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html)
让我们用新值创建一个新的数据框,然后将其追加到现有数据框的末尾。
df1 = pd.DataFrame({‘Company Name’:[‘WhatsApp’],
‘Founders’:[‘Jan Koum, Brian Acton’],
‘Founded’: [2009],
‘Number of Employees’: [‘50’] })df1
df1 是我们想要添加到现有数据帧 df 的新数据帧
df = df.append(df1, ignore_index=True)df
案例 2:在特定位置添加新行
现在让我们在索引 3 处添加一个新的值行,意思是在“SpaceX”公司下面。为此,我们可以通过指定索引和要添加的值来使用 pandas " iloc 方法。以下是 iloc 方法的文档
[## 熊猫。DataFrame.iloc - pandas 0.25.3 文档
完全基于整数位置的索引,用于按位置选择。主要基于整数位置(从到的…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iloc.html)
df.iloc[3] = [‘YouTube’, ‘Chad Hurley, Steve Chen, Jawed Karim’, 2005, ‘2,800’]df
在 iloc 的帮助下,我们可以在数据框内的任意位置添加新行。
3.从数据框中删除行
有时,您可能很少需要从数据库或数据框中移除不必要的数据。为此,pandas 中的“ drop 方法”完成了这项工作。让我们来看两种情况,比如删除带有索引的行和借助值删除行。下面是熊猫下降方法的文档:
[## 熊猫。data frame . drop-pandas 0 . 25 . 3+0 . g 851328575 . dirty 文档
从行或列中删除指定的标签。通过指定标签名称和相应的轴来删除行或列,或者…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop.html)
案例 1:删除带有索引的行
现在,这可以通过在 drop 方法中提到索引来实现
df = df.drop([df.index[5]])df
如上所示,索引 5,即 WhatsApp 公司的行被完全删除。
案例 2:借助值删除一行。
现在让我们看看如何删除一行及其值。
df = df[df.Founders != ‘Chad Hurley, Steve Chen, Jawed Karim’]df
现在 YouTube company 的行在提供其值的帮助下被删除。
4.从数据框中访问行的值
从数据框中访问值非常简单,您所要做的就是使用“ loc 方法”。loc 方法接受索引作为参数,通过指定它,您可以从中检索值。以下是 loc 方法的文档:
[## 熊猫。data frame . loc-pandas 0 . 25 . 3+0 . g 851328575 . dirty 文档
通过标签或布尔数组访问一组行和列。主要基于标签,但也可用于…
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.loc.html)
现在,让我们假设我们需要访问第三行的值,调用 loc[2] ,你的工作就完成了。这些行从第 0 个索引开始存储,因此第 3 行索引是 2 (0,1,2)。
df.loc[2]**Company Name SpaceX
Founders Elon Musk
Founded 2002
Number of Employees 6,500
Name: 2, dtype: object**
5.更改数据框中行的值
这可以通过使用“at”方法来完成。要使用“at”方法,您只需指定它的索引和列名的位置,然后指定需要更改的值。以下是“at”方法的文档:
[## 熊猫。DataFrame.at - pandas 0.23.1 文档
编辑描述
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/version/0.23/generated/pandas.DataFrame.at.html)
举个例子,假设我想把微软的员工人数改成 20 万人(对不起微软为了我的教程我不得不这么做)。
df.at[1, ‘Number of Employees’] = ‘200,000’df
This is after the change of value (Microsoft)
This is before the change of value (Microsoft)
这就是如何将熊猫数据框用作数据库。您已经成功完成了本教程的所有步骤。我希望你喜欢读它。在数据帧上可以执行更多的操作、功能或方法。我不能一口气解释所有的操作,如果你们需要更多关于这些概念的教程,请告诉我。此外,如果你们有任何疑问,请联系我或在评论区发表回复,我会尽快回复。不要忘记阅读我在本教程中提供的熊猫官方数据框的所有文档链接。在那之前再见。祝你愉快。
使用熊猫“重采样”功能
Photo by JESHOOTS.COM on Unsplash
改变过去的下一件最好的事情——将它聚合起来。熊猫重采样功能的技术介绍。
T 他的文章是对用于日期时间操作的熊猫重采样功能的技术方面的介绍。我希望它能成为那些不太愿意去挖掘熊猫源代码的人的一个可读的伪文档来源!
如果您想查看用于生成示例的代码并查看本文中未包含的更多示例,请单击此处的链接。
我们开始吧!
什么是重采样?
当给定一个以某个时间间隔记录的数据集,并且您想将时间间隔更改为其他时间间隔时,重新采样是必要的。例如,您可以将每月数据聚合为年度数据,或者将每小时数据向上采样为分钟数据。
重采样的语法非常简单:
<DataFrame or Series>.**resample(arguments).<aggregate function>**
我将深入探讨什么是参数以及如何使用它们,但首先这里有一个基本的、现成的演示。您将需要一个日期时间类型的索引或列来执行以下操作:
# Given a Series object called data with some number value per date
>>>
╔═══════════════════════╦══════╗
║ date ║ val ║
╠═══════════════════════╬══════╣
║ 2000-01-01 00:00:00 ║ 0 ║
║ 2000-01-01 00:01:00 ║ 2 ║
║ 2000-01-01 00:02:00 ║ 4 ║
║ 2000-01-01 00:03:00 ║ 6 ║
║ 2000-01-01 00:04:00 ║ 8 ║
║ 2000-01-01 00:05:00 ║ 10 ║
║ 2000-01-01 00:06:00 ║ 12 ║
║ 2000-01-01 00:07:00 ║ 14 ║
║ 2000-01-01 00:08:00 ║ 16 ║
╚═══════════════════════╩══════╝
Freq: T, dtype: int64# We can **resample this** to every other minute instead and aggregate by summing the intermediate rows:
data.resample('2min').sum()>>>
╔═════════════════════╦══════╗
║ date ║ val ║
╠═════════════════════╬══════╣
║ 2000-01-01 00:00:00 ║ 2 ║
║ 2000-01-01 00:02:00 ║ 10 ║
║ 2000-01-01 00:04:00 ║ 18 ║
║ 2000-01-01 00:06:00 ║ 26 ║
║ 2000-01-01 00:08:00 ║ 16 ║
╚═════════════════════╩══════╝Freq: 2T, dtype: int64
现在,我们已经对重采样有了基本的了解,让我们进入代码!
争论
按照源代码的顺序:
pd.DataFrame.resample
(
**rule,**
how=None,
**axis=0,** fill_method=None,
**closed=None,
label=None,**
convention=”start”,
kind=None, **
loffset=None,**
limit=None,
**base=0,
on=None,**
**level=None** )
我已经大胆提出了我将涉及的论点。其余部分要么被弃用,要么用于句点,而不是日期时间分析,我在本文中将不再赘述。
1.“规则”参数
string
包含规则别名和/或数字
这是重采样的核心。您在这里输入的字符串决定了数据重新采样的时间间隔,如下行的粗体部分所示:
data.resample('**2min**').sum()
如您所见,您可以在字符串前插入浮点数或整数来更改频率。您甚至可以在非常特定的时间内将多对浮点/字符串组合在一起!例如:
'3min' or '3T' = 3 minutes'SMS' = Two times a month'1D3H.5min20S' = One Day, 3 hours, .5min(30sec) + 20sec
为了省去查找重采样字符串的麻烦,我发布了下表:
From Pandas documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases
一旦你加入了你的规则,你需要决定如何减少旧的数据点或者填充新的数据点。该函数紧接在重采样函数调用之后:
data.resample('2min').**sum()**
重采样有两种:
- 下采样 —在更宽的时间范围内(从几个月到几年)重新采样
- 这非常简单,因为它可以使用所有的 groupby 聚合函数,包括 mean()、min()、max()、sum() 等等。
- 在下采样中,行数从下降到。
2 .上采样 —在更短的时间内(从小时到分钟)重新采样
这将导致额外的空行,因此您有以下选项用数值填充这些空行:
1\. ffill() or pad()
2\. bfill() or backfill()
- **‘向前填充’或‘填充’—**使用最后一个已知值填充新值。
- **‘回填’—**使用下一个已知值来填充新值。
- 您还可以使用不带参数的 asfreq() 函数用 nan 填充**。这将产生包含 nan 的新数据点,您可以稍后使用 fillna() 函数。**
以下是向前和向后填充的一些演示:
Starting with months table:
╔════════════╦═════╗
║ date ║ val ║
╠════════════╬═════╣
║ 2000-01-31 ║ 0 ║
║ 2000-02-29 ║ 2 ║
║ 2000-03-31 ║ 4 ║
║ 2000-04-30 ║ 6 ║
║ 2000-05-31 ║ 8 ║
╚════════════╩═════╝print('Forward Fill')
print(months.resample('SMS').ffill())╔════════════╦═════╗
║ date ║ val ║
╠════════════╬═════╣
║ 2000-01-15 ║ NaN ║
║ 2000-02-01 ║ 0.0 ║
║ 2000-02-15 ║ 0.0 ║
║ 2000-03-01 ║ 2.0 ║
║ 2000-03-15 ║ 2.0 ║
║ 2000-04-01 ║ 4.0 ║
║ 2000-04-15 ║ 4.0 ║
║ 2000-05-01 ║ 6.0 ║
║ 2000-05-15 ║ 6.0 ║
╚════════════╩═════╝# Alternative to ffill is bfill (backward fill) that takes value of next existing months point
print('Backward Fill')
print(months.resample('SMS').bfill())╔════════════╦═════╗
║ date ║ val ║
╠════════════╬═════╣
║ 2000-01-15 ║ 0 ║
║ 2000-02-01 ║ 2 ║
║ 2000-02-15 ║ 2 ║
║ 2000-03-01 ║ 4 ║
║ 2000-03-15 ║ 4 ║
║ 2000-04-01 ║ 6 ║
║ 2000-04-15 ║ 6 ║
║ 2000-05-01 ║ 8 ║
║ 2000-05-15 ║ 8 ║
╚════════════╩═════╝
2.“关闭”参数
'left', 'right', or None
我将在这里包括他们的文档注释,因为它相当简洁地描述了基础知识。
仓区间的哪一侧是封闭的。除了“M”、“A”、“Q”、“BM”、“T20”、“BA”、“BQ”和“W”之外,所有频率偏移的默认值都是“左”。
closed 参数告诉哪一侧被包括在内,‘closed’是在每个时间间隔的计算中包括的一侧(意味着另一侧不包括在内)。你可以在这里看到它的表现:
# Original Table 'minutes'
╔═════════════════════╦═════╗
║ date ║ val ║
╠═════════════════════╬═════╣
║ 2000-01-01 00:00:00 ║ 0 ║
║ 2000-01-01 00:01:00 ║ 2 ║
║ 2000-01-01 00:02:00 ║ 4 ║
║ 2000-01-01 00:03:00 ║ 6 ║
║ 2000-01-01 00:04:00 ║ 8 ║
║ 2000-01-01 00:05:00 ║ 10 ║
║ 2000-01-01 00:06:00 ║ 12 ║
║ 2000-01-01 00:07:00 ║ 14 ║
║ 2000-01-01 00:08:00 ║ 16 ║
╚═════════════════════╩═════╝# The default is closed='left'
df=pd.DataFrame()
df['left'] = minutes.resample('2min').sum()
df['right'] = minutes.resample('2min',closed='right').sum()
df
>>>╔═════════════════════╦══════╦═══════╗
║ index ║ left ║ right ║
╠═════════════════════╬══════╬═══════╣
║ 2000-01-01 00:00:00 ║ 2 ║ 6.0 ║
║ 2000-01-01 00:02:00 ║ 10 ║ 14.0 ║
║ 2000-01-01 00:04:00 ║ 18 ║ 22.0 ║
║ 2000-01-01 00:06:00 ║ 26 ║ 30.0 ║
║ 2000-01-01 00:08:00 ║ 16 ║ NaN ║
╚═════════════════════╩══════╩═══════╝
3.“标签”参数
'left', 'right', or None
同样,文档非常有用。
用哪个箱柜边缘标签来标记桶。除了“M”、“A”、“Q”、“BM”、“BA”、“BQ”和“W”之外,所有频率偏移的默认值都是“左”。
此参数不会更改基础计算,它只是在执行聚合后根据所需的边重新标记输出。
df=pd.DataFrame()
# Label default is left
df['left'] = minutes.resample('2min').sum()
df['right'] = minutes.resample('2min',label='right').sum()
df>>>╔═════════════════════╦══════╦═══════╗
║ ║ left ║ right ║
╠═════════════════════╬══════╬═══════╣
║ 2000-01-01 00:00:00 ║ 2 ║ NaN ║
║ 2000-01-01 00:02:00 ║ 10 ║ 2.0 ║
║ 2000-01-01 00:04:00 ║ 18 ║ 10.0 ║
║ 2000-01-01 00:06:00 ║ 26 ║ 18.0 ║
║ 2000-01-01 00:08:00 ║ 16 ║ 26.0 ║
╚═════════════════════╩══════╩═══════╝
4.“Loffset”参数
string
匹配规则符号。
这个论点也是不言自明的。它没有改变任何计算,只是在指定的时间内移动标签。
df=pd.DataFrame()
df['no_offset'] = minutes.resample('2min').sum()
df['2min_offset'] = minutes.resample('2min',loffset='2T').sum()
df['4min_offset'] = minutes.resample('2min',loffset='4T').sum()
df>>>
╔═════════════════════╦═══════════╦═════════════╦═════════════╗
║ index ║ no_offset ║ 2min_offset ║ 4min_offset ║
╠═════════════════════╬═══════════╬═════════════╬═════════════╣
║ 2000-01-01 00:00:00 ║ 2 ║ NaN ║ NaN ║
║ 2000-01-01 00:02:00 ║ 10 ║ 2.0 ║ NaN ║
║ 2000-01-01 00:04:00 ║ 18 ║ 10.0 ║ 2.0 ║
║ 2000-01-01 00:06:00 ║ 26 ║ 18.0 ║ 10.0 ║
║ 2000-01-01 00:08:00 ║ 16 ║ 26.0 ║ 18.0 ║
╚═════════════════════╩═══════════╩═════════════╩═════════════╝
5.“基本”参数
numeric
与重采样规则中使用的单位相关的输入
将计算的基准时间移动一段时间。正如文档所描述的,该函数移动“原点”。
minutes.head().resample('30S').sum()
>>>
╔═════════════════════╦═════╗
║ date ║ val ║
╠═════════════════════╬═════╣
║ 2000-01-01 00:00:00 ║ 0 ║
║ 2000-01-01 00:00:30 ║ 0 ║
║ 2000-01-01 00:01:00 ║ 2 ║
║ 2000-01-01 00:01:30 ║ 0 ║
║ 2000-01-01 00:02:00 ║ 4 ║
║ 2000-01-01 00:02:30 ║ 0 ║
║ 2000-01-01 00:03:00 ║ 6 ║
║ 2000-01-01 00:03:30 ║ 0 ║
║ 2000-01-01 00:04:00 ║ 8 ║
╚═════════════════════╩═════╝minutes.head().resample('30S',base=15).sum()
>>>
╔═════════════════════╦═════╗
║ date ║ val ║
╠═════════════════════╬═════╣
║ 1999-12-31 23:59:45 ║ 0 ║
║ 2000-01-01 00:00:15 ║ 0 ║
║ 2000-01-01 00:00:45 ║ 2 ║
║ 2000-01-01 00:01:15 ║ 0 ║
║ 2000-01-01 00:01:45 ║ 4 ║
║ 2000-01-01 00:02:15 ║ 0 ║
║ 2000-01-01 00:02:45 ║ 6 ║
║ 2000-01-01 00:03:15 ║ 0 ║
║ 2000-01-01 00:03:45 ║ 8 ║
╚═════════════════════╩═════╝The table shifted by 15 seconds.
6.轴’,‘开’和’水平’
这些参数指定重采样所基于的列名或索引。
如果数据的日期是沿着列而不是沿着行,请指定axis = 1
如果日期列不是索引,请使用以下命令指定该列的名称:
on = 'date_column_name'
如果您有一个多级索引数据帧,使用level
指定要重新采样的正确日期时间索引的级别。
7.未涉及的其他参数
由于使用其他方法捕获功能,其余的参数已被否决或多余。例如,how
和fill_method
在重采样调用后不再需要聚合函数,但是how
用于下采样,fill_method
用于上采样。如果你感兴趣,你可以在源文档中读到更多关于这些论点的内容。
谢谢大家!
今天就到这里吧!我希望我能阐明重采样是如何工作的,以及它的每个参数是做什么的。敬请关注更多教程和其他数据科学相关文章!
在 Android 应用程序中使用 Spotify API:要点
概观
在本教程中,我将向您展示如何与 Spotify API 进行交互。如何认证、调用和解析结果。我们将发现 Spotify API 的能力,可以获得什么样的信息,以及我们可以对它进行什么样的操作。
虽然它是一个 REST API,因此对每个客户端都是一样的,但 iOS、Android 和 Web 的身份验证有很大不同。所以,在这篇文章中,我们将聚焦于 Android 认证。我们将要对 API 进行的调用也可以在 iOS 或 web 应用程序中以同样的方式使用。为了在 Android 上创建调用,我们将使用广为人知的凌空框架(【https://github.com/google/volley】)。
所以,我们走吧!
免责声明:Spotify API 可能会发生变化,因此在该功能中,这些调用可能不再有效,或者必须进行更改。如果出现问题,请查阅官方文档。
注意:使用 Spotify 开发者工具,即表示您接受 Spotify 开发者服务条款。基于简单的休息…
developer.spotify.com](https://developer.spotify.com/documentation/web-api/)
创建 Spotify API 项目
首先,我们需要创建一个 Spotify API 项目,以允许我们使用它进行认证。为此,我们前往https://developer.spotify.com/dashboard,用我们的 Spotify 账户登录。
现在我们必须执行以下步骤:
- 点击右上角的“创建客户 ID”
- 填写表单、名称、描述和我们正在构建的内容(手机应用程序)
- 回答我们是否正在开发商业集成。答案:否
- 接受服务条款。
现在,我们必须将重定向 URI 添加到我们的白名单中。为此,让我们进入“编辑设置”,在“重定向 URIs”下,我们添加要重定向的包名。例如,在我的例子中,这将是*" com . spotifyapiexample://callback "*。请注意,在 URI 中不允许有下划线。也可以在知道包名之后再做,但是必须在我们第一次运行应用程序之前完成。
很好,现在我们已经完成了,我们可以继续我们的 Android 项目了。
创建 Android 项目
让我们用一个空活动创建一个基本的 android 项目。我将调用我的 Spotify_api_example 。请随意选择你的名字。如果您还没有将您的应用程序列入白名单,现在是时候使用“😕/callback”来这样做了
首先,我们需要将 Spotify Android auth 库添加到我们的项目中。从 https://github.com/spotify/android-auth/releases.下载项目 解压文件夹,并将文件夹复制到项目根目录下的/app/libs 目录下。
然后打开你的 build.gradle (Module: app)文件,添加对库的依赖:
认证和获取用户信息
现在,让我们创建一个新的活动(空活动),我把它叫做 SplashActivity。在这个活动中,我们要做三件事:验证 Spotify API,获取当前用户的一些信息,并在获取令牌和信息后重定向到主活动。
首先,打开您的 AndroidManifest.xml 并将启动活动更改为我们新创建的 SplashActivity。此外,将互联网权限添加到清单中,这样我们就可以与外界通信了。
现在打开你的水花活动。让我们创建一个名为 authenticateSpotify() 的新方法。
- 首先,我们打开一个 AuthenticationRequest,其中包含我们的 ClientID、我们想要的响应类型(在本例中是一个身份验证令牌)和 RedirectURI ( 这个 URI 必须被列入我们在 https://developer.spotify.com/dashboard和 的项目中的白名单)
- 然后,我们设置我们请求的范围(例如,用户最近阅读播放)。这些是我们需要向用户请求的不同权限,例如,读取他的个人信息的权限。请求的作用域将显示给用户,他必须将它们授予您的应用程序。
- 最后,我们发送请求。这将打开 Spotify(如果安装了的话)或者返回到用户必须登录的 WebView。REQUEST_CODE 只是一个静态数字(例如 1337)来标识我们刚刚启动的应用程序。
现在,让我们定义一些我们需要的常量。用您的 ClientID 和 RedirectURI 替换它们。您可以在 Spotify 仪表盘中找到 ClientID。
当用户登录时。他将被重定向到您的应用程序。如果请求成功,我们将在那里接收令牌。将以下内容添加到 SplashActivity 类:
用户从 Spotify API 被重定向回来。然后你的应用识别 RequestCode,所以很明显它是从 Spotify 重定向过来的。然后,应用程序检查它是否收到了令牌,并相应地继续进行。我们使用 shared preferences(https://developer . Android . com/reference/Android/content/shared preferences)将令牌保存在我们的持久存储中。请务必查看评论,进一步了解正在发生的事情。
**最后,我们必须初始化我们的共享首选项,并调用 authenticateSpotify()方法。**将以下代码添加到您的 SplashActivity 中:
恭喜你。我们现在收到了有效的 API 令牌,可以开始使用 Spotify API 了。如果您想尝试一下,请取消对 waitForUserInfo()的注释;调用 onActivityResult。您应该可以使用您的 Spotify 帐户登录。但遗憾的是,其他事情都不会发生。
我们要打的第一个电话是获取一些关于我们用户的信息。例如,如果您想跟踪谁在使用您的应用程序。您可以通过用户唯一的 Spotify ID 来识别他们。
让我们首先用我们将从 Spotify API 收到的信息为我们的用户创建一个模型。创建一个名为“用户”的新类,并添加以下代码:
现在,为了保持结构化,让我们为将要使用的每个 API 端点创建一个服务类。创建一个名为“Connectors”的新包,创建一个名为“UserService”的新类,并添加以下代码:
如果我们查看 Spotify API 文档,我们会发现这个请求非常简单。这是一个对端点的标准 GET 请求,我们只需要我们的承载令牌(在上一步中收到的那个)作为报头。不需要其他参数。
这里,我们用 JsonObjectRequest 生成一个基本的 GET 请求。我们简单地用 Gson 和我们创建的用户类解析结果。所以我们创建了一个新的用户对象,一旦我们从 API 得到响应,我们就用结果填充对象的值。VolleyCallBack 接口有助于我们知道何时收到了有效的响应,然后我们可以相应地继续进行。
为了让我们的回调工作,我们创建了一个名为“VolleyCallBack”的新接口
我们只需要一个方法,当请求成功并且我们收到响应时通知我们。
现在我们需要在 SplashActivity 类中实现 waitForUserInfo()方法。
现在,如果您启动您的应用程序,您应该获得您的令牌,获得用户信息,并应被重定向到 MainActivity,它将只显示“Hello World!”。在后台,您的应用程序获取了一个有效的认证令牌,并请求有关登录用户的附加信息。Spotify ID 保存在永久存储器中,因此我们可以在以后再次访问它。我们只需要显示信息。
我们不打算构建一个大的用户界面,本教程的主要目标是向您展示如何获取数据,如何使用数据由您决定。
获取最近播放的歌曲
现在我们已经收到了令牌和用户信息,是时候使用另一个 API 端点了。我们将获取用户最近播放的歌曲。为你的歌曲建立一个新的模型,类似于我们已经创建的用户模型。
接下来,让我们创建一个非常简单的 UI 来显示 Spotify ID、一首歌曲的标题和一个按钮。
现在让我们编写两个新方法并初始化我们的 UI 元素。
在我们的 getTracks()方法中,我们调用了 SongService 方法,该方法将从 API 中获取歌曲。收到数据后,我们将使用 updateSong()方法更新歌曲的名称。
我们需要一个名为 SongService 的新类,我们将从 Spotify API 中获取歌曲。这个类类似于我们实现的 UserService,处理我们发送给 API 的请求。
同样,如果我们查看 Spotify API 文档,我们会看到这是一个简单的 GET 请求,除了头中的 Bearer token 之外,没有任何必需的参数。
https://developer.spotify.com/console/get-recently-played/
典型的响应如下所示:
让我们在 SongService 中实现它
The UI should look like this, but feel free to change things around
这一次,解析结果比上一次稍微困难一些。因为我们收到了一个 JSON 数组,所以我们必须遍历那个数组中的每一项,并将该项分配给我们的 song 对象。我们像上次对 Gson 那样做。最后,我们返回一个类型为 < Song > 的数组列表,其中包含了我们从 API 中获取的歌曲。
现在,如果你启动这个应用程序,你应该会看到你最近听过的一首歌的名字。我们只展示一个。这个按钮还不能做任何事情,所以我们来改变它。
将歌曲添加到您喜欢的歌曲
为我们的按钮创建一个 OnClicklistener,并将其分配给我们的按钮。
我们需要生成一个 PUT 请求。这一次我们必须提供一个主体,它必须包含一组 Spotify ID 字符串。我们将在这个数组中只包含一个 ID,因为我们将为每首歌曲生成一个请求。
https://developer.spotify.com/console/put-current-user-saved-tracks/
编辑 SongService 类并添加以下行:
首先,我们准备主体,这里称为有效载荷。我们生成一个新的 JSONArray,并添加一个带有歌曲 ID 的条目。我们将整个东西包装在一个 JSONObject 中,有效载荷就准备好了。
现在我们准备将要发送的请求。这次是一个 PUT 请求,所以方法发生了变化。另外,它类似于我们已经发送的其他请求。最后,我们将请求添加到我们的队列中,因此它将被异步执行。
如果您启动该应用程序,您应该能够看到这首歌,如果您按下添加按钮,它会将这首歌添加到 Spotify 帐户中您喜欢的歌曲中,并且会列出一首新歌,您可以再次将其添加到您的资料库中。继续检查它是否工作。
结论
关于 Spotify API 的教程到此结束。这是 Spotify API 可以实现的一个小例子。可能性几乎是无穷无尽的,也许你知道你想用它做什么。不管怎样,祝你编码愉快!
看看我的另一个 Android 项目,我用 Spotify API 提供了更多的功能和调用,比如添加和删除播放列表、播放歌曲等等。
[## hyferion/SpotifyRecLabelingAndroid
这个应用程序是基于机器学习的应用程序的一部分,用于歌曲的分类和制作…
github.com](https://github.com/Hyferion/SpotifyRecLabelingAndroid)
另外,你可以在 Github 上找到本教程的代码:
[## Hyferion/spotify_api_example
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/Hyferion/spotify_api_example)
在我的下一篇文章中,我将谈论我在 Spotify API 上开发的另一个应用程序。
使用无监督的机器学习来发现隐藏的科学知识
Word2vec 从数百万份摘要中学习材料科学
Credit: Olga Kononova
跟上新科学文献出版的步伐变得越来越困难。一个研究人员可能要花几个月的时间来做一个广泛的文献综述,即使是一个单一的主题。如果一台机器可以在几分钟内阅读所有发表在特定主题上的论文,并告诉科学家最佳的前进方向,会怎么样?嗯,我们离那还很远,但是我们下面描述的研究提出了一种新的方法,在最少的人类监督下,利用科学文献发现材料。
为了让计算机算法利用自然语言,单词需要以某种数学形式来表示。2013 年,名为 Word2vec [ 1 , 2 的算法的作者发现了一种有趣的方法,可以从大量文本中自动学习这种表示。出现在文本中相似上下文中的单词通常具有相似的含义。因此,如果训练神经网络来预测目标单词的相邻单词,它将学习相似目标单词的相似表示。他们表明,单个单词可以有效地表示为高维向量(嵌入),单词之间的语义关系可以表示为线性向量运算(参见此处获取更详细解释 Word2vec 的教程)。这种语义关系的一个著名例子是表达式
“国王”——“王后”≘“男人”——“女人”(1),
其中在相应单词的向量之间执行减法。(1)两边的成对词之间的这种语义关系代表了性别的概念。
Figure 1: Analogies between pairs of words are captured by linear operations between the corresponding embeddings. Figure on the right borrowed from [3].
自然地,如果我们使用纯粹的科学文本,而不是普通的文本源,如普通爬虫或维基百科,在我们的例子中[ 3 是几百万份材料科学摘要,这些向量运算嵌入了更专业的知识。举个例子,
“z ro2”-“Zr”≘“NiO”-“Ni”,
其中上述表达式代表氧化物的概念。
语义关系的另一个例子是单词相似度,由嵌入的点积(投影)决定。在最初的 Word2vec 模型中,单词“large”和“big”具有彼此接近的向量(具有大的点积),但是远离“亚美尼亚”的向量。在我们的专业模型中,与“LiCoO2”最相似的词是“LiMn2O4”,这两种都是锂离子电池的阴极材料。事实上,如果我们使用 t-SNE [ 4 ]将大约 12,000 种最受欢迎的材料(文中提到了 10 种以上)投影到 2D 平面上,我们会发现这些材料主要根据它们的应用和成分相似性进行聚类。
Figure 2: Materials used for similar applications as well as with similar chemical compositions cluster together. The most common elements in each “application cluster” match our materials science knowledge. Each chart on the bottom is obtained by counting chemical elements in the compositions of the materials from the corresponding application clusters. Figure borrowed from [3].
现在,我们可以做一些更有趣的事情,根据特定的应用给图 2 左上角的“材质贴图”上色。对应于单一材料的每个点可以根据其嵌入与应用词嵌入的相似性来着色,例如“热电”(用于描述热到电的转换的词,反之亦然)。
Figure 3: Materials “light up” according to their similarity to the application keyword. Figure borrowed from [3].
正如你们中的许多人可能已经猜到的,上图中最亮的地方是众所周知的热电材料,在科学摘要中与“热电”一词一起被明确提到。然而,其他一些亮点从未作为热电学进行过研究,因此该算法正在指示一种没有在文本中明确写入的关系。问题是,这些材料能成为尚待发现的良好热电材料吗?令人惊讶的是,答案是是的!
我们测试这个假设的几种方法之一是通过训练单词嵌入,就好像我们还在过去一样。我们将 2000 年至 2018 年之间发表的科学摘要一年一年地删除,并训练了 18 个不同的模型。我们使用这些模型根据材料与“热电”一词的相似性(图 3 中颜色的强度)对材料进行了排序,并选取了当年未被研究为热电材料的前 50 种材料。事实证明,这些材料中的许多后来被报道为热电材料,如下图所示。
Figure 4: If we went to the past one year at a time and made prediction using only the data available at that time, many of them would have come true by now. Each grey line corresponds to the predictions for a given year, and the solid red and blue lines are averaged across all prediction years. Figure borrowed from [3].
事实上,2009 年的五大预测之一应该是 CuGaTe2,它被认为是 2012 年才发现的当今最好的热电学之一。有趣的是,当我们的手稿[ 3 ]在准备和审查中时,我们用所有可用的摘要做出的 50 个预测中有 3 个也被报道为良好的热电学。
那么,这一切是如何运作的呢?我们可以通过查看预测材料的上下文词得到一些线索,看看这些上下文词中,哪些与材料和应用关键词“热电”都有很高的相似度。下面列出了前 5 个预测中的 3 个预测的一些主要上下文单词。
Figure 5: Context words for 3 of our top 5 predictions that contribute the most to the predictions. The width of the connect lines is proportional to cosine similarities between the words. Figure borrowed from [3].
实际上,该算法捕获对热电材料很重要的上下文单词(或者更准确地说,上下文单词的组合)。作为材料科学家,我们知道硫族化物(一类材料)通常是良好的热电材料,带隙的存在在大多数时候是至关重要的。我们看到算法是如何利用单词的共现来学习的。上图仅捕捉了一阶连接,但高阶连接也可能有助于预测。
对于科学应用,自然语言处理(NLP)几乎总是被用作从文献中提取已知事实的工具,而不是进行预测。这不同于股票价值预测等其他领域,例如,在股票价值预测中,对有关公司的新闻文章进行分析,以预测其股票价值在未来将如何变化。但即便如此,大多数方法还是将从文本中提取的特征输入到其他更大的模型中,这些模型使用结构化数据库中的附加特征。我们希望这里描述的想法将鼓励科学发现的直接、无监督的 NLP 驱动的推理方法。Word2vec 并不是最先进的 NLP 算法,因此下一步自然是用更新颖的、上下文感知的嵌入来替代它,比如 BERT [ 5 和 ELMo [ 6 ]。我们还希望,由于这里描述的方法需要最少的人工监督,其他科学学科的研究人员将能够使用它们来加速机器辅助的科学发现。
笔记
获得良好预测的关键步骤是对材料使用输出嵌入(Word2vec 神经网络的输出层),对应用关键字使用单词嵌入(Word2vec 神经网络的隐藏层)。这有效地转化为预测摘要中单词的共现。因此,该算法正在识别研究文献中的潜在“差距”,例如研究人员未来应该研究的功能应用的化学成分。详见原版的补充资料。
我们用于 Word2vec 训练和预训练嵌入的代码可在https://github.com/materialsintelligence/mat2vec获得。代码中的默认超参数是本研究中使用的参数。
放弃
这里讨论的工作是我在劳伦斯伯克利国家实验室担任博士后时进行的,与一个了不起的研究团队一起工作——约翰·达吉伦、利·韦斯顿、亚历克斯·邓恩、秦子·荣、奥尔加·科诺诺娃、克里斯汀·a·佩尔松、格布兰德·塞德尔和阿努巴夫·贾恩。
也非常感谢 Ani Nersisyan 对这个故事提出的改进建议。
参考
[1] T. Mikolov,K. Chen,G. Corrado & J. Dean,向量空间中词表征的有效估计(2013),【https://arxiv.org/abs/1301.3781】
[2] T. Mikolov,I. Sutskever,K. Chen,G. Corrado & J. Dean,单词和短语的分布式表征及其组合性(2013),【https://arxiv.org/abs/1310.4546】
[3] V. Tshitoyan,J. Dagdelen,L. Weston,A. Dunn,Z. Rong,O. Kononova,K. A. Persson,G. Ceder & A. Jain,无监督单词嵌入从材料科学文献中捕获潜在知识(2019), Nature 571,95–98
[4] L. Maaten & G. Hinton,使用 t-SNE 可视化数据(2008 年),机器学习研究杂志
[5] J. Devlin,M.-W. Chang,K. Lee & K. Toutanova,Bert:用于语言理解的深度
双向转换器的预训练(2018),https://arxiv.org/abs/1810.04805
[6] M. E. Peters,M. Neumann,M. Iyyer,M. Gardner,C. Clark,K. Lee,L. Zettlemoyer,深度语境化的词表征(2018),https://arxiv.org/abs/1802.05365
使用 USE(通用语句编码器)检测假新闻
自从引进通用语句编码器(使用)以来,它已经成为 Tensorflow Hub 中下载量最多的预训练文本模块。通用句子编码器系列包括:
通用句子编码器具有用于语义相似性和问答检索的不同模块。
我们将使用通用句子编码器 large 进行假新闻检测,这是一个文本分类问题。假新闻(也称为垃圾新闻、伪新闻或恶作剧新闻)是一种黄色新闻或宣传,由通过传统新闻媒体(印刷和广播)或在线社交媒体传播的故意虚假信息或骗局组成
我们使用 Python 和 Jupyter Notebook 来开发我们的系统,我们将使用的库包括KerasT6、Numpy、Pandas、 Sklearn 、 Matplotlib 、 Tensorflow 和 Tensorflow Hub 。完整的代码和数据可以从这里下载。
数据探索
首先,我们将看看我们的数据。我们的数据有三列,即标题、文本和标签。
data = pd.read_csv('fake_or_real_news.csv')
data = data[['title', 'text', 'label']]
data.columns = ['Title', 'Text', 'Label']
data.head()
现在我们检查标题和文本的最大字数。
最大标题长度:289
最大文本长度:115372
由于新闻文章的文本更重要,而且我们也有内存限制,所以我们将只使用文本进行分类。
print("Max title length:", data.Title.str.len().max())
print("Max text length:", data.Text.str.len().max())
现在我们看到了阶级分布。我们有 3171 个真的和 3164 个假的例子。
data.Label.value_counts()
为了得到数据集的形状,我们使用熊猫数据框的形状属性。我们总共有 6335 个例子。
data.shape
因为我们的问题是二元分类。我们需要给我们的模型传递一个二维输出向量。为此,我们在数据框中添加了两个 one hot 编码列。
pos = []
neg = []
for l in data.Label:
if l == 'FAKE':
pos.append(0)
neg.append(1)
elif l == 'REAL':
pos.append(1)
neg.append(0)data['Pos']= pos
data['Neg']= neg
现在让我们看看我们的数据框架。
data.head()
将数据分为测试和训练
现在,我们将数据集分为训练集和测试集。我们将使用 90 %的数据进行训练,10 %的数据进行测试。我们使用随机状态,所以每次我们都得到相同的训练和测试数据。
data_train, data_test = train_test_split(data,
test_size=0.10,
random_state=42)
加载通用语句编码器
下一步是加载通用句子编码器。使用 tensorflow_hub 很容易做到。这一步可能需要一些时间。
module_url = "[https://tfhub.dev/google/universal-sentence-encoder-large/2](https://tfhub.dev/google/universal-sentence-encoder-large/2)"
embed = hub.Module(module_url)
获取嵌入
现在我们将把训练句子转换成嵌入。这是通过简单地将整个句子传递给 embed object 来实现的。我们为每个句子得到一个 512 维的向量。
with tf.Session() as session:
session.run([tf.global_variables_initializer(),
tf.tables_initializer()])
training_embeddings = session.run(embed(data_train.Text.to_list()))
定义神经网络
我们现在将设计一个总共有两层的浅层神经网络。嵌入被传递到具有“Relu”激活和 128 个单元的密集层。接下来是我们的输出层。这一层有两个单元,因为我们的模型是一个二元分类器。Softmax 用作输出层的激活函数。
model = Sequential()
model.add(Dense(128, activation = 'relu'))
model.add(Dense(2, activation = 'softmax'))model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['acc'])
model.summary()将打印所有图层的摘要以及输出的形状。
model.summary()
培养
历元数是您的模型将循环和学习的数量,批量大小是您的模型在单个时间看到的数据量。
num_epochs = 30
batch_size = 32history = model.fit(training_embeddings,
data_train[label_names].values,
epochs=num_epochs,
validation_split=0.1,
shuffle=True,
batch_size=batch_size)
绘制准确度和损失图
首先,我们将绘制测试和训练精度相对于历元数的曲线。
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
现在,我们将绘制测试和训练损失与历元数的关系图。
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
测试
现在我们将把测试例子转换成嵌入。
with tf.Session() as session:
session.run([tf.global_variables_initializer(),
tf.tables_initializer()])
test_embeddings = session.run(embed(data_test.Text.to_list()))
哇!只需三十次迭代和一个小数据集,我们就能获得 90 %的准确率。请记住,我们没有对数据进行预处理,因此这种准确性可以得到认可。
predictions = model.predict(test_embeddings,
batch_size=1024,
verbose=1)
labels = ['REAL', 'FAKE']prediction_labels=[]
for p in predictions:
prediction_labels.append(labels[np.argmax(p)])print("Accuracy:", sum(data_test.Label==prediction_labels)/len(prediction_labels))
如果你有任何问题或建议,欢迎在下面留言。你也可以在Linkedin上和我联系。