利用python进行数据分析
本篇是《利用python进行数据分析》的学习笔记。
numpy:数组与向量化计算
- ndarray,一种高效多维数组,提供了基于数组的便捷算术操作以及灵活的广播功能。
- 对所有数据进行快速的矩阵计算,而无需编写循环程序。
- 对硬盘中的数组数据进行读写的工具,并对映射文件进行操作。
- 线性代数,随机数生成和傅里叶变换功能。
- 用于连接numpy到C/C++类库的C语言API。
numpy本身并不提供建模和科学函数,理解numpy的数组以及基于数组的计算将帮助更高效的使用基于数组的工具,如pandas。
对于大多数数据分析应用,要关注在数据处理、清洗、构造子集、过滤、变换以及其他运算中进行快速的向量化计算。
- 常见的数组算法,sort、unique以及set操作。
- 高效的描述性统计和聚合数据。
- 数据排列和相关数据操作,如对异构数据进行merge和join。
- 使用数组表达式来表明条件逻辑。
- 分组数据的操作:聚合、变换、函数式操作。
numpy的设计对与含有大量数组的数据非常有效;numpy在内部将数据储存在连续的内存块上;numpy算法库使用C语言写,所以在操作数据内存时,不需要任何类型检查或其他管理操作;
numpy ndarray:多维数组对象,一个快速、灵活的大型数据集容器。
推荐使用import numpy as np的导入方式,numpy这个命名空间包含了大量与python内建函数重名的函数。
一个ndarray是一个通用的多维同类数据容器,每一个数组都有一个shape属性,用来表征数组的每一维度的数量,每一个数组都有一个dtype属性,用来描述数组的数据类型。
生成数组最简单的方式是使用array函数(np.array)。array函数接收任意序列型对象,生成一个新的包含传递数据的numpy数组。
除了array函数,还有其它方式创建新数组,(np.zeros((3,6))、np.ones((3,6))、np.empty((2,3,2)))
arange是python内建函数range的数组版:
ndarray数据类型
数据类型dtype,是一个特殊的对象,它包含了ndarray需要为某一种类型数据所申明的内存块信息(元数据)。dtype是numpy能与其他系统数据灵活交互的原因。
numpy所支持的数据类型:浮点型、整数、布尔、字符串……
可以使用astype方法显示的转换数组的数据类型:(使用astype时总是生成一个新的数组)
numpy数组算术
数组在进行批量操作不必进行for循环,称这种特性为向量化
基础索引与切片
一位数组的操作比较简单,看起来和列表类似。区别在于python的内建列表,数组的切片是原数组的视图,原地操作。所以如果只想对数组的copy切片,必须显示的复制数组(copy()函数)。
在二维数组中,每个索引对应的元素是一个一维数组
>>>arr= np.array([[1,3],[2,4]])
>>>arr[0][1]
>>>3
>>>arr[0,1]
>>>3
#这两种方式效果一样
axis=0 # 0轴表示行
axis=1 # 1轴表示列
数组的切片索引,结合列表的切片理解。
布尔索引
比较结果返回值是bool类型:
产生bool索引
布尔数组的长度必须和数组轴索引长度一致
比较类型:==、!=、~=(取反)
使用布尔索引选择数据时,总是生成数据的copy,即使返回的数组没有任何变化。
神奇索引
数组转置
transpose方法和T属性,
shape属性的三个数值(通道数,行数,列数)初始顺序为0,1,2
ndarray有一个swapaxes方法,该方法接收一对轴编号作为参数,并对轴进行调整用于重组数据(该方法返回的是数据的视图,没有对数据进行复制),虽然该方法返回的是视图,但不可以对视图进行赋值,所以调用属性不会改变原数组。
通用函数
一种在ndarray数据中进行逐元素操作的函数,对简单函数进行向量化封装
一元通用函数:sqrt、exp……
二元通用函数:add、maximum……
返回多个数组:modf(返回浮点值数组的整数部分和小数部分)
使用数组进行面向数组编程
将条件逻辑作为数组操作:numpy.where(条件,值一,值二)是三元表达式x if condition else y的向量化版本。
where在数据分析中的典型用法是根据一个数组来生成一个新的数组,传递给np.where的数组既可是同等大小的数字,也可是标量。
数学和统计方法:关于计算整个数组统计值或轴向数据的数学函数,可以作为数组类型的方法被调用。可以使用聚合函数(也叫缩减函数):sum、mean、std(标准差)。有的方法:cumsum(累积函数)、cumprod并不会聚合,会产生一个中间结果。
基础数组统计方法 | |
---|---|
方法 | 描述 |
sum | 沿着轴向计算所有元素累和 |
mean | 数学平均 |
std,var | 标准差和方差 |
min,max | 最大值和最小值 |
argmin,argmax | 最小值和最大值的位置 |
cumsum | 从0开始元素类积和 |
cumprod | 从1开始元素累积和 |
布尔值数组方法
两个非常有用的方法:any(检查数组中是否至少有一个true)和all(检查数组中是否每一个值都为true)
排序
numpy数组可以使用sort方法按位置排序:np.sort(1)。
顶层的sort方法返回的是已经排好序的数组copy,而不是对原数组按位置排序。
唯一值与其它集合逻辑
numpy包含一些针对一维ndarray的基础集合操作。常用的一个方法是np.unique(),返回的是数组中唯一值排序后形成的数组。
另一个函数:np.inld()。可以检查一个数组中的值是否在另外一个数组中,并返回一个布尔值数组。
数组的集合操作 | |
---|---|
方法 | 描述 |
unique(x) | 计算x的唯一值,并排序 |
intersectld(x,y) | 计算x和y的交集,并排序 |
unionld(x,y) | 计算x和y的并集,并排序 |
inld(x,y) | 计算x中的元素是否包含在y中,返回一个布尔值数组 |
setdiffld(x,y) | 差集,在x中但不在y中的元素 |
setxorld(x,y) | 异或集,在x或y中,但不属于x,y交集的元素 |
利用数组进行文件的输入和输出
numpy可以在硬盘中将数据以文本或二进制的形式存入硬盘或由硬盘载入。
这里只介绍numpy的内建二进制格式,因为用pandas或其他工具来载入表格或文本型的数据更常用。
np.save和np.load是高效存取硬盘数据的两大工具函数。数组在默认情况下是以未压缩的格式进行存储的,后缀名:.npy。
线性代数
线性代数,如矩阵乘法,分解,行列式等方阵数学,是所有数组类库的重要组成部分。
numpy的线性代数中的*是逐元素乘积,而不是矩阵的点乘积。numpy的点乘积由dot()函数实现
#x.dot(y)等价于np.dot(x,y)
#特殊符号@也作为中缀操作符,用于点乘矩阵操作
numpy.linalg拥有一个矩阵分解的标准函数集,以及其他常用函数,例如求逆和行列式。
常用线性代数函数列表 | |
---|---|
函数 | 描述 |
diag | 将一个方阵的对角(或非对角)作为一维数组返回,或者将一维数组转换成一个方阵,并且在对角线上有非零点 |
dot | 矩阵点乘 |
trace | 计算对角元素和 |
det | 计算矩阵的行列式 |
eig | 计算方阵的特征值和特征向量 |
inv | 计算方阵的逆矩阵 |
pinv | 计算矩阵的Moore-Penrose伪逆 |
qr | 计算QR分解 |
svd | 计算奇异值分解 |
solve | 求解x的线性系统Ax=b,其中A是方阵 |
lstsq | 计算Ax=b的最小二乘解 |
伪随机数生成
numpy.random模块填补了python内建的random模块的不足,可以高效地生成多种概率分布下的完整样本值数组。
np.random.normal(size=(4,4)),用normal来获得正态分布样本。
np.random.seed(1234),伪随机数是由具有确定性行为的算法根据随机数种子生成的,可以根据这条语句更改numpy随机数种子。
numpy.random中的部分函数列表 | |
---|---|
函数 | 描述 |
seed | 向随机数生成器传递随机状态种子 |
permutation | 返回一个序列的随机排列,或者返回一个乱序的整数范围序列 |
shuffle | 随机排列一个序列 |
rand | 从均匀分布中抽取样本 |
randint | 根据给定的由低到高的范围抽取随机整数 |
randn | 从均值0方差1的正态分布中抽取样本 |
binomial | 从为项分布中抽取样本 |
normal | 从正态(高斯)分布中抽取样本 |
beta | 从beta分布中抽取样本 |
chisquare | 从卡方分布中抽取样本 |
pandas
pandas数据结构:Series和Dataframe
Series是一种一维的数组型对象,它包含了一个值的序列(与numpy中的数据类型相似),并包含了数据标签,称为索引(index)。
我们不为数据指定索引,默认生成的索引从0~N-1.可以通过values属性和index属性分别获得Series对象的值和索引。
通常需要创建一个索引序列,用标签标示每个数据点:
从另一个角度考虑Series,可以认为他是一个长度固定且有序的字典。
利用字典生成Series对象:
当把字典传递给Series构造函数时,产生的Series索引将是排好序的字典键。
pandas记录缺失值为NaN,用 isnull和 notnull来检查缺失数据
Series对象自身和其索引都有name属性,这个特性与pandas其他重要功能集成在一起:
Dataframe
Dataframe表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔值等)。
Dataframe既有行索引,也有列索引,它可以被视为一个共享相同索引的Series的字典。Dataframe中,数据被存储在一个以上的二维块。
创建Dataframe有多种方式:利用包含等长度列表或numpy数组的字典来形成Dataframe。利用包含字典的嵌套字典赋值给dataframe。包含series的字典也可创建dataframe对象。
对与大型Dataframe,head函数只会选出头部前五行。从Dataframe中选取列是数据的视图,而不是copy。因此,对Series进行修改会映射到Dataframe中。
Dataframe的行和列都有name属性,它的values属性会将包含在Dataframe中的数据以二维ndarray的形式返回:(如果Dataframe的列是不同的dtypes,则values的dtype会自动选择合适的所有列的类型)
索引对象
pandas中的索引对象是用于存储轴标签和其他元数据(例如轴名称或标签)。在构造Series或Dataframe时,所使用的任意数组或标签序列都可以在内部转换为索引对象:(索引对象是不可变(这个不可变是指不能更改,但不包括增删查的操作)的,用户无法修改索引对象),索引对象也像一个固定大小 的集合。(索引对象可以包含重复的标签)
一些索引对象的方法和属性 | |
---|---|
方法 | 描述 |
append | 将额外的索引对象粘贴到原索引后,产生一个新的索引 |
difference | 计算两个索引的差值 |
intersection | 计算两个索引的交集 |
union | 计算两个索引的并集 |
isin | 计算表示每一个值是否在传值容器中的布尔数组 |
delete | 将位置i的元素删除,产生新的索引 |
drop | 根据传参删除指定索引值,并产生新的索引 |
insert | 在位置i插入元素,产生新的索引 |
is_monotonic | 如果索引序列递增则返回true |
is_unique | 如果索引序列唯一则返回true |
unique | 计算索引的唯一值序列 |
重建索引
pd.reindex(),该方法用于创建一个符合新索引的新对象,会将数据按照新的索引进行排序,如果某个索引值之前不存在,则会引入缺失值。可以使用columns关键字进行列索引的重建。
method可选参数:pd.reindex(range(6),method=‘ffill’)这个方法会在重建索引时插值,插值方式为前项填充。
轴向上删除条目:drop(),删除轴0上的数据:drop([0,1]);删除轴1上的数据:drop(‘column1’,axis=1)。drop会修改Series和Dataframe的尺寸和形状,直接对原对象进行操作而不返回新对象。
使用loc和iloc选择数据
针对Dataframe在行上的标签索引,允许使用轴标签(loc)和整数标签(iloc)
整数索引
这里出错,是因为难以推断用户所需的索引方式(标签索引和位置索引)
而对于非整数的索引,不会有潜在的歧义。所以为了保证一致性,如果有包含整数的轴索引,数据选择时始终使用标签索引
两个pandas对象相加,没有并集的标签位置上,对象内部数据对齐会产生缺失值。
使用填充值:将fill_value作为参数传入
灵活的算术方法 | |
---|---|
函数 | 描述 |
add, radd | 加法 |
sub,rsub | 减法 |
div,rdiv | 除法 |
floordiv,rfloordiv | 整除 |
mul,rmul | 乘法 |
pow,rpow | 幂次方 |
函数的应用和映射
numpy的通用函数(逐元素操作)对pandas对象也有效。
另一个常用的操作是将函数应用到一行或者一列的一维数组上,Dataframe的apply方法可以实现这个功能,默认应用于列上,传递参数axis='columns’给apply函数,函数将会在每行被调用一次。
逐元素的python函数也可以使用,可以使用applymap方法。
排序和排名
根据需求对数据集进行排序:
- sort_index()、sort_index(axis=1)。
- 数据默认升序排序,也可按照降序排序sort_index(ascending=False)。
- 按照值进行排序,使用sort_values方法,默认情况下,所有缺失值会被排列在尾部。
- 对Dataframe进行排序,可以使用一列或多列作为排序键,使用可选参数by:sort_values(by=‘a’)。
- 对多列进行排序,传递列名的列表sort_values(by=[‘a’,‘b’])。
- rank()方法是实现排名的方法,默认情况下,rank通过将平均排名分配到每个组来打破平级关系:
- rank中可以设置参数来改变排名方法,如使用观察顺序rank(method=‘first’),使用参数ascending=‘False’指定降序排序。
含有重复标签的轴索引 - 判断索引标签是否唯一:df.index.is_unique()
pandas对象的统计方法
归约方法:
归约方法可选参数 | |
---|---|
方法 | 描述 |
axis | 归约轴,0为行,1为列 |
skipna | 排除缺失值,默认为True |
level | 如果轴是多层索引的(MultiIndex),该参数可以缩减分级层数 |
累积型方法
既不是归约方法,也不是累积型方法:如describe(),它一次性产生多个汇总统计。
一些汇总统计,比如相关性(corr)和协方差(cov),是由多个参数计算出来的。
pandas读取和写入数据集及文件格式
- 文本格式数据的读写
将表格型的数据读取为dataframe是pandas的重要特征
pandas的解析函数 | |
---|---|
函数 | 描述 |
read_csv | 从文件、URL或文件型对象都区分割好的数据,逗号是默认分隔符 |
read_table | 从文件、URL或文件型对象都区分割好的数据,制表符(‘\t’)是默认分隔符 |
read_fwf | 从特定宽度格式的文件读取数据 |
read_clipboard | read_table的剪贴板版本,再将表格从web页面转化成数据时有用 |
read_execl | 从execl的xls文件中读取表格数据 |
read_hdf | 读取用pandas储存的HDFS文件 |
read_html | 从HTML文件中读取所有表格数据 |
read_json | 从json字符串中读取数据 |
read_msgpack | 读取massagepack二进制格式的pandas数据 |
read_pickle | 读取以python pickle格式存储的任意对象 |
read_sas | 读取储存在SAS系统中定制存储格式的SAS数据集 |
read_sql | 将SQL查询结果读取为为Dataframe格式 |
read_stata | 读取state格式的数据集 |
read_feather | 读取feather 二进制格式 |
这些函数的可选参数主要有下几种类型:
- 索引
- 类型推断和类型转换
- 日期时间解析
- 迭代
- 未清洗数据问题:跳过行、页脚、注释及其他次要数据。
read_csv()读取没有表头的csv文件,可指定header=None来允许pandas自动分配默认列名,也可以使用names=[‘a’,‘b’,‘c’]来自己指定列名。指定某列为行索引,可将列名传递给参数index_col:
skiprows参数可以用来指定跳过指定行。
na_values选项可以传入一个列表或一组字符串来处理缺失值:
一些read_csv/read_table函数参数
- 分块读入文本文件
当处理的数据文件非常大,可能需要读入文件的一个小片段或者按小块遍历文件。
用nrows来指定读取数据的行数。pd.read_csv(“test.csv”,nrows=5)这样指定读取前5行的内容。
为了分块读入文件,可以指定chunksize作为每一块的行数 pd.read_csv(“test.csv”,chunksize=1000),返回值为一个TextParser对象,该对象允许根据chunksize遍历文件。 - 将数据写入文本格式
调用Dataframe的to_csv方法,我们可以将数据导出为逗号分割的文件。
文件中有空字符,可以使用其他标识值对缺失值进行标注:data.to_csv(sys.stout(控制台中打印的文本结果),na_rep=‘NULL’)。
可以禁止行列标签的写入:data.to_csv(sys.stout,index=False,header=False)
Series也有to_csv的方法。 - 使用分隔格式
绝大多数表格数据都可以使用函数pandas.read_table从硬盘中读取。在一些情况下,一些手动操作是必不可少的的(接收一个带有一行或多行错误的文件并不少见),csv文件有多种不同风格,可以根据csv.Dialect定义一个简单的子类:
csv方言选项 | |
---|---|
参数 | 描述 |
delimiter | 一个用于分割字段的字符,默认是‘,’ |
lineterminator | 行终止符,默认是‘\r\n’,读取器都会忽略终止符并识别跨平台行终止符 |
quoteachar | 用在含特殊字符字段的引号,默认是‘ " ’ |
quoting | 引用惯例,选项包括csv.QUOTE_ALL(引用所有字段),csv.QUOTE_MINIMAL(只使用特殊字符,如分隔符),csv.QUOTE_NONNUMERIC和csv.QUOTE_NONE(不引用) |
skipinitialspace | 忽略每个分隔符后的空白,默认是你False |
doublequote | 如何处理字段内部的引号,如果为True,则是双引号 |
escapechar | 当引用设置为csv.QUOTE_NONE时用于转义分隔符的字符串,默认是禁用的 |
对于更复杂类型的csv模块,可以使用re.split()进行拆分。
- JSON数据
json数据已经成为Web浏览器和其他应用间通过HTTP请求发送数据的标准格式,是一种比CSV等表格形式更为自由的数据形式。有几个python库用于读写json数据,其中json是内置在python标准库中的。将json字符串转化为python形式时,使用json.loads()方法:
loads()函数传的是字符串,而load()传的是文件对象,使用loads()时需要先读取文件再使用。
json.dumps()可以将python对象在转换回json。
pandas中读取json的方法:pd.read_json(),该方法可以自动的将json数据集按照指定次序转化为Series或Dataframe。如果需要将pandas中的数据导出为json,一种办法是对Series和Dataframe使用to_json()方法:data.to_json(orient=‘records’)。 - XML和HTML:网络抓取
python拥有很多可以对HTML和XML进行读取,写入数据的库:如lxml、Beautiful Soup、html5lib。其中lxml是相对更快的库,但其他库可以更好的处理异常xml、HTML文件。
pd,rasd_html函数有很多选项,他会搜索并尝试解析所有包含在中的表格型数据,返回的结果是Dataframe对象的列表。(pandas内建函数read_html使用附加库lxml)。 - 使用lxml.objectify解析XML
xml是另一种常用的结构化数据格式,它使用元数据支持分层、嵌套数据。
xml和HTML结构类似,但xml更通用。
使用lxml从更为通用的xml格式解析数据:使用lxml.objectify,并使用getroot()来获取对xml根节点的引用。
二进制格式
使用python内建的pickle序列化模块进行二进制格式操作是存储数据(也称序列化)最高效。最方便的方式之一。pandas对象拥有一个to_pickle方法可以将数据以pickle格式写入磁盘,使用pd.read_pickle()读取文件中的pickle对象。
使用HDF5格式
用于储存大量的科学数组数据,它以C库的形式提供,并且它有许多语言的接口。
每个HDF5文件可以储存多个数据集并支持元数据。适于处理不适合储存在内存的超大型数据,可以使你高效的读写大型数组的一小块。pandas提供了一个高端的接口,可以简化Series和Dataframe的储存。HDFStore类像字典一样工作并处理低级别细节。
HDFStore支持两种存储模型,‘fixed’和‘table’。后者速度更慢,但支持一种特殊语法的查询操作。
HDF5并不是数据库,而是一种适合一次写入多次读取的数据集。尽管数据可以在任何时间添加到文件中,但如果多个写入者持续写入,文件可能会损坏。
读取Microsoft Execl文件:pd.ExeclFile()——>pd.read_execl()。
将pandas对象写入到execl文件中,必须先生成一个ExeclWriter,然后使用pandas对象的to_execl()方法将数据写入。
与Web API交互
很多网站都有公开API,通过json或其他方式提供数据服务。有多种方式可以利用python来访问API,简单易用的方式是requests包(爬虫)
与数据库交互
基于关系型数据库储存的数据,将SQL中数据读取为Dataframe:使用python内建的sqlite3驱动来生成一个SQLite数据库。
SQLAlchemy项目是一个流行的python SQL工具包,抽象去除了SQL数据库之间的许多常见差异。pandas有一个read_sql函数允许从通用的SQLAlchemy连接中轻松读取数据。
数据清洗与准备
处理缺失值
对于数值型数据,pandas使用浮点值NaN来表示缺失值,让缺失值成为NA(not available,不可用)。在统计学应用中,NA可以是不存在的数据或存在但不可观察的数据。当清洗数据用于分析时,对缺失值进行分析以确定数据收集问题或数据丢失导致的数据偏差通常很重要。python内建的None值在数组中也被当做NA处理。
NA处理方法 | |
---|---|
函数名 | 描述 |
dropna | 根据每个标签的值是否是缺失数据来筛选轴标签,并根据允许丢失的数据量来确定阈值 |
fillna | 用某些值填充缺失数据或使用插值方法(ffill或bfill) |
isnull | 返回表明哪些值是缺失的 |
notnull | isnull的反函数 |
过滤缺失值
在Series中,可以选择dropna和isnull,在Dataframe中dropna默认会删除所有包含缺失值的行
dropna(how=‘all’):删除所有值均为NA的行
dropna(axis=1):删除列
dropna(thresh=2):这一行除去NA值,剩余数值的数量大于等于n,便显示这一行
补全缺失值
有时可能要以多种方式补全缺失值,而不是删除它。
大多数情况下,主要使用fillna方法来补全缺失值。调用fillna时,可以使用一个常数来替代缺失值。
fillna(0),用0补全缺失值
fillna({1:0,2:1}):补缺缺失值时调用字典,可以为不同列设定不同的填充值。
fillna(inplace=True):fillna返回的是一个新对象,也可以修改已经存在的对象。
fillna(method=‘ffill’):前项填充。
fillna(method=‘ffill’,limit=2):前项填充,但同一前项最多只能填充两行。
fillna(data.mean()):可以选择用中位数,或均值等更有意义的数来填充缺失值。
fillna函数参数 | |
---|---|
参数 | 描述 |
value | 标量型或字典型对象填充缺失值 |
method | 插值方法,如果没有其他参数,默认是ffill |
axis | 需要填充的轴,默认axis=0 |
inplace | 修改被调用的对象,而不是生成一个备份 |
limit | 用于前项或后向填充时最大的填充范围 |
数据转换
-
删除重复值
Dataframe的duplicated方法返回的是一个布尔值Series,这个Series反映的是每一行是否存在重复值(与之前出现过的行所有的值相同时,返回一个true,否则返回False)。
drop_duplicated()返回的是Dataframe,内容是duplicated返回数组中为False的部分。
drop_duplicated()和duplicated()默认都是队列进行操作。
drop_duplicated([‘k1’]):基于k1列去除重复值。
drop_duplicated()和duplicated()默认都是保留第一个观测到的值。
drop_duplicated(kee=‘last’)将会返回重复值中的最后一个。 -
使用函数或映射进行数据转换
Series的map方法接受一个函数或一个包含映射关系的字典型对象。
data[‘food’].map(animals(一个字典,里面包含food和animals的映射关系))。
data[‘food’].map(lambda x:animals[x.lower()]):map里面是lambda表达式。
使用map是一种可以便捷执行按元素转换及其他清洗相关操作的方法,用来修改一个对象中的子集的值。 -
替代值
使用fillna填充缺失值是通用值替换的特殊案例,replace提供了更为简单灵活的实现。
replace(-999,np.nan):用NaN来代替-999。
replace([-999,-1000],[1,0]):用1代替-999,用0代替-1000。
replace({-999:1,-1000:0}):意义同上。 -
重命名轴索引
可以通过函数或某种形式的映射对轴标签进行转换,生成新的且带有不同标签的对象。
轴索引也有map方法:data.index.map(函数或映射关系),这样的方法生成了一个新的对象。
如果想要创建数据集后的版本,并且不修改原有的数据集,一个有用的方法是rename:
如:data.rename(index=str.title,columns=str.upper)。
rename可以结合字典型对象使用,data.rename(index={‘x’:y}) -
离散化和分箱
一组人群数据,需要将他们分组放到离散的年龄框中:
pandas对象是一个特殊的Categories对象:
可以用right=False来改变哪一边是闭的:
可以通过labels选项来传递一个列表或数组来传入自定义的箱名:
如果传给cut整个箱来代替显示的箱边,pandas将根据数值中的最大值和最小值计算出等长的箱。
上面指定生成4个箱,其中precision=2将十进制的精度限制在两位。
qcut是一个与分箱密切相关的函数,它基于样本分位数进行分箱。取决于数据的分布,使用cut通常不会使每个箱的数据量相同。由于qcut使用样本的分位数,可以根据这些值的频率来选择箱子均匀分隔:
这些离散化的函数对于分位数和分组分析特别有用。 -
检测和过滤异常值
过滤或转换异常值在很大程度上是应用数组操作的事情。
-
置换和随机抽样
使用numpy.random.permutation对Dataframe中的Series或行进行置换是非常方便的,在调用permutation时根据你想要的轴长度可以产生一个表示新顺序的整数数组。
sample()方法可以选出原数据集的一个随机子集:
要生成一个带有替代值的样本(允许有重复选项),将replace=True(replace方法会创建一个新的数组,所以不会影响原始数据,如果希望直接改变原始数据的话,在括号内传入inplace = True就可以了.)传入sample方法:
-
计算指标/虚拟变量
将分类变量转化为“虚拟”或“指标”矩阵是另一种用于统计建模或机器学习的转换操作。如果Dataframe中的一列有k个不同的值,则可以衍生一个k列的值为1或0的矩阵或Dataframe。pandas有一个get_dummies函数用于实现该功能:
get_dummies()前缀参数使用:
df[‘rag’]是Series对象,而Series对象没有join函数,可以将其转化为Dataframe对象,韩后连接上指标矩阵:
如果Dataframe中的一行属于多个类别,如电影,可以同属惊悚、悬疑等类型,这种情况略为复杂,建立指标矩阵要自行处理逻辑。(如P665页的内容)
对于更大的数据,使用多种成员构建指标变量的方法并不快速。更好的方法是写一个将数据写为numpy数组的底层函数,然后将结果封装进Dataframe里。
将get_dummies与cut等离散化函数结合使用是统计应用的一个有效办法:
字符串操作
pandas允许将字符串和正则表达式应用在数据数组上。 -
字符串对象方法
内建字符串方法 | |
---|---|
split | 指定分隔符 |
strip,rstrip,lstrip | 用于清除字符串前/后的空格 |
str.join() | 将str作为分隔符,拼接字符串 |
str1 in str2 | 检测str1是否在str2中 |
str.index(x) | 获取子字符串x的索引,没找到会抛出异常 |
str.find(x) | 获取子字符串x的索引,没找到会返回-1 |
str.count(x) | 子字符串x在字符串str中出现的次数 |
replace(x,y) | 用字符串y代替x |
endwith | 如果字符串以后缀结尾返回True |
startwith | 如果字符串以前缀开始返回True |
rfind(x) | 返回x在字符串中最后一个出现位置,没找到抛出-1 |
lower | 将字符串全部转化为小写 |
upper | 将字符串全部转化为大写 |
casefold | 将字符串转化为小写,并将任何特定区域的字符组合转换为常见的可比较形式 |
ljust,rjust | 左对齐或右对齐,用空格填充字符串的相反侧以返回具有最小宽度的字符串 |
- 正则表达式
单个表达是被称为regex,是根据正则表达式语言形成的字符串。
python内建模块re是正则表达式库:正则表达式
- pandas中的向量化字符串函数
包含字符串列的Dataframe对象,如果其中某一项是缺失值,常用的面向字符串的映射关系或正则表达式会失效 ,Series有面向数组的方法用于跳过NA值的字符串操作,这些方法通过Series的str属性调用:
data.str.contains(‘xy’):检查字符串中是否含有xy,跳过空值。
有多种方法可以进行向量化的元素检索。可以使用str.get[索引]或str[索引]属性内部检索。
部分向量化字符串方法列表 | |
---|---|
cat | 根据可选的分隔符按元素粘合字符串 |
contains | 返回是否含有某个模式或正则表达式的布尔值数组 |
count | 模式出现的次数计数 |
extract | 使用正则表达式从字符串Series对象每一行中抽取一个或多个子字符串,返回每行的字符串组形成一列的Dataframe |
endwith | 等价于对每个字符串使用endwith() |
数据规整:连接、联合和重排列
分层索引(部分索引)
是pandas中重要特性,允许在一个轴上有两个或两个以上索引层级。
分级索引在重排列数据和数据透视表中等分组操作中扮演了重要的角色。
在Dataframe中,每个轴都可以拥有分层索引,分层的层级可以有名称,这些名称会在控制台中显示。
-
重排序和层级排序
df.swaplevel(‘key1’,‘key2’):接收两个层级名称(或序号),返回一个进行了层及变更的新对象(数据不变)。
df.sort_index(level=1):只能在单一层级上进行排序,level=1是按照列上的第二个层级索引进行排序(下标从0开始)。如果索引按照字典顺序从最外层开始排序,调用sort_index(level=0)或sort_index可以实现这样的效果。 -
按层级进行汇总统计
通过level选项可以指定在某个特定的轴上进行聚合:
上面的例子使用了pandas的groupby机制。 -
使用Dataframe的列进行索引
set_index():将Dataframe对象的列转化为行索引,并默认将这些列从Dataframe中移除,可以使用drop=False选项将这些列保留。
reset_index():是set_index()的反操作,分层索引的索引层级会被移动到列中。
联合与合并数据集
-
数据库风格的Dataframe连接
合并或连接操作通过一个或多个键来联合数据集,pandas中的merge函数主要用于将各种join操作算法运用在数据上:
pd.merge(df1,df2,on=‘key’):将df1对象和df2按照键值(列名)key来进行连接。
pd.merge(df1,df2,left_on=‘key1’,right_on=‘key2’):如果每个对象的列名不同,可以像这样分别为他们指定列名。
默认情况下,merge操作做的是内连接,也可以指定进行left、right、outer(外连接)。(how=‘outer’)。
多对多连接是行的笛卡尔积。
使用多个键进行合并时,传入一个列名列表:pd.merge(df1,df2,on=[‘key1’,‘key2’]),当进行列-列连接时,传递的Dataframe索引对象会被丢弃。
合并操作最后一个要考虑的问题是如何处理重叠的列名。merge有一个suffixes后缀选项,用于在左右两边Dataframe对象的重叠列名后指定需要添加的字符串:pd.merge(df1,df2,on=‘key1’,suffixes=[’_left’,’_right’])。
-
根据索引合并
pd.merge(df1,df2,left_on=‘key’,right_index=True):left_index=True和right_index=True表示使用left、right的数据集的行索引作为连接键(可以只传一个,如上例,也可以两个都传)。
在多层索引数据的情况下,在索引上的连接是一个隐式的多键合并。
Dataframe有一个方便的join实例方法,用于按照索引合并:df1.join(df2,how=‘ouetr’)。该方法也可以用于多个索引相同或相似但没有重叠列的Dataframe对象,join方法进行左连接,完全保留左边Dataframe的行索引。对于一些简单的索引-索引合并,可以向join方法传入一个Dataframe列表(列表中包含多个Dataframe对象)。 -
沿轴向连接
numpy的concatenate函数可以在numpy数组上实现数据拼接:
pandas的concat函数:默认情况下,concat方法是沿axis=0轴生效的,join选项可以指定连接方式(outer或inner),默认是合集(outer),join_axes选项可以指定连接后的索引顺序(join_axes=[‘1’,‘3’,‘4’,‘2’])。
拼接在一起各部分在结果集中无法区分,可以使用keys选项来创建多层索引:keys=[‘one’,‘two’,‘three’]。也可以传入字典来代替列表:pd.concat({‘key1’:df1,‘key2’:df2},axis=1)。leels选项用来指定多层索引的层级。 -
联合重叠数据
还有一个数据联合场景,既不是合并也不是连接,有两个数据集,这两个数据集的索引全部或部分重叠。
numpy的where函数,这个函数可以进行面向数组的if-else操作:np.where(pd.isnull(a),b,a),等价于if pd.isnull a=b else a。
Series有一个combine_first方法:b.combine_first(a),根据传入的对象来修补调用函数的对象,在Dataframe中,会逐列进行这种操作。
重塑和透视(重排列表格型数据的操作被称为重塑或透视)
- 使用多层索引进行重排序
两个基础操作:statck(堆叠)、unstatck(拆堆)。
列名透视为行索引(变为最底层):
默认按最列高层级(从0开始)来拆堆(按哪一层进行拆堆,这一层行索引就被透视为列,其他索引位置不变):
默认情况下,堆叠会过滤出缺失值,因此堆叠操作是可逆的。
在Dataframe中进行拆堆时,被拆对的层级会变成结果中最低的层级(最里面的一层) - 将长透视为宽
pivot方法等价于使用set_index()创建分层索引之后调用unstack。 - 将宽透视为长
在Dataframe中,pivot方法(一列变换为新的Dataframe中的多列)的反操作是pandas.melt(),他将多列合并成一列,产生过一个新的Dataframe,其长度比输入的更长,使用melt时,必须指明哪些列是分组指标(如果有的话,可以理解为保留哪一个列索引):
pd.melt()方法也可指定子集。
绘图与可视化
要关注matplotlib和以它为基础的库。
在使用 Notebook 环境绘图时,需要先运行 Jupyter Notebook 的魔术命令 %matplotlib inline。这条命令的作用是将 Matplotlib 绘制的图形嵌入在当前页面中。而在桌面环境中绘图时,不需要添加此命令,而是在全部绘图代码之后追加 plt.show()。
导入惯例:import matplotlib.pyplot as plt
matplotlib所绘制的图位于图片(Figure)对象中。
matplotlib官方文档
使用子图网格创建图片:plt.subplots(),创建一个新的图片,然后返回包含了已生成的子图对像的numpy数组:
这是非常有用的,因为数组axes可以像二维数组那样方便的进行索引,如axes[0,1]。也可以通过使用sharex和sharey来表明子图分别拥有相同的x轴和y轴。
pyplot.subplots选项 | |
---|---|
nrows | 子图的行数 |
ncols | 子图的列数 |
sharex | 所有子图使用相同的x轴 |
sharey | 所有子图使用相同的y轴 |
subplot_kw | 传入add_subplot的关键字参数字典,用于生成子图 |
**fig_kw | 在生成图片时使用额外的关键字参数,例如plt.subplots(2,2,figsize=(8,6)) |
- 颜色、标记和线类型
plot中个参数选项 | |
---|---|
linestyle | 图线类型 |
color | 线颜色 |
marker | 线标记 |
label | 线的描述信息(一个图上有多条线,线名来区别不同的线) |
在Jupyter中使用plt.plot?命令可以查看plot的帮助文档。
-
刻度、标签和图例
对于大多数图表的修饰工作,有两种主要方式:使用程序性的pyplot接口和更多面向对象的原生matplotlib API。pyplot接口设计为交互式使用,包含了像xlim、xticks和xticklabels等方法。这些方法分别控制了绘图范围、刻度位置以及刻度标签。 -
设置标题、轴标签、刻度和刻度标签
要改变x轴的刻度,最简单的方式是使用set_xticks和set_xticklabels。set_xticks表示在数据范围内设定刻度的位置,默认情况下,这些刻度也有标签,但是我们可以使用set_xticklabels为标签赋值:
set_xlabel用于设置x轴的名称,set_title会给子图设置一个标题。 -
添加图例
图例是用来区分绘图元素的另一个重要内容,有多种方式可以添加图例。
一个简单的方式是在添加每个图表时传递label参数,添加了label选项之后,可以调用ax.legend()或plt.legend()自动生成图例:
legend方法有位置参数loc,loc用来指定在图表的那个位置放置图例:best会自动挑选最合适的位置。 -
注释与子图加工
可以使用text(添加文本)、arrow和annote(两者结合添加箭头和文本)方法来添加注释和文本:
matplotlib含有多种常见图形的对象,这些对象的引用是patches。一些图形,比如矩形和圆形,可以在pyplot中找到,但图形的全集位于patches。想要在图表中添加图形,要生成patch(补丁)对象shp,并调用ax.add_patch(shp)将它加入到子图中:
-
将图片保存到文件
可以使用plt.savefig将活动图片保存到文件,关于图片的存储,有几个重要选项:.dpi(控制每英寸点数的分辨率)、bbox_inches(可以修剪实际图形的空白):
savefig可以将图片写入所有文件行对象中。
figure.savefig选项 | |
---|---|
参数 | 描述 |
fname | 包含文件路径或python文件型对象的字符串,图片格式是从扩展名推断出来的 |
dpi | 每英寸点数的分辨率,默认值是100 |
facecolor,edgecolor | 子图之外的图形背景颜色,默认是w(白色) |
format | 文件格式 |
bbox_inches | 要保存图片的范围,如果传递‘tight’,将会除掉图片周围空白部分 |
使用pandas和seaborn绘图
matplotlib是一个相当底层的工具。在pandas中,可能有多个数据列,并且带有行和列标签。pandas有很多内建方法可以简化从Dataframe和Series对象生成可视化的过程。另一个库是seaborn,导入seaborn会修改默认的matplotlib配色方案和绘图样式,会提高图标的可读性和美观性。
以下先介绍pandas对象的内置绘图方法:
- 折线图
Series和Dataframe都有一个plot属性,用于绘制基本图形,默认情况下,plot()绘制的是折线图(plot等价于plot.line())。Dataframe方法在同一个子图中将每一列绘制成不同的折线,并自动生成图例。 - 柱状图
plot.bar()和plot.barh()可以分别绘制垂直和水平方向的柱状图。在绘制柱状图时,Series或Dataframe的索引将会被用作x轴刻度(bar)或y轴刻度(barh)。在Dataframe中,柱状图将每一行中的值分组到并排柱中的一组:
对于在绘图前需要聚合和汇总的数据,使用seaborn包会使工作更为简单:
- 直方图和密度图
直方图是一种条形图,用于给出值频率的离散显示。数据点被分成离散的,均匀间隔的箱,并且绘制每个箱中数据点的数量。密度图是一种与直方图相关的图表类型,它计算观测数据可能出现的连续概率分布估计。通常的做法是将这种分布近似为“内核”的混合,就是类似于正态分布那样简单的分布。因此,密度图也被称为“内核密度估计图”(KDE)。plot.kde使用传统法定混合法绘制密度图。
sns.distplot()方法可以绘制直方图和连续密度估计。 - 散点图或点图
散点图或点图可以用于检验两个一维数据序列之间的关系:
在探索性数据分析中,能够查看一组变量中所有散点图是有帮助的,这被称为成对图或散点图矩阵,从头开始绘制这样一个图是由工作量的,seaborn有一个方便的函数pairplot函数,它支持在对角线上放置每个变量的直方图或密度估计值:
- 分面网格和分类数据
如果数据集有额外的分组,使用分面网格是利用多种分组变量对数据进行可视化的方式seaborn拥有一个有效的内建函数factorplot,它可以简化多种分面绘图:
factorplot支持其它可能有用的图类型,具体取决于要显示的内容。也可以使用更为通用的seaborn.FacetGrid类创建自己的分面网格图。
数据聚合与分组操作
pandas提供了一个灵活的groupby接口,允许对数据集进行切片、切块和总结。
GroupBy机制(实际上就是分组)
一个简单的分组聚合样例:
分组键可以是多种形式的,并且键不一定是完全相同的类型:
与需要分组的轴向长度一致的值列表或值数组、Dataframe列名的值、可以将分组轴向上的值和分组名称相匹配的字典或Series、可以在轴索引和索引中的单个标签上调用的函数。
本次试验所用数据集:
如果想要根据key1标签计算data1列的均值,有以下方法可以实现:
1.访问data1列并使用key1列调用groupby方法:
数据根据分组键进行了聚合,产生一个新的Series,这个Series使用key1列的唯一值作为索引,列名成为索引名。
如果我们将多个分组依据作为列表传入,则会得到下面的结果:
以上例子中,分组键都是Series,分组键也可以是正确长度的任何数组。
- 遍历各分组
GroupBy对象支持迭代,会生成一个包含组名和数据块的二维元组序列:
计算出数据块的字典:
以上是横向分组,也可以进行列项分组:
- 选择一列或所有列子集
将从Dataframe创建的GroupBy对象用列名或列名称数组进行索引时,会产生用于聚合列子集的效果:
groupby操作,如果传递的是列表或者数组,则此索引操作返回的对象是Dataframe。如果传入的是列索引,则返回的对象是Series。 - 使用字典和Series分组
分组信息可能会以非数组形式存在,以下实验使用的是下面的数据集:
假设我们有各列的分组对应关系,那么就可以根据字典的映射关系来进行groupby操作,并进行各种运算:
Series也有和字典相同的功能,可以理解为Series本身也就是行索引和值的映射关系。 - 使用函数分组
与使用字典或Series分组相比,使用python函数是定义分组的一种更通用的方式。作为分组键传递的函数将仅调用每个索引值一次,并返回将用作分组操作的值:
- 根据索引层级分组
分层索引数据集可以在轴索引的某个层级上进行聚合,根据层级分组时,将层级数值或层级名称传递给level关键字:df.groupby(level=‘key’,axis=1)。
数据聚合
聚合是所有根据数组产生标量值的数据转换过程,聚合操作包括:mean、count、min和sum等。
用户可以自定义聚合函数,要使用自己的聚合函数,需要将函数传递给aggregate或agg方法:
- 逐列与多函数应用
以上例子是对Series或Dataframe对象的所有列进行聚合(使用aggregate和自定义函数,或者使用sum、std等方法)。我们也可以同时使用多个函数对各列进行聚合,如果传递的是函数名列表,将会得到一个列名是这些函数名的Dataframe
传递有自定义函数的元组列表,如果传递的是(name,function)元组的列表,每个元组的第一个元素将作为Dataframe的列名:
如果想要实现将不同的函数应用到一个或多个列上,要实现这个功能,需要将含有列名与函数对应关系的字典传递给agg:
只有多个函数应用于至少一个列时,生成的Dataframe才有分层索引。 - 返回不含有行索引的聚合数据
在前面所有的例子中,聚合函数局返回时都是带有行索引的,有时行索引是分层的。由于不是所有情况下都需要索引,所以在大多数情况下可以通过向groupby传递as_index=False来禁用分组键作为索引的行为(也可以在结果上调用reset_index也可以达到同样的效果):
应用:通用拆分——应用——联合
GroupBy分组之后应用apply,apply会将传递给其的函数应用到每一个分组上,之后尝试将每一块拼接到一起,其中的过程是:先按照key进行groupby分组,调用apply(function),function在Dataframe的每一个分组被调用,之后使用pandas.concat将函数结果粘贴在一起,并使用分组明作为各组的标签,因此结果中包含一个分层索引,该分层索引的内部层级包含原Dataframe的索引值。
- 压缩分组键
在之前的例子中,生成的Dataframe对象包含分层索引(分组键和原始对象的索引),可以通过传递group_keys=False来禁用这个功能。 - 分位数与桶分析
将cut和qcut函数用于将数据按照自己选择的箱位或样本分位数进行分桶,与groupby方法一起使用这些函数可以对数据更方便地进行分桶或分位分析:
为了根据样本分位数计算出数量相等的桶,则需要使用qcut(传递labels=False来获得分位数数值)。 - 使用指定分组值填充缺失值
在清除缺失值时,可以使用修正值或来自于其他数据的值来填充NA值,使用选项fillna,假设需要填充值按组变化:
- 随机采样与排列
要从大的数据集中抽取随机样本用于蒙特卡罗模拟目的或某些其他应用程序,有很多办法来执行“抽取”,如可以使用Series的sample方法:
# 构造一副扑克牌
# 红桃,黑桃,梅花,方块
suits = ['H', 'S', 'C', 'D']
card_val = (list(range(1, 11)) + [10] * 3) * 4
base_names = ['A'] + list(range(2, 11)) + ['J', 'K', 'Q']
cards = []
for suit in ['H', 'S', 'C', 'D']:
cards.extend(str(num) + suit for num in base_names)
deck = pd.Series(card_val, index=cards)
#从这副牌中随机拿出5张
def draw(deck,n=5):
return deck.sample(n)
draw(deck)
#从每个花色中随机抽取两张牌(花色是索引的第二个字符)
func=lambda x:x[-1]
deck.groupby(func).apply(draw,n=2)
- 分组加权平均和相关性
本次实验使用下面的数据集:
数据透视表与交叉表
数据透视表是电子表格程序中和其他数据分析软件中常见的数据汇总工具。他根据一个或多个键聚合一张表的数据,将数据在矩形格式中排列。python中的pandas透视表是通过groupby工具以及使用分层索引的重塑操作实现的。Dataframe拥有一个pivot_table方法,并且含有一个顶层的pandas.povit_table函数。除了为groupby提供一个方便的接口,pivot_table还可以添加部分总计,也称为边距。
想要计算某个数据集在行方向上按key1和key2排列的分组平均值,可以用pivot_table方法:df.pivot_table(index=[‘key1’,‘key2’])。这个功能也可以直接使groupby实现。
pivot_table还可以实现更复杂的功能:如书上的tips数据集,在tip_pct和size上聚合,并根据time分组,将somker放入列中,将day放入行中:df.pivot_table([‘tip_pct’,‘size’],index=[‘time’,‘day’],columns=somker)。(可以看出:pivot_table的参数顺序是(datas,index,columns))。
可以通过传递margins=True来扩充这个表包含的部分总计,结果是添加ALL行和列标签,对行和列进行相应的汇总:
要使用不同的聚合函数时,将函数传递给aggfunc选项:df.pivot_table(‘tip_pct’, index=[‘time’, ‘smoker’], columns=‘day’, aggfunc=len, margins=True)。len聚合函数会给出一张分组大小的交叉表(计数或出现频率)。
- 交叉表:crosstab
交叉表是数据透视表的一个特殊情况,计算的是分组中的频率。下面实验中使用数据集:
时间序列
在多个时间点观测或测量数据形成了时间序列。
日期及时间数据的类型及工具
python的标准库包含了日期和时间数据类型。datetime、time和calendar模块时开始处理时间数据的主要内容,导入模式为:from datetime import datetime
datetime模块中的类型 | |
---|---|
date | 使用公历日历储存的日历日期(年,月,日) |
time | 将时间储存为小时,分钟,秒和微秒 |
datetime | 储存日期和时间 |
timedelta | 表示两个datetime之间的差(日,秒和微秒) |
tzinfo | 用于储存时区信息的基本类型 |
- 字符串与datetime互相转换
datetime格式说明 | |
---|---|
类型 | 描述 |
%Y | 四位的年份 |
%y | 两位的年份 |
%m | 两位的月份[01,12] |
%d | 两位的日期号[01,31] |
%H | 小时,24小时制[00,23] |
%I | 小时,12小时制[01,12] |
%M | 两位的分钟,[00,59] |
%S | 秒,[00,61] |
%w | 星期日期[0,6] |
%U | 一年中的星期数[00,53],以星期天为每周的第一天,一年中第一个星期天之前的日期作为第0周 |
%W | 一年中的星期数[00,53],以星期一为每周的第一天,一年中第一个星期一之前的日期作为第0周 |
%z | 格式为+HHMM或-HHMM的UTC时区偏移,如果没有时区则为空 |
%F | %Y-%m-%d的简写 |
%D | %m/%d/%y的简写 |
datetime.strftime是在已知格式的情况下转换日期的方法。对于通用日期格式,可以使用第三方dateutil包的parser.parse方法(这个包在安装pandas时已经自动安装):from dateutil.parser import parse。dateutil能够解析大多数人可以理解的日期表示:如parse(‘Jan 31,1997 10:45 PM’)。
pandas主要是面向处理日期数组的,无论是用作轴索引还是用作Dataframe中的列。to_datetime方法可以转化很多不同的日期表示格式。
NaT(not a time)是pandas中时间戳数据是null值。
时间序列基础
pandas中时间序列:
在Series中不同对象的时间序列之间的算术运算在日期上自动对齐。.
pandas使用numpy的datetime64数据类型在纳秒级的分辨率下储存时间戳,DatetimeIndex中的标量值是pandas的Timestamp对象:
所有使用datetime对象的地方都可以使用timestamp,timestamp可以储存频率信息并了解如何进行时区转换和其他类型操作。
- 索引、选择、子集
当基于标签进行索引和选择和其他Series对象是类似的:
对于长的时间序列,可以通过传递年份或年份和月份进行切片:
使用datetime对象也可以进行切片(也可以使用不包含在时间序列里的时间戳进行切片)(这个操作类似于对numpy对象进行切片,是没有复制数据的,因此对切片数据进行修改也会修改原数据):
等价的实例方法为truncate,它可以在两个日期间对Series进行切片:ts.truncate(after=‘1/1/2011’)。
以上这些操作也适用于Dataframe对象。
- 含有重复索引的时间序列
在某些应用中,可能会有多个数据观察值落在特定的时间戳上,通过检查is_unique属性,可以看出索引是否是唯一的。如果想要聚合含有重复时间戳的数据,可以使用groupby并传递level=0。
日期范围、频率和移位
pandas的时间序列频率并不是固定的,pandas拥有一整套标准的时间序列频率和工具用于重采样、推断频率以及生成固定频率的数据范围,如可以通过调用resample方法将样本时间序列转换为固定的每日频率数据:
在频率间转换,又称为重采样,是一个很大的话题。
- 生成日期范围
pandas.date_range适用于根据特定频率生成指定长度的DatetimeIndex。默认情况下,date_range生成的是每日的时间戳。
pd.date_range(start=None, end=None, periods=None, freq='D', tz=None, normalize=False, name=None, closed=None, **kwargs)
Periods: 指时间范围,要取多长时间的数值,如 pd.date_range(start = ‘2002-01-01”, periods = 50)— 结果为20200101后面50天的依次日期展示)
Freq: 日期偏移量
Normalize: True表示化为正常标准数值,如2020-01-01
normalize=True表示将时间戳标准化为零点
基础时间序列频率(不全) | ||
---|---|---|
别名 | 偏置类型 | 描述 |
D | Day | 日历日的每天 |
B | BusinessDay | 工作日的每天 |
H | Hour | 每小时 |
T或min | Minute | 每分钟 |
S | Second | 每秒 |
L或ms | Milli | 每毫秒(1/1000 秒) |
U | Micro | 每微秒(1/1000000 秒) |
M | MonthEnd | 日历日的月底日期 |
BM | BusinessMonthEnd | 工作日的月底日期 |
MS | MonthBegin | 日历日的月初日期 |
BMS | BusinessMonthBegin | 工作日的月初日期 |
W-MON,W-TUE… | Week | 按照给定的星期日期按每周取日期(MON,TUE,WED,THU,FRI,SAT或SUN) |
WOM-1MON,WOM-2MON… | WeekofMonth | 在每月的第一、二、三或四周创建按周分割的日期(如WON-3FRI代表每月的第三个星期五) |
Q-JAN,Q-FEB | QuarterEnd | 每个月最后一个日历日的季度 |
- 频率和日期偏置
pandas中的频率是由基础时间频率和倍数组成的,基础频率如上表所示,对于每个基础频率,都有一个对象可以用于定义日期偏置,在大多数应用中,没有必要创建这个对象,可以使用字符串别名,“如‘H’或‘4H’”,在基础频率前放一个整数就可以生成倍数。
多个偏执可以通过加法进行联合:Hour(2)+Minute(30)。也可以传递频率字符串如‘1h30min’和之前的Hour(2)+Minute(30)是等价的表达式。
有些频率的描述点并不是均匀分隔的,如‘M(日历日月末)’,取决于当月天数,我们将这些日期成为锚定偏置量。 - 移位日期
Series和Dataframe中有一个shift方法用于简单的向前或向后移位,而不改变索引:
简单地移位不会改变索引,一些数据会被丢弃,所以如果频率是已知的,可以将基础频率传递给shift来推移时间戳而不是推移数据:
- 使用偏置进行日期移位
如果添加了一个锚定偏置量,比如MonthEnd,根据频率规则,第一个增量会将日期“前滚”到下个日期:
锚定偏置值可以使用rollforward和rollback分别显示地将日期向前或向后“滚动”:
时区处理
很多时间序列用户选择世界协调时间或UTC,它是格林尼治时间的后继者,也是目前的国际标准。市区通常表示为UTC的偏置。在python中,时区信息来源于第三方库pytz。
- 时区的本地化和转换
默认情况下,pandas中的时间序列是时区简单型的(所谓的简单型,就是默认为None的意思。如果需要带时区,可以通过tz属性设置,tz='UTC’设置为标准时区。)使用tz_location方法可以从简单时区转换到本地化时区:ts_utc=ts.tz_location(‘UTC’)。一旦时间序列被本地化为某个特定的时区,则可以通过tz_convert将其转化为另一个时区:ts_utc.tz_convert(‘America/New_York’)。tz_location和tz_convert也是DatetimeIndex的实例方法。
- 时区感知时间戳对象的操作
与时间序列和日期范围类似,单独的Timestamp对象也可以进行时间戳本地化并用时区感知时间戳,即一个时区转换为另一个时区:
也可以在创建Timestamp对象时传入tz选项来传递时区。时区感知的Timestamp对象内部储存了一个Unix纪元(1970年1月1日)至今的纳秒数量UTC时间戳数值,该时间戳在时区的转换中是不变的:
- 不同时区间的操作
如果两个不同时区间的时间序列需要联合,那么结果将是UTC时间。由于时间戳以UTC格式存储,所以时间不需要转换。
时间区间和区间算术
时间区间表示的是时间范围,period类表示的正是这种数据类型,需要传递一个字符串或数字和基础频率:
使用period_range可以构造规则的区间序列:pd.period_range(‘2000-01-01’,‘2000-06-30’,freq=‘M’)。
periodIndex类储存的是区间的序列,可以作为pandas对象的轴索引(pd.period_range()生成的就是periodIndex对象)。
- 区间频率转换(P1166)*
使用asfreq可以将区间和periodIndex对象转换为其他频率:
- 季度区间频率*
季度数据是会计、金融和其他行业的标准。很多季度数据是在财年结尾报告的,通常是一年12个月中最后一个日历日或工作日。pandas支持所有可能的12个季度频率,从Q-JUN到Q-DEC:
- 将时间戳(具体的时间点)转换为区间以及逆转换
通过to_period方法转换为区间:
periodIndex对象是区间,DatetimeIndex对象是时间戳。
区间是非重叠时间范围,时间戳只能属于给定频率的单个区间,默认情况下可以根据时间戳推断出新的periodIndex的频率,我们也可以指定自己想要的频率(在结果中包含重复区间也是可以的)。
使用to_timestamp可以将区间再转换为时间戳:
- 从数组中生成periodIndex
比如一个经济数据集,它的年份和季度储存在不同的列中,将这些数组和频率传递给periodIndex,可以联合这些数组形成Dataframe的索引。
重新采样与频率转换
重新采样是指将时间序列从一个频率转换为另一个频率的过程。将更高频率的数据聚合到低频率被称为下采样,而从低频率转换到高频率称为上采样。两个频率相同的转换既不是上采样也不是下采样。
pandas对象都有resample方法,该方法是所有频率转换的工具函数。resample拥有类似于groupby的API,调用resample对数据分组,之后再调用聚合函数:
resample是一个灵活且高性能的方法,可以用于处理大型时间序列。
resample方法参数 | |
---|---|
freq | 表明所需采样频率的字符串或者Dateoffset对象(如‘M’,‘5min’) |
axis | 需要采样的轴向,默认是axis=0 |
fill_method | 上采样时的插值方式,ffill或bfill,默认是不插值的 |
closed | 下采样中,每段间隔哪一端是闭区间,right或left |
label | 下采样中,right或left的箱标签用来标记聚合结果(如9:30到9:35的间隔可以被标记为9:30或9:35) |
loffset | 对箱标签进行时间调整(如second(-1)可以将标签向前移动一秒) |
limit | 在向前或向后填充时,填充区间的最大值 |
kind | 对区间(period)或时间戳(timestamp)的聚合,默认为时间序列索引的类型(就是与原来的保持一致) |
convention | 在对区间重采样时,用于将低频周期转化为高频周期的约定(‘start’或‘end’),默认为‘end’ |
-
向下采样
将数据聚合到一个低频率上是一种常见的时间序列任务。要注意的是每个数据点只能属于一个时间间隔,所以时间间隔的区间都是半闭合的,我们要决定哪一端是闭区间:ts.resample(‘5min’,closed=‘right’).sum()。这例子按5分钟增量定义了箱体边界。默认情况下,箱体左边界是闭合的。还可能用到loffset选项来调整时间序列:如loffset=‘-1s’会使每个时间序列减去一秒,也可以通过调用shift的方法来实现loffset同样的效果。
OHLC(开端-峰值-谷值-结束)重采样。 -
向上采样与插值
从低频率转换为高频率时,并不需要任何聚合。
使用asfreq方法在不聚合的情况下转换到高频率:ts.resample(‘D’).asfreq()。
使用ffill方法可以填充数值,在函数内传入选项limit=2可以限制填充区间。 -
使用区间进行重采样
对以区间为索引的数据进行采样与时间戳类似,但由于区间涉及时间范围,所以上采样和下采样更为严格:
1.在下采样中,目标频率必须是原频率的子区间。
2.在上采样中,目标频率必须是原频率的父区间。
移动窗口函数*
rolling算子,它的行为与resample和groupby类似。rolling可以在Series或Dataframe上通过一个window(以一个区间数字来表示)进行调用。如rolling(250)表示取一天及其后面的249天作为一个窗口,每次滑动一天。当时间序列从起位置据拥有的数据小于窗口大小,使用expanding算子。
- 指数加权函数
指定一个常数衰减因子用来向更多近期观测值提供权重,可以替代使用具有相等加权观察值的静态窗口尺寸方法。有多种方式可以指定衰减因子,其中一个比较流行的方法是span(跨度),是一个使结果与窗口大小等于跨度的简单移动窗口函数。指数加权统计值给近期的观测值更多的权重,越近的数据越有影响力。pandas拥有ewm算子,同rolling、expanding算子结合使用。 - 二元移动窗口函数
一些统计算子,如相关度和协方差,需要操作两个时间序列。 - 用户自定义的移动窗口函数
在rolling及其相关方法上使用apply方法,可以在移动窗口中使用自己设计的数组函数方法。唯一的要求是从每个数组中产生一个单值。
高阶pandas
- 分类数据
pandas的Categorical类型(分类编码数据)
利用df[‘fruit’].astype(‘category’)可以将pythonSeries对象转化为Categorical对象。
Categorical对象拥有categories(categories储存了去重之后的奇异值)和codes(codes储存每个奇异值对应的编码)属性。
也可以通过python的其他序列类直接生成pandas的Categorical类型:pd.Categorical([‘foo’,‘bar’,‘foo’])
除非显示地指定,分类转换是不会指定类别的顺序的,可以使用from_codes或其他构造函数指定一个有意义的顺序pd.Categorical.from_codes(codes,categories,ordered=True)。
要注意的是,分类的数据可以不是字符串。
使用Categorical对象进行计算
pandas的某些函数,比如groupby函数,与Categorical对象协同工作时性能更好。
之前学过的函数pandas.qcut分箱函数。结果会返回pandas.Categorical;
使用分类获得更高性能
Dataframe中一列的分类会明显的使用更少内存,使用分类对象进行groupby操作明显更快,因为底层算法使用了基于整数代码的数组而不是字符串数组。
分类方法
Series包含的分类数据拥有一些特殊的方法,这些方法类似于Series.str的特殊字符串方法。这些方法提供了快捷访问类别和代码的方式。
cat_s=s.astype(‘catefory’)
//特殊属性cat提供了对分类方法的访问
cat_s.cat.codes、cat_s.cat.categories
//可以使用set_categories方法来改变类别
cate_s.cat.set_categories(new_categories)
在大型数据集中,分类数据是用于节省内存和更高性能的便捷工具,当过滤了一个大型的Dataframe或Series之后,很多类别将不会出现在数据中,为了解决这个问题,可以使用remove_unused_categories方法来去除未观察到的类别:
cat_s.cat.remove_unused_categories()
Series的分类方法 | |
---|---|
方法 | 描述 |
add_categories | 将新的类别添加到已有类别的尾部 |
as_ordered | 对类别排序 |
as_unordered | 使类别无序 |
remove_categoies | 去除类别,将被移除的值置为null |
remove_unused_categoies | 去除没有出现在数据中的类别 |
rename_categories | 重命名类别 |
reorder_categories | 与rename_categories相似,但结果是经过排序的类别 |
set_categories | 用指定一组新的类别来替换现有类别,可以增加或删除列 |
创建用于建模的虚拟变量
当使用统计数据或机器学习工具时,通常会将分类数据转换为虚拟变量,也称为one-hot编码。这会产生一个Dataframe,每个不同的类别都是它的一列,这些列包含一个特定类别的出现次数,否则为0。
pandas.get_dummies函数将一维的分类数据转换为一个包含虚拟变量的Dataframe:
pd.get_dummies(cat_s)
-
高阶GroupBy应用
分组转换和展开GroupBy
在分组操作中学习了apply方法用于执行转换操作,还有另外一个内建方法transform,与apply方法类似但是会对可使用的函数种类加上更多限制:
1.transform可以产生一个标量值,并广播到各分组的尺寸数据中
2.transform可以产生一个与输入分组尺寸相同的对象
3.transform不可改变它的输入
对于内建的聚合函数,可以向GroupBy的agg方法一样传递一个函数字符串别名:
内建的聚合函数如‘mean’或‘sum’通常会比apply函树更快。这些函数与transform一起使用时也会存在一个‘快速通道’,可以允许我们执行一个展开分组操作,如:(df[‘value’]-g.transfrom(‘mean’))/g.transfrom(‘std’),一个展开分组操作可能包含多个分组聚合,矢量化操作的整体优势往往超过了这一点。
分组的时间重新采样
对于时间序列的数据,resample方法在语义上是一种基于时间分段的分组操作。
使用TimeGrouper的限制是时间必须是Dataframe或Series的索引。 -
方法链技术
在向数据集应用一系列变换时,有可能会出现创建很多临时变量的现象,而这些变量在分析中从未出现过。
上面两种方式,第一种赋值方式更为快速,assign可以实现更方便的方法链。
方法链技术可能避免了生成中间变量,但是也使代码的可读性变低。
pipe方法可以在调用函数或调用第三方库时使用方法链技术:pipe(def_name,def_参数)
Python建模库介绍
两个流行的建模工具包,statsmodels和scikit-learn。
-
pandas与建模代码的结合
一些利用pandas进行数据操作和建模之间无缝切换的方法。pandas和其他分析库结合点通常是numpy数组,要将Dataframe转换为numpy数组,使用values属性:df.values。
.vlaues属性一般是数据是同构化的时候使用,比如数据都是数字类型的时候,当数据是异构化的时候,结果将是python对象的ndarray。
例子:数据集中有一个非数字类型的列,想用虚拟变量替代category(分组)列,要先创建虚拟变量,之后删除‘categories’列,然后进行结果连接:
-
使用Patsy创建模型描述
Patsy是一个用于描述统计模型(尤其是线性模型)的python库,Pasty能够很好地支持statsmodels中特定的线性模型。
Patsy的公式是特殊的字符串语法:
语法a+b指代为模型而创建的设计矩阵中的名词列。
patsy.dmatrices函数在数据集上使用了一个公式字符串,并为一个线性模型产生了设计矩阵:
这些patsy的DesignMatrix实例是含有附加元数据的numpy adarray。
上面的结果输出有一个Intercept(截距)名词列。这是线性模型中的惯例,可以通过给模型添加名词列+0来加入截距:
patsy对象可以直接传递一些算法,比如numpy.linalg.lstsq等,这些算法都会执行一个最小二乘回归:
Patsy公式中的数据转换
也可以将python代码混合到patsy公式中,在执行公式时,patsy库将尝试在封闭作用域中寻找所使用的函数:
一些常用的变量转换包括标准化(对均值0和方差1)和居中(减去平均值),为了实现这个目的,patsy具有内置函数:standardize、center……
patsy.build_design_matrices函数可以使用原始数据样本内数据集中保存的信息将变换应用到新的样本外数据上。
分类数据与patsy
有多种方式可以将非数字类型数据转换以用于模型的设计矩阵,当在patsy公式中使用非数字名词列,他们会被默认的转换为虚拟变量。
数字类型列可以使用C函数解释为分类类型:pats.dmatrices(‘y~C(x)’,data) -
statsmodels介绍
statsmodels是一个python库,用于拟合多种统计模型,执行统计测试以及数据探索和可视化。statsmodels包含很多经典的统计方法。包含在statsmodels中的一些模型:
- 线性模型,广义线性模型和鲁棒线性模型(健壮)
- 线性混合效应模型
- 方差分析(ANOVA)方法
- 时间序列过程和状态空间模型
- 广义的矩量法
statsmodels的基本工具使用和使用带有patsy公式和pandas Dataframe对象的建模接口。
评估线性模型
统计模型中有几种线性回归模型,较基本的(最小二乘),较复杂的(迭代重新加权的最小二乘)。
statsmodels中线性模型有两个不同的主要接口:基于数组的和基于公式的,通过以下模块导入访问:
以下展示如何使用这些模块:
sm.add_constant函数可以将截距列添加到现有矩阵:
sm.OLS类可以拟合一个最小二乘线性回归:
模型的fit方法返回一个回归结果对象,该对象包含了估计的模型参数和其他诊断:
评估时间序列处理
statsmodels中另一种模型用于时间序列分析。其中包括回归过程,卡尔曼滤波和其他状态空间模型,以及多变量自回归模型。
- scikit-learn介绍
scikit-learn是使用广泛的python机器学习库。它包含广泛的标准的监督和无监督机器学习方法,包括用于模型选择和评估、数据转换、数据加载、模型持久化的工具。这些模型可用于分类、聚类、预测和其他常见任务。书中这里介绍的是scikit-learn API风格。
statsmodels和scikit-learn通常不能提供缺失数据,因此要检查各列是否包含缺失数据。如果所需的列中有缺失数据。有很多方法可以进行缺失数据的插补。可以使用scikit-learn的LogisticRegression模型创建一个模型实例:
实际中,模型中具有可以调整的参数,并且存在可用于参数调整的交叉验证等技术以避免过度拟合训练数据。这样在新数据上会产生更好地预测性能或稳健性。交叉验证通过分割训练数据来模拟样本外预测。基于像均方误差之类的模型准确度分数,可以对模型参数执行网格搜索。一些模型具有内置的交叉验证估计类,如LogisticRegressionCV类可以与一个参数一起使用,该参数表示网格搜索在模型正则化参数C上的细致度。
要手动进行交叉验证,可以使用cross_val_score帮助函数,该函数处理数据拆分过程。
以上只是python建模库的一些皮毛。
高阶numpy
- numpy对象内幕
numpy的ndarray提供了一种方法将一组同构结构数据解释为多维数组对象。数据类型或dtype决定数据如何被解释为浮点数、整数、布尔值或我们查看的任何其他数据类型