本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是spark专题的第七篇文章,我们一起看看spark的数据分析和处理。

过滤去重

在机器学习和数据分析当中,对于数据的了解和熟悉都是最基础的。所谓巧妇难为无米之炊,如果说把用数据构建一个模型或者是支撑一个复杂的上层业务比喻成做饭的话。那么数据并不是“米”,充其量最多只能算是未脱壳的稻。要想把它做成好吃的料理,必须要对原生的稻谷进行处理。

但是处理也并不能乱处理,很多人做数据处理就是闷头一套三板斧。去空值、标准化还有one-hot,这一套流程非常熟悉。以至于在做的时候都不会想,做这些处理的意义是什么。我们做数据处理也是有的放矢的,针对不同的情况采取不同的策略。所以说到这里,你应该已经明白了,首要任务还是需要先对数据有个基本的了解,做到心中有数

那么怎么做到心中有数呢?我们先来看一个具体的例子,假设现在我们有了这么一批数据:

df = spark.createDataFrame([
(1, 144.5, 5.9, 33, 'M'),
(2, 167.2, 5.4, 45, 'M'),
(3, 124.1, 5.2, 23, 'F'),
(4, 144.5, 5.9, 33, 'M'),
(5, 133.2, 5.7, 54, 'F'),
(3, 124.1, 5.2, 23, 'F'),
(5, 129.2, 5.3, 42, 'M'),
], ['id', 'weight', 'height', 'age', 'gender'])

这批数据粗略看起来没什么问题,但实际上藏着好几个坑。

首先,id为3的数据有两条,不仅如此,这两条数据的特征也完全一样。其次,id为1和4的数据特征也完全相同,只是id不同。除此之外,id为5的数据也有两条,但是它们的特征都不同。显然这不是同一条数据,应该是记录的时候出现的错误。

那么对于这样一份数据,我们怎么发现它们当中的问题,又怎么修正呢?

我们先从最简单开始,先来找找完全一样的数据。我们通过count方法可以求出整个数据集当中的条数,通过distinct().count()可以获得去重之后的数据数量。这两个结合一起使用,就可以看出是否存在数据完全重复的情况。

可以看出来,直接count是7条,如果加上distinct的话是6条,也就是说出现了数据的完全重复。那么我们可以知道,我们需要做一下去重,去除掉完全重复的行,要去除也非常简单,dataframe当中自带了dropDuplicates方法,我们直接调用即可:

很明显,刚才两条完全一样id为3的数据少了一条,被drop掉了。

接下来,我们继续分析,怎么判断是否存在id不同但是其他数据相同的情况呢?

其实也是一样使用distinct.count,只不过我们需要把count distinct运算的范畴去除掉id。我们可以通过columns获取dataframe当中的列名,我们遍历一下列名,过滤掉id即可

这里我们依然还是套用的distinct.count只不过我们在使用之前通过select限制了使用范围,只针对除了id之外的列进行去重的计算

不仅distinct如此,dropDuplicate同样可以限制作用的范围。使用的方法也很简单,我们通过subset这个变量来进行控制,我们传入一个list,表示duplicate的范围

可以很明显地看到,我们的数据又减少了一条。说明我们去除掉了id不同但是内容一样的情况,最后还剩下id相同,但是内容不同的情况。这种情况一般是由于记录的时候发生了错误,比如并发没有处理好,导致两条不同的信息采用了同一个id。

这个很简单,因为我们已经经过了整体去重了,所以正常是不应该存在id一样的条目的。所以我们只需要判断id是否有重复就好了。判断的方法也很简单,我们count一下id的数量。

这里我们可以和之前一样通过distinct.count来判断,这里我们介绍一种新的方法,叫做agg。agg是aggregate的缩写,直译过来是聚合的意思。通过agg我们可以对一些列进行聚合计算,比如说sum、min、max这些。在这个问题当中,我们要进行的聚合计算就是count和count distinct,这两个也有现成的函数,我们导入就可以直接用了。

也就是说通过agg我们可以同时对不同的列进行聚合操作,我们发现加上了distinct之后,只剩下了4条,说明存在两条不同的数据id一样的情况。

接下来我们要做的就是给这些数据生成新的id,从而保证每一条数据的id都是unique的。这个也有专门的函数,我们直接调用就好:

monotonically_increasing_id这个方法会生成一个唯一并且递增的id,这样我们就生成了新的id,完成了整个数据的去重过滤。

空值处理

当我们完成了数据的过滤和清洗还没有结束,我们还需要对空值进行处理。因为实际的数据往往不是完美的,可能会存在一些特征没有收集到数据的情况。空值一般是不能直接进入模型的,所以需要我们对空值进行处理。

