-
-
- 我们离不开数据分析
- 数据分析入门学习路线
- 1.1 统计学基本知识
- 1.2 机器学习基本算法
- 1.3 编程语言及工具
- 数据分析重头戏之数据整理
- 2.1 理解你的业务数据
- 2.2 明确各个特征的类型
- 2.3 找出异常数据
- 2.4 不得不面对缺失值
- 2.5 令人头疼的数据不均衡
- Python 入门必备知识
- 3.1 解释型Vs编译型
- 3.2 Python 最常用的对象
- 3.2.1 list 核心知识
- 3.2.2 dict 核心知识
- 3.2.3 tuple 和 set 简介
- 3.3 Python 入门必备知识点
- 3.3.1 对象上的特殊函数
- 3.3.2 列表生成式
- 3.3.3 位置参数和关键字参数
- NumPy 入门必备知识
- 4.1 ndarray 对象
- 4.2 NumPy 的向量化增强
- 4.3 NumPy 的传播机制
- Pandas 数据分析必备入门知识
- 5.1 类一维数组 Series
- 5.1.1 创建 Series
- 5.1.2 增加元素
- 5.1.3 删除元素
- 5.1.3 修改元素
- 5.1.4 访问元素
- 5.2 类二维数组 DataFrame
- 5.2.1 创建 DataFrame
- 5.2.1 增加数据
- 5.2.2 删除数据
- 5.2.3 修改数据
- 5.2.4 访问数据
- 5.3 必备的 20 个统计学函数
- 5.4 必备的缺失值处理技巧
- 5.5 必备的数据透视处理函数
- 5.1 类一维数组 Series
- Matplotlib
- 6.1 必备的绘图理论知识
- 6.2 绘图必备 100 行代码
- 机器学习线性回归模型
- 7.1 11 个必备基本概念
- 7.2 三 个假定
- 7.3 建立线性回归模型
- 7.4 最大似然估计求参数
- 7.5 梯度下降求解优化问题
- 7.6 手写不调包实现的 5 个算子
- 7.7 手写不调包实现的整体算法框架
- 面试及资料分享小结
- 8.1 数据挖掘工程师面试实录
- 8.1.1 一 面
- 8.1.2 二 面
- 8.1.3 三面
- 8.2 推荐哈佛大学数据分析课程
- 8.1 数据挖掘工程师面试实录
-
我们离不开数据分析
首先,非常感谢你订阅了这场 chat, 我会努力准备,用心书写,将我过去的项目经历、学习心得,一五一十地、毫无保留地记录下来。并且,我会尽力写的通俗易懂,逻辑更有条理一些。不出意外,我还会反复修改多次,直到做到最好。
相信订阅本场 chat 的小伙伴中,有的已经工作了,可能*时还要经常加班。和你们一样,我也工作 5 年了,也是经常加班。不过,我会每天抽时间,写点技术文章,也不知怎么地,只有这样,我才觉得一天过得充实,才觉得没有虚度。
最*几年,做的这些项目,大多与数据分析与算法应用相关。岗位虽然是算法工程师,但是与数据分析打得交道也很多,双管齐下,最后才能确保算法的落地。在几年前,我还想当然地认为做算法的就应该偏重算法研究与应用,可能数据分析相关的技术真的没那么重要,不过我很快意识到自己的错误,重新将数据分析放在一个重要的位置,去研究学习。
结合过往经历,说下自己对算法设计和数据分析工作的一些浅显体会。
对于算法落地而言,个人认为准确性和稳定性是最重要的。在校园时,相信老师都跟我们讲过算法的几个重要性质,比如算法的时间复杂度,空间复杂度,鲁棒性等等,都具备了这些性质的算法当然是一个好算法。
然而,实际情况是,实际场景往往比较复杂,比如,影响因素及之间的关系很复杂;数据匮乏不说,手上的数据还有一半是垃圾等等,这一系列难点,都加大了我们算法设计的难度,哪怕只是设计一个满足基本场景的算法。这使我明白,设计的算法要优先保证能得到一个正确的结果。
其次,作为工程项目,确保系统的稳定性,尽量或者上线后基本没有 bug 显得同样重要。否则,你连觉都睡不好,还提什么其他性能。所以为了稳妥起见,大部分算法设计都不会从零做起,大都会基于成熟稳定的开源框架,然后在上面扩展,开花结果。
已经说的很直白了,还没有工作的小伙伴,可以思考一下。光鲜的事物背后,未必有它真正看起来那样的光彩夺目。
人工智能的强大离不开数据,既然离不开数据,自然就少不了数据处理与分析的相关技术,那么公司就一定需要数据处理与分析的人员。
数据分析为啥如此重要呢? 一句话,喂进去的是垃圾,出来的就是垃圾。机器学习、深度学习的算法设计的再牛叉,如果进去的是垃圾数据,深度学习学出来的模型也不会好到哪里去。
所以,数据科学相关的技术,工作中是离不开的,未来只会越来越重要。
说完我的一些体会后,我简单引出一个文章展开的思路。
作为数据分析的入门课程,首先说一下入门数据科学的完整学习路线;然后,介绍数据分析中花费时间较多的:数据清理 (data munging);
学习路线、主要任务介绍完后,接下来就要开始动手实践、实现这些任务了。Python 作为数据分析和人工智能的首选语言,介绍关于它的一些核心知识,让帮助入门者快速上手。
为了工作中加速数据分析的脚步,依次介绍基于 Python 的科学计算库 NumPy, Pandas, 要想快速上手这些库,理解它们的机制是必不可少的,比如 NumPy 的广播机制。数据分析的另一个重要任务就是数据可视化,接下来,介绍基于 Python 生态的可视化库:Matplotlib, 使用 100 行左右的代码,来打包常用的函数用法。
数据分析往往需要做一些回归分析,分类分析等,作为目前火热的机器学习、深度学习,我们通过一个经典的回归算法,深刻明白其原理,知道公式推导,手写实现它。
学习这些理论和工具的同时,你也需要开始实战了。为此,我们选用哈佛大学的数据分析课,它是开源的,并且授课所用数据全部来自实际场景,算是最大贴*你的实际工作日常了,可谓干货十足!
接下来,你可以去寻找数据分析、机器学习相关的工作了。作为全面的学习路线,我还非常用心地为小伙伴们,准备了 2 个数据分析、机器学习相关的真实面试经历。
让我们开始数据分析的学习之旅吧!
数据分析入门学习路线
在开始介绍学习路线之前,我想告诉大家,本篇 chat 展开提纲中,已经包括了数据分析知识的主要部分,因此,大家应该已经有一个大概轮廓了。
每个人学习一门新知识前,大都想去了解下这门知识的学习思路是怎样的,都有哪些知识是必须要学的。所以,我们也只去论述关于数据分析,那些必须要学习的知识,也就是学好数据分析的必备技能。
1.1 统计学基本知识
首先,入门数据分析需要必备一些统计学的基本知识,在这里我们简单列举几个入门级的重要概念。概率,*均值,中位数,众数,四分位数,期望,标准差,方差。在这些基本概念上,又衍生出的很多重要概念,比如协方差,相关系数等。
这一些列常用的统计指标,都在强大的数据分析包 Pandas 中实现了,非常方便,在下面的 Pandas 介绍章节,会详细列出。
我们看下概率的通俗理解。 概率 P 是对随机事件发生的可能性的度量。例如,小明在期末考试前,统计了下自己在今年的数学考试成绩,结果显示得到 80 分以下的次数为 2 次,得 80 分~ 90 分的次数为 10 次,得到 90 分以上次数为 3 次,那么:
小明得到 80 分以下的概率为:
P( < 80 ) = 2/(2+10+3) = 13.3%
80~90分的概率为:
P( 80 ~ 90) = 10/(2+10+3) = 66.7%
90分以上的概率:
P( > 90) = 3/(2+10+3) = 20%
期望值 E,在一个离散性随机变量实验中,重复很多次实验,每次实验的结果乘以其出现的概率的总和。如上例中,小明在今年的期末考试,我们对他的期望值大约是多少呢?套用上面的公式,80 分以下的值取一个代表性的分数:70 分,80~90:85 分,90 分以上:95 分,
E = 70 * 0.133 + 85 * 0.667 + 95 * 0.2
计算出的结果为 85,即期末考试我们对小明的合理期望是 85 分左右。
方差 ,用来度量随机变量取值和其期望值之间的偏离程度,公式为:
σ2=∑(X−μ)2Nσ2=∑(X−μ)2N
其中:
XX 表示小明的分数这个随机变量 μμ 表示样本*均值 NN 表示样本的个数,即在此等于 15 个
已经知道小明的 15 次考试的分数,均值刚才我们也计算出来了为 85 分,带入到上面的公式中,便能得出偏离 85 分的程度大小。
如果方差很大,那么小明在期末考试的分数可能偏离 85 分的可能性就越大;如果方差很小,那么小明很可能期末考试分数在 85 分左右。
当然,你还得了解,事件,离散事件,连续性事件,了解数据的常见分布,比如泊松分布,正态分布等,归一化等知识。限于篇幅,在此,我就不一一展开了。我为大家推荐一本精简的这方面入门书籍,浙大盛骤等老师合编的《概率论与数理统计》这本书,大家可以有选择地学习书中的重要个概念。
1.2 机器学习基本算法
说统计学是一种基于事实的演绎学问,它是严谨的,可以给出确切解释的。不过,机器学习就不一样了,它是一门归纳思想的学问,比如深度学习得出的模型,你就很难解释其中的具体参数为什么取值为某某某。它的应用在于可以提供一种预测,给我们未来提供一种建设性的指导。
数据分析师需要了解机器学习的基本理论、常见的那十几种算法,这样对于我们做回归、分类、聚类分析,都是不可缺少的。
直到现在,也有很多小伙伴在公众号后台,问我,该如何入门机器学习。我通常的回答都是,先理解一种基本的算法,包括从算法的原理,公式推导,手写编码实现这个算法,可视化算法的结果。当完成整个一遍时,你也就差不多入门了,知道机器学习是怎么一回事了。
这种方法,比较直接、奏效,作为过来人,当时我也是这么做的,可以说实践出真知,同样推荐给还有这方面疑惑的小伙伴,不要再拖延了,没有一个神奇魔幻的方法,可以帮助你不用动手的,就可以掌握它 。
从现在开始学习和动手,参考接下来的介绍机器学习的章节,我会附上完整的代码,你只需要从头敲一遍,差不多你就可以入门机器学习了。越直接,越奏效,先上路,再上道!
1.3 编程语言及工具
如果说数学是纯理论,可能只需要动脑的学问地话,计算机和它最不同的一点就是,需要动手。记得 linux 大神托瓦兹,作为世界上最著名的程序员、计算机科学家,linux 内核和 git 的主要发明人。他就曾经说过,talk is poor, show me the code.
的确,计算机属于工科学问,动手编码的能力非常重要,现在越来越多的理工科博士,也开始注重编码能力了,而且有的编码能力也是超强,写出来的代码可读性、可扩展性都很好。这在过去,博士能做到这个的,大概还不是太多(这是个人观察得出,未经数据考证,结论可能有误 ),或许,当前,博士找工作面临压力也很大,物竞天择,适者生存,民营、私企不会养一个闲人。
数据分析和机器学习领域,同样需要能熟练使用至少一门变成语言,目前此领域,使用较多的就是 Python 和 R 语言。Python 又适合与机器学习领域,所以数据分析相关的从业人员,目前使用 Python 的也较多,当然 R 语言也不少。基于 Python 的生态环境也很不错,有很多数据科学包,比如文中提到的 NumPy、SciPy、Pandas 等等。
入行前,多多动手实践一些项目和名校的开源课程,可以驱动我们掌握它们,毕竟面对一些实际需求,这样做目标明确,自然会驱使你去掌握这些包的更多功能和 API 使用。
总结,这个数据分析的入门路线,主要分为三部分,现在相信小伙伴们已经目标已经很明确了。
下面,看下数据分析的重头戏,数据整理(data munging)。
数据分析重头戏之数据整理
数据整理,英文名称 data munging,是指在获取到的原数据基础上,理解这些业务数据,整理清洗它们,作为接下来算法建模的输入数据。在文章刚开始,我们就提到过,这部分工作的重要性,绝不亚于算法模型,时间占比可能大于算法选择和设计环节。
2.1 理解你的业务数据
我们在拿到需要分析的数据后,千万不要急于立刻开始做回归、分类、聚类分析。
第一步应该是认真理解业务数据,可以试着理解去每个特征,观察每个特征,理解它们对结果的影响程度。
然后,慢慢研究多个特征组合后,它们对结果的影响。借助上个章节提到的,常用的统计学指标,比如四分位,绘制箱形图,可以帮助我们寻找样本的取值分布。
同时,可以借助另一个强大的可视化工具: seaborn ,绘制每个特征变量间的相关系数热图 heatmap,帮助我们更好的理解数据,如下图所示:
colormap = plt.cm.RdBu
plt.figure(figsize=(14,12))
sns.heatmap(train.astype(float).corr(),linewidths=0.1,vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True)
2.2 明确各个特征的类型
明确我们的数据类型,也是数据整理阶段的必备任务之一。
如果这些数据类型不是算法部分期望的数据类型,你还得想办法编码成想要的。比如常见的数据自增列 id 这类数据,是否有必要放到你的算法模型中,因为这类数字很可能被当作数字读入。
某些列的取值类型,虽然已经是数字了,它们的取值大小表示什么含义你也要仔细捉摸。因为,数字的相*相邻,并不一定代表另一种层面的相邻。
有些列是类别型变量(categorical variable),例如著名的 Kaggle 泰坦尼克生还预测比赛中,乘客上船地点 Embarked 这个变量就是类别型变量。如果给 Embarked 变量用 Embarked 编码为 1、2、3 ,这样编码是不合理的。
一般这种类型的编码方式有 one-hot 编码,dummy variable 两种方式。
2.3 找出异常数据
有时候我们的数据存在异常值,并且这种概率挺大的。这实际上会导致结果出现偏差。比如,统计中国家庭人均收入时,如果源数据里面,有王建林,马云等这种富豪,那么,人均收入的均值就会受到极大的影响,这个时候最好,绘制箱形图,看一看百分位数。
了解数据范围,设定最大值、最小值限度是很非常重要的。
2.4 不得不面对缺失值
现实生产环境中,拿到的数据恰好完整无损、没有任何缺失数据的概率,和买彩票中将的概率差不多。
数据缺失的原因太多了,业务系统版本迭代, 之前的某些字段不再使用了,自然它们的取值就变为 null 了;再或者,压根某些数据字段在抽样周期里,就是没有写入数据......
处理缺失数据,最好弄明白它们为什么缺失了,比如,像上面说道的,如果是在抽样周期里,这些字段取值缺失了,那么可以咨询业务人员,这些字段大概率会取得哪些值。
接下来,填充缺失数据,比如均值填充,或者,为缺失的数据创建一类特殊值。
极端情况下,如果发现模型的效果受此字段影响较大,发现彻底删除此字段效果更好,那完全剔除可能是不错的选择。不过这样做也有风险,可能为模型带来更大的偏差。
2.5 令人头疼的数据不均衡
理论和实际总是有差距的,理论上很多算法都存在一个基本假设,即数据分布总是均匀的。这个美好的假设,在实际中,真的存在吗?很可能不是!
算法基于不均衡的数据学习出来的模型,在实际的预测集上,效果往往差于训练集上的效果。实际数据往往分布得很不均匀,存在所谓的 “长尾现象”
,又称:“二八原理”。
就不均衡解决的难易程度而言,数据量越大,不均衡的问题越容易解决,相反,数据量很小,再不均衡,解决起来就比较困难了,比如典型的太空中是否有生命迹象这个事情,压根就没有太多相关的因素数据,如果某个特征的取值远多于另外一种,处理这种数据不均衡问题,就比较困难了。
所有以上 5 个方面的问题,对于一个数据分析师或数据科学家而言,都是需要认真处理对待的。限于篇幅,每个方面的详细解决技术,大家可以自行搜索相关文献和技术博客,一般大都有对应的解决措施。通过这个 chat,你能知道数据整理工作主要有这 5 个方面或者任务,等着你去探索解决,基本也就可以了。
当然,数据分析完成数据整理后,接下来的主要任务:特征工程,也是非常重要的。大家也可以查询相关资料,记得在我的公众号里,关于这个话题,曾经不只一次的探讨过,大家可以参考,一起交流。
接下来,开始介绍编程工具环节,继续我们的数据分析探索之旅。
Python 入门必备知识
Python 语言主要的特点,通俗的说就是语法简洁,开发效率高,一般数据分析,机器学习,深度学习会选用 Python 语言,同时基于 Python 的开发生态环境比较友好。
下面,从 Python 语言的特点,以及*时使用较多的几个典型对象展开,这些都是入门必备的功能,掌握这些你就可以说自己入门 Python 了。
不过,为了兼顾已经比较熟悉 Python 的小伙伴,我打算留有一定篇幅介绍一些 Python 的进阶功能,它们作为 Python 的重要特性,在*时工作中也会用到。
3.1 解释型Vs编译型
Python 是解释型语言,对于 Python 刚刚入门的小伙伴,可能对解释性有些疑惑。不过,没关系,我们可以通过大家已经熟悉的编译型语言,来帮助我们理解 Python 的解释性。
编译型语言,如 C++、Java,它们会在编译阶段做类型匹配检查等,因此,数据类型不匹配导致的编译错误,在编译阶段就会被检查出来,例如:
Intger a = 0;
Double b = 0.0;
a = b; // Double类型的变量 b 试图赋值给 Integer 型的变量 a, 编译报错
// 因为 Integer 类型 和 Double 类型 不存在继承关系,
// 类型不能互转
但是,Python 就不会在编译阶段做类型匹配检查,比如,Python 实现上面的几行语句,会这样写:
a = 0 # 不做任何类型声明
b = 0.
a = b # 这种赋值,Python 会有问题吗?
答案是不会的。此处就体现了 Python 的解释特性,当我们把 0 赋值给 a 时,Python 解释器会把它 a 解释为 int 型,可以使用内置函数 type(variable) 显示地检查 variable 的类型:
In [70]: type(a)
Out[70]: int
In [69]: type(b)
Out[69]: float
In [71]: a = b # 在把 float 型 b 赋值给 a 后, # a 就被解释为float
In [72]: type(a)
Out[72]: float
在把 float 型 b 赋值给 a 后, a 就被解释为 float. 相信此时,你已经对 Python 的解释型有一定了解了,下面说一些 Python 最常用的对象及其方法。
3.2 Python 最常用的对象
Python 最常用的几个对象: list、dict、tuple 以及它们三者的灵活组合。list 可以看作是线性表和链表的结合体,dict 可以看作是 key-value 对的组合,tuple 是不可更改的 "list"(注意,这种表述未必严谨,但个人认为比较通俗易懂,尤其对于入门者而言)。
上面提到 Python 用于数据分析,机器学习,非常方便。我们*时需要处理的数据怎么也有成千上万行,要想利用这些数据做分析,不可避免地当要将它们缓存到内存中。很显然,最直接的存储结构必然是容器。无一例外,list、dict、tuple 都是 Python 中构建好的容器,都是可迭代的对象,它们因此被使用频次高也就不足为奇了。
简单来说,我们使用 [] 创建一个 list,使用 {} 创建一个 dict,使用 () 创建一个 tuple。创建好我们的容器后,下一步就要知道如何添加、删除、更新、查询访问里面的元素。
3.2.1 list 核心知识
先说说 list 的这些操作:
In [73]: a = [[1,3,5],[7,4,2]] # 创建二维数组 a
In [74]: a
Out[74]: [[1, 3, 5], [7, 4, 2]]
In [75]: a.append([0,8,3]) # 插入元素[0,8,3]
In [76]:
Out[76]: [[1, 3, 5], [7, 4, 2], [0, 8, 3]]
注意 append 操作直接插入到 a 的末尾,而不是 a 的副本的末尾,这样做无疑节省了内存空间。面向对象编成,不管是 C++、Java、Python 都会有这类问题,务必要清楚操作是在 a 上修改,还是在 a 的副本上修改,各有用处,此处不再详细展开讨论,后面的进阶课程,我们可以再展开。
如果想批量增加元素,可以使用 extend,如下所示,我们先使用 copy 下 a ,返回 b ,注意 list 实例的 copy 一律属于浅>拷贝(shallow copy),注意这种拷贝带来的副作用,在此我们也不详细展开,后续我们可以找时间讨论无论哪种面向对象的语言都存在的 shallow copy and deep copy 问题,以及如何选用哪种 copy 节省内存空间的同时,也能按预期实现目标。可以看到 b.extend(a) 后,b 内批量导入了 a。
In [100]: a
Out[100]: [[1, 3, 5], [7, 4, 2], [0, 8, 3]]
In [101]: b = a.copy()
In [102]: b
Out[102]: [[1, 3, 5], [7, 4, 2], [0, 8, 3]]
In [103]: b.extend(a)
In [104]: b
Out[104]: [[1, 3, 5], [7, 4, 2], [0, 8, 3], [1, 3, 5], [7, 4, 2], [0, 8,3]]
list 提供的删除元素方法有 pop,它默认删除 list 实例的最后一个元素,基于此,可以模拟栈(first in last out)的功能,因此 Python 中没有内置单独的栈对象。
In [94]: last = a.pop()
In [95]: a
Out[95]: [[1, 3, 5], [7, 4, 2]]
In [96]: last
Out[96]: [0, 8, 3]
pop 后返回了删除的最后一个元素,last。
除了 pop 的默认删除最后一个元素,如果想删除某个指定元素呢? remove 可以实现。它们具体使用如下:
In [111]: a.remove([7,4,2])
In [112]: a
Out[112]: [[1, 3, 5], [0, 8, 3]]
In [115]: a.remove(a[0])
In [116]: a
Out[116]: [[0, 8, 3]]
可以看到,它可以实现删除制定的某个元素。
更新 list 的某个元素值,只需要索引到这个元素,然后重新赋值即可。所以更新操作的关键还是要知道如何访问某个或某些元素,Python 为此提供了强大的单个下标索引和切片索引机制。
如下所示, list 的 insert 可以实现在指定索引位置添加元素,a[1] 返回第一维的索引编号从 0 开始的,索引为 1 的元素, a[1][0] 返回其第二维的索引为 0 的元素。而 a[1][:2] 返回其第二维的索引为 0, 1,注意不包括 2 的切片。
In [121]: a.insert(0,[1,3,5])
In [122]: a
Out[122]: [[1, 3, 5], [0, 8, 3]]
In [123]: a[1]
Out[123]: [0, 8, 3]
In [124]: a[1][0]
Out[124]: 0
In [125]: a[1][:2]
Out[125]: [0, 8]
语言也是一门底层系统,工程角度看,就像开头提到的,系统的健壮性是重要的。比如 C++、Java、C# 等语言,数组访问抛出的一个比较常见的错误就是 outOfIndex,超出数组访问界限。不过,Python 没有像这些语言那样,也内置了这个异常,而是选择沉默,把该返回的返回给我们,对越界不理会。比如:
In [126]: a[1][:200]
Out[126]: [0, 8, 3]
很显然,第二维度的长度为 3, 在索引到 200 时,显然会越界,但是 Python 不会抛出任何异常,而是正常返回这一维度的所 有元素即可。
Python 的 list 存储的元素可以为不同类型(hetergeneous), 而不像 C++、Java、C# 那样要求是同质(homogeneous)的。
In [127]: a = [1,'de']
In [128]: a
Out[128]: [1, 'de']
3.2.2 dict 核心知识
dict 就是一种字典结构,key 唯一。
创建一个 dict ,在此介绍两种,一种直接 {},另一种 dict 构造函数创建,如下:
In [131]: d = {'a':[1,2,3], 'b':['gz','lg','zx']}
In [132]: d
Out[132]: {'a': [1, 2, 3], 'b': ['gz', 'lg', 'zx']}
另一种 dict 构造函数创建,如下,第一个参数指定 keys,第二个参数指定 value。
In [174]: d = dict.fromkeys(['a','b'],[1,2,5])
In [175]: d
Out[175]: {'a': [1, 2, 5], 'b': [1, 2, 5]}
接下来,依次介绍字典内元素的增加、删除、更新、访问。增加元素对于 dict 而言就是增加一个键值对,如下所示:
In [3]: d = dict.fromkeys(['a','b'],[1,2,3])
In [4]: d
Out[4]: {'a': [1, 2, 3], 'b': [1, 2, 3]}
In [5]: d['c'] = ['gz','lg']
In [6]: d
Out[6]: {'a': [1, 2, 3], 'b': [1, 2, 3], 'c': ['gz', 'lg']}
Python 中的 dict,如果添加一个键值对时,原字典中此键已经存在,则直接更新,比较方便,如下:
In [1]: d = dict.fromkeys(['a','b'],[1,2,3])
In [2]: d['c'] = ['gz','lg']
In [3]: d
Out[3]: {'a': [1, 2, 3], 'b': [1, 2, 3], 'c': ['gz', 'lg']}
In [4]: d['b'] = [3,5,8]
In [5]: d
Out[5]: {'a': [1, 2, 3], 'b': [3, 5, 8], 'c': ['gz', 'lg']}
想要删除字典中的某个键值对时,使用 pop,传入想要删除的键值对的键,如下:
In [7]: d
Out[7]: {'a': [1, 2, 3], 'b': [3, 5, 8], 'c': ['gz', 'lg']}
In [8]: d.pop('b')
Out[8]: [3, 5, 8]
In [9]: d
Out[9]: {'a': [1, 2, 3], 'c': ['gz', 'lg']}
如果字典中不存在此键,则返回设置的默认值,比如我们自己设置的: 'key is not in dict' , 如下:
In [9]: d
Out[9]: {'a': [1, 2, 3], 'c': ['gz', 'lg']}
In [10]: d.pop('b','key is not in dict')
Out[10]: 'key is not in dict'
访问键值对,只能通过键,访问对应的值,如下:
In [11]: d['a']
Out[11]: [1, 2, 3]
dict 是可迭代的对象类型,可以通过 for 遍历每个键值对,如下直接访问每个键:
In [12]: for i in d:
...: print(i)
...:
a
c
要想访问每个键值对,可以借助字典的 items() 访问,items 方法返回字典内每项的视图:
items(...) method of builtins.dict instance
D.items() -> a set-like object providing a view on D's items
items 方法遍历获得键值对,如下:
In [13]: help(d.items)
In [14]: for item in d.items():
...: print(item)
...:
('a', [1, 2, 3])
('c', ['gz', 'lg'])
3.2.3 tuple 和 set 简介
tuple 对象是以 () 创建的,set 对象是以 {} 创建的,下面简单认识下它们:
In [20]: t = ('a',[1,4,6],{'b':[4,2,0]}) #tuple有3个元素
In [21]: t
Out[21]: ('a', [1, 4, 6], {'b': [4, 2, 0]})
In [22]: s = {1,4,6} # 创建 set
In [23]: s
Out[23]: {1, 4, 6}
简单来说,tuple 是不可更改的对象(immutable),也就是一旦创建,里面的元素就不能再改动了。set 是无重复元素的集合体,是可更改的对象(mutable),它和 list、dict 一样都是 mutable 的对象。关于 immutable 和 mutable 的性质,使用注意事项,在此不再详细展开,后续进阶课程里,我们可以详细讨论。
3.3 Python 入门必备知识点
介绍完了Python 内的几个核心对象后,下面来认识 Python 语言入门的几个语法特性,可以说它们是入门 Python 的必备知识了。
3.3.1 对象上的特殊函数
此小节介绍的这些,可以算是进阶部分,刚入门的小伙伴可以后面再来回顾。
常用的 Python 解释器 CPython ,底层都是使用 C 开发的,如 list、dict、tuple、set,其实就是一个结构体。
细心的小伙伴可能会观察到,list 的里面存在一些特殊函数,它们都以双划线__开头,双划线结尾,这类方法可以看做为对象上的特殊方法。我们新建一个的类上,默认都存在这类特殊方法。
接下来,演示如何实现这类特殊的函数。
In [37]: class student:
...: def __init__(self,name):
...: self.name = name
...: def __len__(self):
...: return len(self.name)
In [38]: xiaoming = student('xiaoming')
In [39]: len(xiaoming)
Out[39]: 8
如上,重新实现对象上的默认方法 __init__
和 __len__
.
在这里为大家起个头,更多其他特殊方法,大家可以自行研究,后续我们可以再展开讨论。
3.3.2 列表生成式
Python 的列表表达式,可以生成 list 对象,基本语法和用法如下:
In [40]: [abs(i) for i in [-4,3,1,-9,2]]
Out[40]: [4, 3, 1, 9, 2]
结合 if 和 else,写起来更加简洁,如下:
In [42]: [i+1 if i>0 else i+1 for i in [-4,3,1,-9,2]]
Out[42]: [-3, 4, 2, -8, 3]
3.3.3 位置参数和关键字参数
如果介绍 Python 入门,不介绍函数的位置参数 ( positional argument ) 和关键字参数( keyword argument ) ,总是感觉缺少点什么,它们在 Python 函数中到处可见,理解和使用它们,为我们日后深入 Python 打下坚实的根基。
我们在 Python 中到处可见类似这样的函数:
In [43]: def fun(*args, **kwargs):
...: print(args,kwargs)
*args
表示位置参数,**kwargs
表示关键字参数。
相比于 Java, C++ 语言,这类参数为我们带来很大的便利,当我们不知道形参的个数时,可以声明这样的形参。
具体使用,如下:
In [44]: fun([4,3,1],name = 'gz', color = 'red')
([4, 3, 1],) {'name': 'gz', 'color': 'red'}
可以看到位置参数 args 被传入 [4,3,1]
,并且被载入到 tuple 对象 args 中,而关键字参数统一被传入了 dict 对象 kwargs 中。
使用位置参数和关键字参数,需要注意一点,位置参数不能位于关键字参数后面,否则会弹出 SyntaxError ,如下:
In [45]: fun(name = 'gz', [4,3,1])
File "<ipython-input-45-cd568523bb4d>", line 1
fun(name = 'gz', [4,3,1])
^
SyntaxError: positional argument follows keyword argument
关于 Python,主要介绍了 Python 语言入门必备的知识,包括了解 Python 的解释性;最常用的核心对象,比如 list, dict, tuple, set;一些重要的使用必备知识点需要了解,比如对象上特殊方法,列表生成式,位置参数和关键字参数等。
这些可能还不够,还有一些重要的特性,比如 Python 的生成器(generator),面向函数式编程模块等,我们可以接下来在进阶课程中介绍。
NumPy 入门必备知识
Python 已经够强大,用 Python 做数据分析时,发现需要写不少 for 循环,尤其当嵌套起来时,不但影响开发效率,还会影响代码的可读性。
后来,出现了很多基于 Python 开发的科学计算库,用于数据分析最优秀的工具包之一当属 Pandas,它对 R 语言也非常友好。
Pandas 库是基于 NumPy 和 Python 开发的,它借鉴了很多 NumPy 的设计理念,因此,介绍 Pandas 前,我们先了解 NumPy 的入门必备知识。
4.1 ndarray 对象
NumPy 系统封装了一个强大的类似于多维数组的对象。使 NumPy 前,需要先安装 NumPy 包,推荐通过安装 anaconda 使用各个常用的包。
import numpy as np # 导入 NumPy 包,起个别名为 np
创建一个 ndarray 对象,如下:
In [47]: n1d = np.array([1,4,5])
In [48]: n1d
Out[48]: array([1, 4, 5])
n1d 实例支持的一些数值操作与原声的 Python 有所不同,比如乘法操作,在 Python 中 *
,实现元素的复制:
In [49]: [1,4,5]*3
Out[49]: [1, 4, 5, 1, 4, 5, 1, 4, 5]
而在 NumPy 中 ndarray 数组支持的 *
,实现的功能如下,每个元素都乘以 3.
In [50]: n1d*3
Out[50]: array([ 3, 12, 15])
如果在 Python 中实现这个效果,我们需要循环遍历每个元素来完成,如这样实现:
In [52]: [i*3 for i in [1,4,5]]
Out[52]: [3, 12, 15]
这种实现方式,没有 NumPy 实现的更简洁,这正体现了 NumPy 的一种设计思想:向量化(vectorization).
下面介绍 NumPy 的这个重要设计思想。
4.2 NumPy 的向量化增强
再通过一些例子感受下,向量化设计给我们带来的便捷。
linspace 默认线性切分 50 份,且默认包含边界值 10.
In [57]: b = np.linspace(0,10)
In [58]: b
Out[58]:
array([ 0. , 0.20408163, 0.40816327, 0.6122449 , 0.81632653,
1.02040816, 1.2244898 , 1.42857143, 1.63265306, 1.83673469,
2.04081633, 2.24489796, 2.44897959, 2.65306122, 2.85714286,
3.06122449, 3.26530612, 3.46938776, 3.67346939, 3.87755102,
4.08163265, 4.28571429, 4.48979592, 4.69387755, 4.89795918,
5.10204082, 5.30612245, 5.51020408, 5.71428571, 5.91836735,
6.12244898, 6.32653061, 6.53061224, 6.73469388, 6.93877551,
7.14285714, 7.34693878, 7.55102041, 7.75510204, 7.95918367,
8.16326531, 8.36734694, 8.57142857, 8.7755102 , 8.97959184,
9.18367347, 9.3877551 , 9.59183673, 9.79591837, 10. ])
b + 1 操作,默认每个元素都加1,这其中还涉及到 NumPy 的广播机制,会在接下来小节中介绍。
In [59]: b + 1
Out[59]:
array([ 1. , 1.20408163, 1.40816327, 1.6122449 , 1.81632653,
2.02040816, 2.2244898 , 2.42857143, 2.63265306, 2.83673469,
3.04081633, 3.24489796, 3.44897959, 3.65306122, 3.85714286,
4.06122449, 4.26530612, 4.46938776, 4.67346939, 4.87755102,
5.08163265, 5.28571429, 5.48979592, 5.69387755, 5.89795918,
6.10204082, 6.30612245, 6.51020408, 6.71428571, 6.91836735,
7.12244898, 7.32653061, 7.53061224, 7.73469388, 7.93877551,
8.14285714, 8.34693878, 8.55102041, 8.75510204, 8.95918367,
9.16326531, 9.36734694, 9.57142857, 9.7755102 , 9.97959184,
10.18367347, 10.3877551 , 10.59183673, 10.79591837, 11. ])
到目前,你可能对向量化有一定认识了。用 Python 写的原生函数,默认参数只接受单个元素,比如:
In [61]: def myabs(a):
...: return abs(a)
...:
In [62]: myabs(-5)
Out[62]: 5
如果传入一个 list,就会报错:
In [63]: myabs([1,4,-3])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-63-f7385068c934> in <module>
----> 1 myabs([1,4,-3])
<ipython-input-61-860035516c2e> in myabs(a)
1 def myabs(a):
----> 2 return abs(a)
3
TypeError: bad operand type for abs(): 'list'
那么如何增强 myabs 函数功能,使之具备处理 list 的能力呢?可以通过 NumPy 的向量化函数 vectorize 实现,如下所示,非常方便。
In [64]: zq_myabs = np.vectorize(myabs)
In [65]: zq_myabs([1,3,-5])
Out[65]: array([1, 3, 5])
以上就是 NumPy 的向量化设计机制,下面介绍 NumPy 中另一个强大的设计机制:传播(broadcast),这个机制不仅存在于 NumPy 中,Pandas, TensorFlow 中也都有。
作为 NumPy 入门者,这个机制理解起来可能有一定困难,不过它真的太重要了,一定要慢慢理解它,这是成为一个 NumPy 专家的必备知识。
4.3 NumPy 的传播机制
先看一个例子:
In [66]: a = np.array([[1],[-5],[3]])
In [67]: a
Out[67]:
array([[ 1],
[-5],
[ 3]])
In [68]: b = np.array([[5,2,4,9]])
In [69]: b
Out[69]: array([[5, 2, 4, 9]])
In [70]: a + b
Out[70]:
array([[ 6, 3, 5, 10],
[ 0, -3, -1, 4],
[ 8, 5, 7, 12]])
对刚接触 NumPy 的小伙伴,可能看到 a+b 的结果有点发蒙,感觉这是一个神奇的操作,不知怎么回事。
满足以下任意一种情况,都可能发生广播: 1. 如果两个数组的末尾维度(trailing dimension)的长度相符;
- 参与计算的某个维度长度为 1
广播会在缺失和(或)长度为 1 的维度上进行。
再回头上面那个例子,分析下广播是如何发生的。首先检查它们是否满足可广播的条件。
检查 a 的 shape
a = np.array([[1],[-5],[3]])
In [71]: a.shape
Out[71]: (3, 1)
检查 b 的 shape
b = np.array([[5,2,4,9]])
In [72]: b.shape
Out[72]: (1, 4)
容易发现,参与计算的两个对象都出现了某个维度长度为1的情况,因此满足发生传播的条件。
根据广播机制,广播会发生在缺失或长度为 1 的维度上进行,比如 a 会在第二个维度上扩展成为 4, shape 变为 (3,4),扩展后的形状为:
In [75]: ae
Out[75]:
array([[ 1, 1, 1, 1],
[-5, -5, -5, -5],
[ 3, 3, 3, 3]])
b 同理会在第一个维度上扩展为 3, shape 也会变为 (3,4),扩展后的形状如下:
In [78]: be
Out[78]:
array([[5, 2, 4, 9],
[5, 2, 4, 9],
[5, 2, 4, 9]])
这样,对应元素就可以相加了,如下:
In [79]: ae + be
Out[79]:
array([[ 6, 3, 5, 10],
[ 0, -3, -1, 4],
[ 8, 5, 7, 12]])
最后一个细节,NumPy 的 ndarray 对象每个元素类型必须是一致的。这一点不同于原生的 Python,可以不同类型的元素出现在同一个 list 中。
以上就是 NumPy 的必备精华知识,接下来介绍数据分析利器:Pandas。
Pandas 数据分析必备入门知识
Pandas 成为了 Python 数据分析的必备工具包。下面介绍它的两大对象,以及一些常用的数据分析函数。
5.1 类一维数组 Series
Series 是 pandas 两大数据结构中(DataFrame,Series)的一种,我们先从 Series 的定义说起,Series 是一种类似于一维数组的对象,它由一组数据(各种 NumPy 数据类型)以及一组与之相关的数据标签和索引组成。
Series 对象也是一个 NumPy 的数组,因此 NumPy 的数组处理函数可以直接对 Series 进行处理。
与此同时,Series 除了可以使用位置索引作为下标存取元素之外,还可以使用标签下标
存取元素,这一点和字典相似,每个 Series 对象都由两个数组组成: 1. index:它是从 NumPy 数组继承的 Index 对象,保存标签信息。 2. values:保存值的 NumPy 数组。
接下来,分别介绍 Series 内元素的增加、删除、修改、访问。
5.1.1 创建 Series
Series 的标准构造函数, 列举常用的几个参数:
Series(data=None, index=None, dtype=None, name=None)
其中,data 为数据部分,index 为标签部分,省略下默认为自增整数索引,dtype 为 str, numpy.dtype, or ExtensionDtype。
创建一个 series,如下:
In [85]: ps = pd.Series(data=[-3,2,1],index=['a','f','b'],dtype=np.float32)
In [86]: ps
Out[86]:
a -3.0
f 2.0
b 1.0
dtype: float32
5.1.2 增加元素
在 ps 基础上增加一个元素,使用 append,如下:
In [112]: ps.append(pd.Series(data=[-8.0],index=['f']))
Out[112]:
a 4.0
f 2.0
b 1.0
f -8.0
dtype: float64
可以看到,Pandas 允许包含重复的标签
In [114]: psn = ps.append(pd.Series(data=[-8.0],index=['f']))
In [115]: psn
Out[115]:
a 4.0
f 2.0
b 1.0
f -8.0
dtype: float64
In [116]: psn['f']
Out[116]:
f 2.0
f -8.0
dtype: float64
利用标签访问元素,返回所有带标签的数据。
5.1.3 删除元素
使用 drop 删除指定标签的数据,如下:
In [119]: ps
Out[119]:
a 4.0
f 2.0
b 1.0
dtype: float32
In [120]: psd = ps.drop('f')
In [121]: psd
Out[121]:
a 4.0
b 1.0
dtype: float32
注意不管是 append 操作,还是 drop 操作,都是发生在原数据的副本上,不是原数据上。
5.1.3 修改元素
修改数据,直接通过修改的标签,更新对应的数据,就可以,如下所示:
In [123]: psn
Out[123]:
a 4.0
f 2.0
b 1.0
f -8.0
dtype: float64
In [124]: psn['f'] = 10.0
In [125]: psn
Out[125]:
a 4.0
f 10.0
b 1.0
f 10.0
dtype: float64
标签相同的数据都被修改
5.1.4 访问元素
访问元素,Pandas 提供两种方法,一是通过默认的整数索引,或者在 Series 对象未被显示的指定 label 时,都是通过索引访问;另一种方式是通过标签访问。
In [126]: ps
Out[126]:
a 4.0
f 2.0
b 1.0
dtype: float32
In [128]: ps[2] # 索引访问
Out[128]: 1.0
In [127]: ps['b'] # 标签访问
Out[127]: 1.0
5.2 类二维数组 DataFrame
DataFrame 是 Pandas 的两个重要数据结构的另一个,可以看做是 Series 的容器。
DataFrame 同时具有行、列标签,是二维的数组,行方向轴 axis 为 0, 列方向 axis 为 1,如下:
axis : {0 or 'index', 1 or 'columns'}
5.2.1 创建 DataFrame
DataFrame 构造函数如下:
DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
参数意义与 Series 相似,不再赘述。创建 DataFrame 的常用方法:
In [134]: df = pd.DataFrame([['gz',4.0,'2019-01-01'],['lg',1.2,'2019-06-01']],in
...: dex = ['a','f'], columns = ['nm', 'y','da'])
In [135]: df
Out[135]:
nm y da
a gz 4.0 2019-01-01
f lg 1.2 2019-06-01
也可以通过字典传入,得到一样的数据框,如下:
In [136]: df2 = pd.DataFrame({'nm':['gz','lg'],'y':[4.0,1.2], 'da':['2019-01-01'
...: , '2019-06-01']},index = ['a','f'])
In [137]: df2
Out[137]:
nm y da
a gz 4.0 2019-01-01
f lg 1.2 2019-06-01
5.2.1 增加数据
通过增加一个 Series,扩充到 DataFrame 中,如下所示:
In [143]: dfn = df.append(pd.Series(data=['zx',3.6,'2019-05-01'],index=['nm','y'
...: ,'da'],name='b'))
In [144]: dfn
Out[144]:
nm y da
a gz 4.0 2019-01-01
f lg 1.2 2019-06-01
b zx 3.6 2019-05-01
Series 的 index 与数据框的 column 对齐,name 与数据框的 index 对齐。
5.2.2 删除数据
与 Series 删除类似,也是使用 drop 删除指定索引或标签的数据,如下,注意删除仍然是在 dfn 的副本上进行的,像下面这样删除对 dfn 没有任何影响。
In [145]: dfn.drop('b')
Out[145]:
nm y da
a gz 4.0 2019-01-01
f lg 1.2 2019-06-01
如果要删除某列,需要设定 axis 为 1, 如下所示:
In [147]: dfn.drop('y',axis=1)
Out[147]:
nm da
a gz 2019-01-01
f lg 2019-06-01
b zx 2019-05-01
5.2.3 修改数据
修改依然是先通过索引或标签定位到数据,然后修改,如下所示:
In [151]: dfn.loc['a','da']='2019-04-01'
In [152]: dfn
Out[152]:
nm y da
a gz 4.0 2019-04-01
f lg 1.2 2019-06-01
b zx 3.6 2019-05-01
修改数据作为入门,理解到这里或许可以了。但是要想进阶或真正成为 Pandas 专家,必须要了解一个关键点:链式赋值( chained assignment),大家可以自己先去查一查,在入门课程中,我们暂时不展开,后面进阶课程中会详细聊一聊。
5.2.4 访问数据
最新几个版本,Pandas 推荐使用访问接口 iloc、loc、访问数据,详细使用如下:
In [153]: df
Out[153]:
nm y da
a gz 4.0 2019-01-01
f lg 1.2 2019-06-01
In [154]: df.iloc[1,:]
Out[154]:
nm lg
y 1.2
da 2019-06-01
Name: f, dtype: object
In [155]: df.loc['f',:]
Out[155]:
nm lg
y 1.2
da 2019-06-01
Name: f, dtype: object
In [156]: df.loc['f','nm']
Out[156]: 'lg'
5.3 必备的 20 个统计学函数
文章一开始,我们提到入门数据分析,需要了解一定的统计学知识,常用的统计指标,比如众数、均值、方差、四分位数等。好消息是,Pandas 中都实现类似这些统计指标,下面分别列举:
函数 | 英文术语 | 中文 |
---|---|---|
count | Number of non-null observations | 观测值的个数 |
sum | Sum of values | 求和 |
mean | Mean of values | 求*均值 |
mad | Mean absolute deviation | *均绝对方差 |
median | Arithmetic median of values | 中位数 |
min | Minimum | 最小值 |
max | Maximum | 最大值 |
mode | Mode | 众数 |
abs | Absolute Value | 绝对值 |
prod | Product of values | 乘积 |
std | Bessel-corrected sample standard deviation | 标准差 |
var | Unbiased variance | 方差 |
sem | Standard error of the mean | 标准误差 |
skew | Sample skewness (3rd moment) | 偏度系数 |
kurt | Sample kurtosis (4th moment) | 峰度 |
quantile | Sample quantile (value at %) | 分位数 |
cumsum | Cumulative sum | 累加 |
cumprod | Cumulative product | 累乘 |
cummax | Cumulative maximum | 累最大值 |
cummin | Cumulative minimum | 累最小值 |
cov() | covariance | 协方差 |
corr() | correlation | 相关系数 |
rank() | rank by values | 排名 |
pct_change() | time change | 时间序列变化 |
以上 20 个,都在 Pandas 中对应有详细的意义解释,大家可以自己去研究,在此不再详细展开。
5.4 必备的缺失值处理技巧
Pandas 提供了一些便利的函数,可以帮助我们快速按照设想处理、解决空值。
空值处理的第一招:快速确认数据集中是不是存在空值。有两个函数 isnull、notnull,可以帮助我们快速定位数据集中每个元素是否为空值。
数据源如下所示:
In [165]: dfn
Out[165]:
nm y da
a gz NaN 2019-04-01
f lg 1.2 NaN
b zx 3.6 2019-05-01
检测哪些位置存在空值,如下:
In [166]: pd.isnull(dfn)
Out[166]:
nm y da
a False True False
f False False True
b False False False
Pandas 对象某列或某行,直接拿 np.nan , None 判断元素是否为空,发现返回的都是 False。注意:这样做是不可取的!
第二招,假设存在空值,可以使用 Pandas 中的 fillna 函数填充空值,fillna 有一个关键参数: method,当设置 method 为 pad 时,表示怎样填充呢? 从上一个有效数据传播到下一个有效数据行。
如下,使用默认值 1.0 填充 y 列:
In [174]: dfn['y'] = dfn['y'].fillna(1.0)
In [175]: dfn
Out[175]:
nm y da
a gz 1.0 2019-04-01
f lg 1.2 NaN
b zx 3.6 2019-05-01
第三招,检测到了空值数据,但是不想做任何填充,而是仅仅想丢弃这些空值数据,Pandas 提供了 dropna 函数做这件事情。里面有两个关键参数:axis, how, 例如组合:axis = 0,how = any,表示某行只要某个元素为空值,就丢弃。
In [177]: dfn
Out[177]:
nm y da
a gz 1.0 2019-04-01
f lg 1.2 NaN
b zx 3.6 2019-05-01
In [178]: dfn.dropna(axis=0,how='any')
Out[178]:
nm y da
a gz 1.0 2019-04-01
b zx 3.6 2019-05-01
5.5 必备的数据透视处理函数
Pandas 也有类似 Excel 中提供的数据透视功能。参考官方给出的一个数据,介绍下 pivot_table 函数如何实现数据透视的。
构造数据:
In [182]: df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
...: ... "bar", "bar", "bar", "bar"],
...: ... "B": ["one", "one", "one", "two", "two",
...: ... "one", "one", "two", "two"],
...: ... "C": ["small", "large", "large", "small",
...: ... "small", "large", "small", "small",
...: ... "large"],
...: ... "D": [1, 2, 2, 3, 3, 4, 5, 6, 7],
...: ... "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]})
In [183]: df
Out[183]:
A B C D E
0 foo one small 1 2
1 foo one large 2 4
2 foo one large 2 5
3 foo two small 3 5
4 foo two small 3 6
5 bar one large 4 6
6 bar one small 5 8
7 bar two small 6 9
8 bar two large 7 9
In [216]: pd.pivot_table(df, values=['D','E'], index=['A','B'], columns='C',aggf
...: unc=[np.mean,np.max])
结果如下所示:
Out[216]:
mean amax
D E D E
C large small large small large small large small
A B
bar one 4.0 5.0 6.0 8.0 4.0 5.0 6.0 8.0
two 7.0 6.0 9.0 9.0 7.0 6.0 9.0 9.0
foo one 2.0 1.0 4.5 2.0 2.0 1.0 5.0 2.0
two NaN 3.0 NaN 5.5 NaN 3.0 NaN 6.0
下面分步解析数据透视的过程
我们数据透视是以 A 和 B 两列为 index,一旦选定,内部实现中会根据 A 和 B 形成一个多级索引,并对数据分组。第二维度,也就是 axis 为 1 的维度,设置为 C 列,这样数据会被重构为这种二维表,并且如果聚合函数缺省情况下,默认统计*均值。
In [188]: pd.pivot_table(df, index=['A','B'],columns='C')
结果如下所示:
Out[206]:
D E
C large small large small
A B
bar one 4.0 5.0 6.0 8.0
two 7.0 6.0 9.0 9.0
foo one 2.0 1.0 4.5 2.0
two NaN 3.0 NaN 5.5
当为 pivot_table 设置上 values 来自哪些列,同时定义聚合函数后,就最终得到数据的透视结果。
以上就是 Pandas 里的一些关于数据分析的核心功能,当然还有很多必备的功能,比如分组,排序,数据分箱,编码......我们以后再聊。
Matplotlib
matplotlib 是基于 Python 语言的开源项目,旨在为 Python 提供一个数据绘图包。
实际上,matplotlib 的对象体系严谨而有趣,为使用者提供了巨大的发挥空间。在熟悉了核心对象之后,可以轻易的定制图像。
接下来,介绍 matplotlib API 的核心对象,并介绍如何使用这些对象来实现绘图。
6.1 必备的绘图理论知识
先看一段代码:
# object-oriented plot
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
line, = ax.plot([0,1], [0,1])
ax.set_title("a straight line ")
ax.set_xlabel("x label")
ax.set_ylabel("y label")
canvas.print_figure('chatpic1.jpg')
上面这段代码,至少构建了四个对象: fig( Figure 类), canvas( FigureCanvas 类), ax( Axes 类), line(Line2D 类)。
在 matplotlib 中,整个图像为一个Figure
对象,在 Figure
对象中可以包含一个或多个 Axes
对象,每个Axes
对象都是一个拥有自己坐标系统的绘图区域。
Axes
由 xaxis
, yaxis
, title
, data
构成,xaxis
由坐标轴的线 ,tick
以及label
构成,如下图所示:
上段代码中,ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
,参数含义代表比例,分别表示:图形区域的左边界距离整个 figure 左侧 10% ,底部 10%,宽度和高度都为整个 figure 宽度和高度的 80%。
rect : sequence of float
The dimensions [left, bottom, width, height] of the new axes. All
quantities are in fractions of figure width and height.
canvas 对象,代表了真正进行绘图的后端(backend)。
在具备这些绘图的基本理论知识后,再去使用 matplotlib 库时,就顺手多了。
6.2 绘图必备 100 行代码
Matplotlib 中提供了一个类似于 Matlab 的绘图模块,非常易用,尤其是对于入门者,非常容易上手。
如果熟练使用这个模块,绘制常见的数据分析可视化图,已经基本没有任何问题了。
下面,我们参考哈佛大学关于这部分的课程材料,结合*时常用的,提炼成 100 行代码,应该是最精简的可视化 100 行代码了。
导入绘图模块:
import matplotlib
import matplotlib.pyplot as plt
导入 NumPy 模块:
import numpy as np
准备数据:
x = np.linspace(0, 5, 10)
y = x ** 2
绘制折线图:
plt.plot(x, y, 'r')
plt.xlabel('x')
plt.ylabel('y')
plt.title('title')
plt.show()
第一种绘制多图的方法:
plt.subplot(1,2,1)
plt.plot(x, y, 'r--')
plt.subplot(1,2,2)
plt.plot(y, x, 'g*-')
plt.show()
第二种绘制多图的方法:
fig = plt.figure()
axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # main axes
axes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # insert axes
# 主图
axes1.plot(x, y, 'r')
axes1.set_xlabel('x')
axes1.set_ylabel('y')
axes1.set_title('title')
# 插入的图
axes2.plot(y, x, 'g')
axes2.set_xlabel('y')
axes2.set_ylabel('x')
axes2.set_title('insert title')
第三种绘制多图的方法:
fig, axes = plt.subplots(nrows=1, ncols=2)
for ax in axes:
ax.plot(x, y, 'r')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title')
保存图片:
fig.savefig("chatpic33.png")
设置图例: legend
fig, ax = plt.subplots()
ax.plot(x, x**2, label="y = x**2")
ax.plot(x, x**3, label="y = x**3")
ax.legend(loc=2); # 上、左角
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title');
设置字体,大小:
matplotlib.rcParams.update({'font.size': 18, 'font.family': 'serif'})
设置 latex 格式:
ax.plot(x, x**2, label=r"`$y = \alpha^2$`")
设置颜色,线条:
fig, ax = plt.subplots()
ax.plot(x, x+1, color="red", alpha=0.5) # 半透明红色
ax.plot(x, x+2, color="#1155dd") # 十六进制RGB
ax.plot(x, x+3, color="#15cc55")
添加网格 grid:
fig, axes = plt.subplots(1, 2, figsize=(10,3))
# 默认网格显示
axes[0].plot(x, x**2, x, x**3, lw=2)
axes[0].grid(True)
# 定制网格显示
axes[1].plot(x, x**2, x, x**3, lw=2)
axes[1].grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
双轴显示:
fig, ax1 = plt.subplots()
ax1.plot(x, x**2, lw=2, color="blue")
ax1.set_ylabel(r"area `$(m^2)$`", fontsize=18, color="blue")
for label in ax1.get_yticklabels():
label.set_color("blue")
ax2 = ax1.twinx()
ax2.plot(x, x**3, lw=2, color="red")
ax2.set_ylabel(r"volume `$(m^3)$`", fontsize=18, color="red")
for label in ax2.get_yticklabels():
label.set_color("red")
更多的二维图形展示:
xx = np.linspace(-0.75, 1., 100)
n = np.array([0,1,2,3,4,5])
fig, axes = plt.subplots(1, 4, figsize=(12,3))
axes[0].scatter(xx, xx + 0.25*np.random.randn(len(xx)))
axes[0].set_title("scatter")
axes[1].step(n, n**2, lw=2)
axes[1].set_title("step")
axes[2].bar(n, n**2, align="center", width=0.5, alpha=0.5)
axes[2].set_title("bar")
axes[3].fill_between(x, x**2, x**3, color="green", alpha=0.5);
axes[3].set_title("fill_between")
文本标注:
fig, ax = plt.subplots()
ax.plot(xx, xx**2, xx, xx**3)
ax.text(0.15, 0.2, r"`$y=x^2$`", fontsize=20, color="blue")
ax.text(0.65, 0.1, r"`$y=x^3$`", fontsize=20, color="green");
三维图绘制:
from mpl_toolkits.mplot3d.axes3d import Axes3D
alpha = 0.7
phi_ext = 2 * np.pi * 0.5
def flux_qubit_potential(phi_m, phi_p):
return 2 + alpha - 2 * np.cos(phi_p) * np.cos(phi_m) - alpha * np.cos(phi_ext - 2*phi_p)
phi_m = np.linspace(0, 2*np.pi, 100)
phi_p = np.linspace(0, 2*np.pi, 100)
X,Y = np.meshgrid(phi_p, phi_m)
Z = flux_qubit_potential(X, Y).T
fig = plt.figure(figsize=(14,6))
ax = fig.add_subplot(1, 2, 1, projection='3d')
p = ax.plot_surface(X, Y, Z, rstride=4, cstride=4, linewidth=0)
ax = fig.add_subplot(1, 2, 2, projection='3d')
p = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=matplotlib.cm.coolwarm, linewidth=0, antialiased=False)
cb = fig.colorbar(p, shrink=0.5)
机器学习线性回归模型
数据分析也和机器学习密不可分,对于手上的数据,我们想了解它最后回归哪个模型,此时机器学习的回归算法就是不二之选。
接下来,先介绍一些机器学习的基本概念,帮助不熟悉机器学习的小伙伴快速做个概念普及。
7.1 11 个必备基本概念
-
数据集(data set) 记录的集合,假如我们用 3 个特征,分别为色泽,根蒂,响声来描述西瓜的特点,并且拿到了基于这3个特征的 10 万条记录。如果记录到.csv文件中,这个文件的结构可以记为: fruit[100000][3] ,这样一个二维数组,行数为 10 万,列数为 3(因为有 3 个特征)。
-
属性(attribute) 反映事件或对象在某方面的表现或性质的事项,例如色泽,根蒂,响声等,又称为特征(feature)。属性上的取值,如青绿,浊响等,称为属性值(attribute value)。
-
样本空间(sample space) 又称为属性空间(attribute space),或输入空间。它可以理解为训练数据中实际出现的所有属性值构成的集合空间,如上文中提到的 10 万条西瓜记录,每条记录有 3 个属性取值,组成了一个 fruit[100000][3] 的样本空间。
-
假设空间(hypothetical space) 和样本空间有点类似,它是理论上的所有可能属性值构成的集合空间。
-
特征向量(feature vector) 假如将色泽,根蒂,敲声三个属性作为三个坐标轴 x1, x2, x3,每个西瓜对应一个空间点(一个坐标向量),每个这种示例称为一个特征向量,记为 (x1, x2, x3 )。
-
标记(label) 关于示例结果的信息,比如判断一个西瓜是好瓜,那么这个西瓜便拥有了标记示例,这个西瓜便成了样例(example)。一般用 Xi , yi 表示第 i 个样例,其中 yi 是示例 Xi 的标记。
-
学习(learning) 从数据中学得模型的过程,又称为训练(training)。正如上文所示,10万条西瓜数据集,根据它的三个特征,和每条特征的标记,经过计算最后得到了一个 f,通过这个 f 我们能预测第 10001 个西瓜是否是好瓜,这个过程被称为学习。
-
训练数据 (training data) 训练过程中使用的数据,其中每个样本称为一个训练样本(training sample),训练样本组成的集合称为训练集(training set)。通过这些训练数据通过学习,最终得出一个 f,也就是我们学到的模型。与之相对应的是测试数据,为了测试通过训练数据得到的f准确度能高不高,我们特意预留出一些数据用来专门测试用,这部分数据就被称为测试数据。
-
回归(regression) 如果预测的是连续值,例如预测西瓜的成熟度 ,它必然是个大于 0 的小数值,比如成熟度为 0.9,0.75,抑或是根据房屋面积,使用年限两个特征预测某个房屋的价值,类似这种预测称为回归。
-
分类(classification) 如果我们要预测的是离散值,等于 0,1,2,3 等这类离散值,例如好瓜,坏瓜,称此类学习任务为分类。如果分类的结果为两类,又称此分类为二分类,通常称其中一个为正类(positive class),另一个为反类(negative class)。
-
聚类(clustering) 没有标记的记录集,并且我们还想学习这类数据集,比如想从里头挖出点有用的东西来。然后我们根据某些特征和算法将训练中的西瓜分成若干组,自动形成了几簇,这些簇可能对应一些潜在的概念,比如浅色瓜,深色瓜,本地瓜,这些概念我们都是事先不知道的。
7.2 三 个假定
我们拿到数据后,做完数据整理(前面章节中提到)和特征分析后,接下来想知道这些数据特征对目标函数的影响模型,即做数据回归。
为了简化我们的模型,选用线性数学模型。
为了保证线性数学模型能够取得成功,有两个前提假定非常重要。
-
假设真实值与预测值的误差项 ϵϵ 服从高斯分布,它直接影响线性回归模型是否取得成功,因为如果数据都是离散的,用线性回归预测准确度肯定不好。
-
预测的数据分布和训练时用到的数据分布是相同的
-
假定每个样本之间发生与否都是无关的,这样确保每个样本发生都是独立的。
7.3 建立线性回归模型
因为第 ii 个样本的误差项 ϵ(i)ϵ(i) 服从高斯分布,因此可得:
f(ϵ(i))=1√2πσe(−ϵ(i)22σ2)f(ϵ(i))=12πσe(−ϵ(i)22σ2)
因为建立的是线性数学模型,因此第 ii 个样本根据模型预测值为:
又知,误差项(又称代价函数或成本函数):
ϵ(i)=f(x(i)|θ)−y(i)ϵ(i)=f(x(i)|θ)−y(i)以上式子中:x(i),yix(i),yi 分别表示第 ii 个样本的实际取值,nn 表示特征的个数
综上可得:
至此,我们得到一个只含有 n+1n+1 个特征参数的等式,ff 表示误差取值的概率。
一般选用最大似然估计求权重参数,接下来介绍。
7.4 最大似然估计求参数
最大似然估计就是根据让已经发生的数据取值概率最大,上面说到的第 3 个假定就是样本是否发生是各自独立的,所以 mm 个样本误差的概率 f(ϵ(i))f(ϵ(i)) 都出现的概率可以转化为概率的乘积:
m∏i=1f(ϵ(i))∏i=1mf(ϵ(i))
显示中样本个数 mm 很大,因为又是概率,所以相乘的结果会很小,一般转化为对数,因此,又称最大对数似然估计,可得公式为:
结合已经得出的公式:
最终得到:
上式,就变为只含有 nn 个未知权重参数,求解最大值的优化问题。 [help me with MathJax]
7.5 梯度下降求解优化问题
梯度下降求解的是最小值,因此需要对上节式子取反后求最小值,故:
接下来,求出 J(θi)J(θi) 对权重参数 θiθi 的偏导数,这个不难求,回想下高中知识,便可以得出。
每次迭代时步,θiθi 的更新公式:
其中 ηη 就是学习率
至此公式推导全部结束,下面手写编码实现。
7.6 手写不调包实现的 5 个算子
算法框架包括以下 5 个算子:
'model' 建立的线性回归模型
'cost' 代价函数
'gradient' 梯度公式
'theta update' 参数更新公式
'stop stratege' 迭代停止策略:代价函数小于阈值法
下面分别写出以上五步的具体实现代码,
model
def model(theta,X):
theta = np.array(theta)
return X.dot(theta)
cost
def cost(m,theta,X,y):
'print(theta)'
ele = y - model(theta,X)
item = ele**2
item_sum = np.sum(item)
return item_sum/2/m
gradient
def gradient(m,theta,X,y,cols):
grad_theta = []
for j in range(cols):
grad = (y-model(theta,X)).dot(X[:,j])
grad_sum = np.sum(grad)
grad_theta.append(-grad_sum/m)
return np.array(grad_theta)
theta update
def theta_update(grad_theta,theta,sigma):
return theta - sigma * grad_theta
stop stratege
def stop_stratege(cost,cost_update,threshold):
return cost-cost_update < threshold
7.7 手写不调包实现的整体算法框架
OLS 算法整体框架
如下所示
def OLS(X,y,threshold):
start = time.clock()
'样本个数'
m=100
'设置权重参数的初始值'
theta = [0,0,0]
'迭代步数'
iters = 0;
'记录代价函数的值'
cost_record=[]
'学习率'
sigma = 0.0001
cost_val = cost(m,theta,X,y)
cost_record.append(cost_val)
while True:
grad = gradient(m,theta,X,y,3)
'参数更新'
theta = theta_update(grad,theta,sigma)
cost_update = cost(m,theta,X,y)
if stop_stratege(cost_val,cost_update,threshold):
break
iters=iters+1
cost_val = cost_update
cost_record.append(cost_val)
end = time.clock()
print("OLS convergence duration: %f s" % (end - start))
return cost_record, iters,theta
面试及资料分享
在介绍零基础完整入门数据分析的基本理论,工具包使用,机器学习的一个算法完整算法模型后,你需要多去动手实践,强化这些基本的方法论。
然后,你就可以着手准备面试了。下面分享一些面试真人实录,感谢公众号内一些铁粉贡献的宝贵面试材料。
下面节选的这个面试难度中等偏上,这个岗位主要还是偏算法多一些,对数据的处理分析考核少了一些。
之所以节选这个面试,是因为想要大家知识面更广一些,因为前面所讲主要偏数据分析,借助这个面试大家可以了解到更多与数据挖掘相关的一些面试常问到的算法。
大家快来看看了解多少吧,不会的也不要慌张,先把这些这篇面试中提问到的搞清楚。
8.1 数据挖掘工程师面试实录
感谢公众号铁粉地球球长贡献的面试经历。
面试某独角兽公司校招,面试地点是在北京某高校里面。
8.1.1 一 面
-
逻辑回归损失函数 我的回答:对数损失,或者预测值与实际值的交叉熵损失。
-
如果样本不均衡,需要在损失函数上做操作,该怎么处理? 我的回答:这个之前没遇到过,不过处理样本均衡可以通过下采样和上采样等方法。
-
伯努利分布概率为 P,执行 N 次中 k 次发生,用极大似然估计估算P的值 我的回答:在纸上写出了二项式分布的概率函数作为似然函数,P 为参数,求 argmax, (似然函数求导为零得到)P=k/N。
-
L1, L2 正则化。L1 正则化项怎么求导 我的回答:L1, L2 正则化主要为了防止过拟合,控制模型复杂度,使其具有更好的泛化能力。L2 正则化从贝叶斯理论看,参数分布服从高斯先验。L2 范数是各个参数*方和,是每个w2i<Cwi2<C的条件下最小化训练损失(经验损失)Loss,根据拉格朗日乘子法可以得到L = 训练损失 + 入 L2 范数;L2 能起到压缩参数的作用。L2 正则化从贝叶斯理论看,参数分布服从拉普拉斯先验。L1 范数是各个参数绝对值之和,是每个|wi|<C|wi|<C 的条件下最小化训练损失(经验损失)Loss,根据拉格朗日乘子法可以得到L = 训练损失 + 入L1 范数;L1 范数能起到是参数稀疏化作用,又是可以作为特征选择。同时,L0 正则化是真正起到稀疏化作用,可以认为 L1 正则化是 L0 正则化的*似,因为L0 正则化是NP问题,可能是非凸的,而且不易求导比较难用凸优化求解。L1 正则化在 0 点出不可导,一般做*似*滑处理,其他点得到 W 的符号。 (当时感觉回答完这个问题,进二面没啥问题了)
-
决策树算法以及防止过拟合方法。信息增益率比信息增益多了什么? 我的回答:我先介绍了自信息,信息熵(自信息的期望),信息增益等相关概念。基于信息增益的 ID3,基于信息增益率的 C4.5,基于基尼指数的 CART 等剪枝:后剪枝,预剪枝(调节树的深度,叶子的样本个数等方式),随机森林 Bagging 信息增益率相比于信息增益可以认为做了一个约束,除以原样本信息熵,避免用信息增益因类别过多一下子就全部分开,分成很矮,叶子很多的树(这里表达可能不太准确)。
-
写出 SVM 损失函数,如果过拟合,怎么参数调整 C 我的回答:我写出了正则项,让他给点提示。他说合页损失,给我画了损失函数图并和 LeRu 做比较。C求和 yif(xi)+12||w||yif(xi)+12||w||,如果过拟合,应该减小 C。这里如果除以 C,那么参数就被整合到了正则项里面,可能更好理解。还有如果因用到核函数过拟合,降低核函数复杂度。 [help me with MathJax]
-
随机森林与 boosting 区别 我的回答:他们都是集成学习方法,学习多个有差异且有一定精确度(>50%)的弱分类器。主要区别随机森林用到并行,boosting 通常情况下串行(这个不是很完整)。
-
Xgboost在GBDT基础上做了哪些改进 我的回答:Xgboost 是陈天奇大神在 GBDT 基础上提出来的,最大差别损失函数用到了二阶导数的信息。在特征粒度裂解上用到了并行处理,加快速度。特征裂解位置预先计算,做了排序并缓存,按百分位选择,而不是像 GBDT 全部遍历贪心选择。用到的损失函数用到自定义的 GINI 系数。同时基分类器不仅仅是回归树,可以是加了 L1L2 正则化的线性分类器等 (在网上看了好多改进,说了这几个主要的)。
8.1.2 二 面
-
求 sqrt()
-
一个数组,求除了某元素自身位置之外的其他元素累积,生成一个同长度的数组。要求不能用除法,时间复杂度 O(n),辅助空间 O(1)
-
又问了 SVM 参数怎么调
-
又问了决策树怎么防止过拟合
-
问了 L1,L2 正则化的作用,为什么
-
决策树算法:特征是连续值,特征是离散值的处理
-
一道几何概型的简单题 (画个图求面积比)
-
趣味题,如果一把枪弹夹里有六个子弹,其中有两个子弹会打死人,四个不会有受伤。假如面试官打了自己一枪没死,现在把枪交给你,你得朝自己开一枪,问开枪之前,你是否会让弹夹旋转,切换子弹。
8.1.3 三面
就问了一个问题:如何用int_8位整型数据,模拟两个int_32位整型数据相乘(写代码实现) (给了许多提示还是没写出来,挂了。)
8.2 推荐哈佛大学数据分析课程
这个课程是专门选用 Python 做数据分析的,并且上课选择的材料,使用的是现实中的数据,所以极其宝贵。大家一定要好好利用。
下面是课程的大纲:
课程网址:
小结
chat 开头我们介绍到现实社会是离不开数据分析的,然后介绍了数据分析入门学习路线,从三方便准备,分别是统计学基本知识、机器学习基本算法、编程语言及工具。
第二章介绍了数据分析重头戏:数据整理,分别探讨了,理解你的业务数据,明确各个特征的类型,找出异常数据,不得不面对缺失值,令人头疼的数据不均衡问题。
第三章介绍了 Python 入门必备知识,包括理解解释型和编译型, Python 最常用的对象,包括:list 核心知识,dict 核心知识,tuple 和 set 简介。Python 入门必备知识点,包括:对象上的特殊函数,列表生成式
第四章介绍了 NumPy 入门必备知识,包括 ndarray 对象,以及 NumPy 的最重要的两大机制: 向量化增强,NumPy 的传播机制。
第五章介绍了 Pandas 数据分析必备入门知识。类一维数组 Series,包括创建Series、增加元素、删除元素、修改元素、访问元素。类二维数组 DataFrame 包括:创建 DataFrame、增加数据、删除数据、修改数据、访问数据。后面介绍了必备的 20 个统计学函数,必备的缺失值处理技巧、必备的数据透视处理函数。
第六章介绍了 Matplotlib 的必备的绘图理论知识,根据哈佛大学的经典课程,总结了绘图必备 100 行代码,包括展示了 14 幅图,其中多图绘制,各种二维和三维图可视化,颜色、字体设置,latex符号设置,网格设置等。
第七章,介绍了 机器学习线性回归模型,包括介绍了 11 个必备基本概念。线性回归模型的 3 个假定,基于假定,建立线性回归模型,最大似然估计求参数,利用梯度下降求解优化问题,最后手写不调包实现 5 个算子,还有最终的整体算法框架
第八章介绍了面试和资料分享,面试数据挖掘工程师的真实实录,包括一 面、二 面、三面。最后推荐了哈佛大学的一门经典数据分析课程。
最后感谢大家的参与,文中的难免出现表述不准确之处,欢迎小伙伴们批评指正。
预祝在校的学弟学妹,学业有成;工作的兄弟姐妹,工作顺利!
如果觉得 chat 还可以的,还算走心的,欢迎帮助点赞和转发,让这篇 chat 可以帮助到更多小伙伴。