我们再创建一批数据:

df_miss = spark.createDataFrame([
(1, 143.5, 5.6, 28, 'M', 100000),
(2, 167.2, 5.4, 45, 'M', None),
(3, None , 5.2, None, None, None),
(4, 144.5, 5.9, 33, 'M', None),
(5, 133.2, 5.7, 54, 'F', None),
(6, 124.1, 5.2, None, 'F', None),
(7, 129.2, 5.3, 42, 'M', 76000),
], ['id', 'weight', 'height', 'age', 'gender', 'income'])

这份数据和刚才的相比更加贴近我们真实的情况,比如存在若干行数据大部分列为空,存在一些列大部分行为空。因为现实中的数据往往是分布不均匀的,存在一些特征和样本比较稀疏。比如有些标签或者是行为非常小众,很多用户没有,或者是有些用户行为非常稀疏,只是偶尔使用过产品,所以缺失了大部分特征。

所以我们可能会希望查看一下有哪些样本的缺失比较严重,我们希望得到一个id和缺失特征数量映射的一个pair对。这个操作通过dataframe原生的api比较难实现,我们需要先把dataframe转成rdd然后再通过MapReduce进行


image-20200525163206376

我们可以看到是3对应的缺失值最多,所以我们可以单独看下这条数据:

我们可能还会向看下各列缺失值的情况,究竟有多少比例缺失了。由于我们需要对每一列进行聚合,所以这里又用到了agg这个方法:

这段代码可能看起来稍稍有一点复杂,因为用到了*这个操作。因为当agg这个函数传入一个list之后,可以对多列进行操作。而在这里,我们要对每一列进行统计。由于列数很多,我们手动列举显然是不现实的。所以我们用循环实现,*操作符的意思就是将循环展开。count('*')等价于SQL语句当中的count(1),也就是计算总条数的意思。

从结果当中我们可以看出来,income这个特征缺失得最严重,足足有71%的数据是空缺的。那么显然这个特征对我们的用处很小,因为缺失太严重了,也不存在填充的可能。所以我们把这行去掉:

我们去掉了income之后发现还是存在一些行的缺失非常严重,我们希望设置一个阈值,将超过一定数量特征空缺的行过滤,因为起到的效果也很小。

这个功能不用我们自己开发了,dataframe当中原生的api就支持。

经过这样的处理之后,剩下的缺失就比较少了。这个时候我们就不希望再进行删除了,因为只有个别数据空缺,其他数据还是有效果的, 如果删除了会导致数据量不够。所以我们通常的方式是对这些特征进行填充

缺失值填充是一种非常常见的数据处理方式,填充的方式有好几种。比如可以填充均值,也可以填充中位数或者是众数,还可以另外训练一个模型来根据其他特征来预测。总之手段还是挺多的,我们这里就用最简单的方法,也就是均值来填充。看看spark当中使用均值填充是怎么操作的。

既然要填充,那么显然需要先算出均值。所以我们首先要算出每一个特征的均值。这里性别是要排除的,因为性别是类别特征,不存在均值。所以如果要填充性别的话,就只能填充众数或者是用模型来预测了,不能直接用均值。

均值的计算本身并不复杂,和刚才的一系列操作差不多。但是有一点需要注意,我们这里得到了结果但是却不能直接作为参数传入。因为dataframe中的fillna方法只支持传入一个整数、浮点数、字符串或者是dict。所以我们要把这份数据转化成dict才行。这里的转化稍稍有些麻烦,因为dataframe不能直接转化,我们需要先转成pandas再调用pandas当中的to_dict方法

我们有了dict类型的均值就可以用来填充了:

总结

在实际的工作或者是kaggle比赛当中,涉及的数据处理和分析的流程远比文章当中介绍到的复杂。但去重、过滤、填充是数据处理当中最基础也是最重要的部分。甚至可以说无论应用场景如何变化,解决问题的方法怎么更新,这些都是不可缺失的部分。

如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

本文使用 mdnice 排版

spark | 手把手教你用spark进行数据预处理的更多相关文章

  1. 手把手教你springboot中导出数据到excel中

    手把手教你springboot中导出数据到excel中 问题来源: 前一段时间公司的项目有个导出数据的需求,要求能够实现全部导出也可以多选批量导出(虽然不是我负责的,我自己研究了研究),我们的项目是x ...

  2. 手把手教你用FineBI做数据可视化

    前些日子公司引进了帆软商业智能FineBI,在接受了简单的培训后,发现这款商业智能软件用作可视分析只用一个词形容的话,那就是“轻盈灵动”!界面简洁.操作流畅,几个步骤就可以创建分析,获得想要的效果.此 ...

  3. 手把手教你AspNetCore WebApi:数据验证

    前言 小明最近又遇到麻烦了,小红希望对接接口传送的数据进行验证,既然是小红要求,那小明说什么都得满足呀,这还不简单嘛. 传统验证 [HttpPost] public async Task<Act ...

  4. Apache Beam实战指南 | 手把手教你玩转大数据存储HdfsIO

    https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247494843&idx=2&sn=0dd20caec76e25 ...

  5. 大数据江湖之即席查询与分析(下篇)--手把手教你搭建即席查询与分析Demo

    上篇小弟分享了几个“即席查询与分析”的典型案例,引起了不少共鸣,好多小伙伴迫不及待地追问我们:说好的“手把手教你搭建即席查询与分析Demo”啥时候能出?说到就得做到,差啥不能差人品,本篇只分享技术干货 ...

  6. Spark Streaming中向flume拉取数据

    在这里看到的解决方法 https://issues.apache.org/jira/browse/SPARK-1729 请是个人理解,有问题请大家留言. 其实本身flume是不支持像KAFKA一样的发 ...

  7. 2、 Spark Streaming方式从socket中获取数据进行简单单词统计

    Spark 1.5.2 Spark Streaming 学习笔记和编程练习 Overview 概述 Spark Streaming is an extension of the core Spark ...

  8. 大数据技术之_19_Spark学习_01_Spark 基础解析 + Spark 概述 + Spark 集群安装 + 执行 Spark 程序

    第1章 Spark 概述1.1 什么是 Spark1.2 Spark 特点1.3 Spark 的用户和用途第2章 Spark 集群安装2.1 集群角色2.2 机器准备2.3 下载 Spark 安装包2 ...

  9. 基于Spark Streaming + Canal + Kafka对Mysql增量数据实时进行监测分析

    Spark Streaming可以用于实时流项目的开发,实时流项目的数据源除了可以来源于日志.文件.网络端口等,常常也有这种需求,那就是实时分析处理MySQL中的增量数据.面对这种需求当然我们可以通过 ...

随机推荐

  1. (Java实现)蓝桥杯Excel地址

    历届试题 Excel地址 原题地址 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 Excel单元格的地址表示很有趣,它使用字母来表示列号. 比如, A表示第1列, B表示第2列, ...

  2. Java实现 LeetCode 523 连续的子数组和(ง •_•)ง

    523. 连续的子数组和 给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数. 示 ...

  3. Java实现 蓝桥杯VIP 基础练习 报时助手

    题目描述 给定当前的时间,请用英文的读法将它读出来. 时间用时h和分m表示,在英文的读法中,读一个时间的方法是: 如果m为0,则将时读出来,然后加上"o'clock",如3:00读 ...

  4. Java实现 洛谷 P1089 津津的储蓄计划

    import java.util.*; public class Main { public static void main(String[] args) { Scanner sc=new Scan ...

  5. Java实现 蓝桥杯 历届试题 邮局

    问题描述 C村住着n户村民,由于交通闭塞,C村的村民只能通过信件与外界交流.为了方便村民们发信,C村打算在C村建设k个邮局,这样每户村民可以去离自己家最近的邮局发信. 现在给出了m个备选的邮局,请从中 ...

  6. java实现第六届蓝桥杯格子中输出

    格子中输出 格子中输出 stringInGrid方法会在一个指定大小的格子中打印指定的字符串. 要求字符串在水平.垂直两个方向上都居中. 如果字符串太长,就截断. 如果不能恰好居中,可以稍稍偏左或者偏 ...

  7. python json unicode utf-8处理总结

    1.直接输出字典中文 在python中经常遇见直接print dict(字典),或者dict转json,但是没有给特定的参数,然后打印json字符串,输出的中文就成了unicode码的情况,如下: d ...

  8. mysql基础之-mysql查询缓存(九)

    0x01 MySQL查询缓存 用于保存MySQL查询语句返回的完整结果,被命中时,MySQL会立即返回结果,省去解析.优化和执行等操作 如何检查缓存?? MySQL保存结果与缓存中: 把select语 ...

  9. k8s学习-文档&概念

    1.文档大全 kubernetes objects文档(yaml文件编写): https://kubernetes.io/docs/concepts/overview/working-with-obj ...

  10. SpringSceurity(4)---短信验证码功能实现

    SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...