目录
一、序列与数据框的构造
1、构造序列
2、构造数据框
二、外部数据的读取
1、文本文件的读取
2、电子表格的读取
pd.read_excel参数介绍:
三、数据类型转换及描述统计
四、字符与日期数据的处理
五、常用的数据清洗方法
1、重复观测处理
2、缺失值处理
3、异常值处理
六、数据子集的获取
七、透视表功能
八、表之间的合并与连接
九、分组聚合操作
一、序列与数据框的构造
Pandas模块的核心操作对象就是序列(Series)和数据框(DataFrame)。
1、构造序列
使用如下方式实现:
通过同质的列表或元组构建。
通过字典构建。
通过Numpy中的一维数组构建。
import pandas as pdimport numpy as npdata1=pd.Series([2.8,3.01,8.99,8.59,5.18])data2=pd.Series({'北京':2.8,'上海':3.01,'广东':8.99,'江苏':8.59,'浙江':5.18})data3=pd.Series(np.array((2.8,3.01,8.99,8.59,5.18)))print(data1)print(data2)print(data3)
通过 Series函数将列表、字典和一维数组转换为序列的过程。 构造的序列结果都是第一个打印的样式。 该样式会 产生两列, 第一列属于序列的行索引(可以理解为行号),自动从0 开 始,第二列才是序列的实际值。 通过字典构造的序列就是第二个打印样式,仍然包含两列,所不同的是第一列不再是行号,而是具体的行名称 (label),对应到字典中的键,第二列是序列的实际值,对应到字典中 的值。
序列与一维数组有极高的相似性.
不同的是,序列会有更多的其他处理方法。下面通过几个 具体的例子来加以测试:
print('行号风格的序列:\n',data1[[0,3,4]],'\n')print('行名称风格的序列:\n',data2[[0,3,4]],'\n')print('行名称风格的序列: \n',data2[['上海','江苏','浙江']],'\n')print('通过numpy函数:\n',np.log(data1),'\n')print('通过numpy函数:\n',np.mean(data1),'\n')print('通过序列的方法:\n',data1.mean(),'\n')
如果需要对序 列进行数学函数的运算,一般首选numpy模块,因为Pandas模块在这方 面比较缺乏;如果是对序列做统计运算,既可以使用numpy模块中的函 数,也可以使用序列的“方法”,作者一般首选序列的“方法”,因为序列 的“方法”更加丰富,如计算序列的偏度、峰度等,而Numpy是没有这样的函数的。
这里稍微介绍一下序列的方法,就不演示了:
①检查缺失值;
Series名.isnull() 和 Series名.notnull()
isnull() 判断索引对应的值是否为空,若为空,返回True。语句返回值为Series类型。
notnull() 判断索引对应的值是否为非空,若为空,返回False。
②通过索引获取数据;
获取索引的方法:Series名.index
获取值的方法:Series名.values
2.1 获取单个数据
① 通过下标获取 Series名[索引号]
② 通过标签名获取Series名[标签名]
2.2 获取多个数据
① 通过下标获取 Series名[[索引号1,索引号2]] #获取索引号1和2对应的数据
② 通过标签名获取 Series名[[标签名1,标签名2]]
2.3 切片获取数据
① 通过下标获取 Series名[索引号1:索引号2] #下标切片顾头不顾尾
② 通过标签名获取Series名[标签名1:标签名2] #标签名切片顾头顾尾
③布尔索引;
Series名[条件判断]
布尔索引可理解为True和False。
如:
s11 = s10[s10>3] #取出s10的值大于3的数据
④name属性;
改变对象的名称: Series名.name = 自定义名称
改变对象的索引名称:Series名.index.name = 自定义名称
⑤读取前几行数据;
Series名.head() # 默认读取前5行数据
Series名.head(n) # 也可自己指定读取前n行
⑥读取后几行数据。
Series名.tail() # 默认读取后5行数据
Series名.tail(n) # 也可自己指定读取后n行
感兴趣的可以自己去查还有很多的呢
好了,回归主题,继续介绍pandas
2、构造数据框
数据框中可以存放不同数据类型的序列而数组和序列则没有这样的优势,因为它们只能存放同质数据。应用如下方式:
通过嵌套的列表或元组构造。 通过字典构造。 通过二维数组构造。 通过外部数据的读取构造。
# 构造数据框data1=pd.DataFrame([['张三','23','男'],['李四','27','女'],['王二','26','女']])data2=pd.DataFrame({'姓名':['张三','李四','王二'],'年龄':['23','27','26'],'性别':['男','女','女']})data3=pd.DataFrame(np.array([['张三',23,'男'],['李四','27','女'],['王二','26','女']]))print('嵌套列表构造数据框:\n',data1)print('字典构造数据框:\n',data2)print('二维数组构造数据框: \n',data3)
如果需要手工构造数据框的话,一般首选字典方法。因为字典转换数据框时有具体的变量名
二、外部数据的读取
很显然,每次通过手工构造数据框是不现实的,在实际工作中,更多的情况则是通过Python读取外部数据集,这些数据集可能包含在本地 的文本文件(如csv、txt等),接下来介绍读取外部文件。
1、文本文件的读取
当要读取外部文件时。一般使用 Pandas模块中的read_table函数或read_csv函数。这里的“或”并不是指每 个函数只能读取一种格式的数据,而是这两种函数均可以读取文本文件的数据。
这里主要介绍pd.read_table,因为它们的参数都是差不多的。
pd.read_table(filepath_or_buffer,sep='\t',header='infer',names=None, index_col=None,usecols=None,dtype=None,converters=None,skiprows=None,skipfooter=None,nrows=None,na_values=None, skip_blank_lines=True,parse_dates=False,thousands=None,comment=None,encoding=None)
pd.read_table参数介绍:
filepath_or_buffer:指定txt文件或csv文件所在的具体路径。
sep:指定原数据集中各字段之间的分隔符,默认为Tab制表符。
header:是否需要将原数据集中的第一行作为表头,默认将第一 行用作字段名称。
names:如果原数据集中没有字段,可以通过该参数在数据读取 时给数据框添加具体的表头。
index_col:指定原数据集中的某些列作为数据框的行索引(标 签)。
usecols:指定需要读取原数据集中的哪些变量名。
dtype:读取数据时,可以为原数据集的每个字段设置不同的数 据类型。
converters:通过字典格式,为数据集中的某些字段设置转换函 数。
skiprows:数据读取时,指定需要跳过原数据集开头的行数。
skipfooter:数据读取时,指定需要跳过原数据集末尾的行数。
nrows:指定读取数据的行数。
na_values:指定原数据集中哪些特征的值作为缺失值。
skip_blank_lines:读取数据时是否需要跳过原数据集中的空白 行,默认为True。
parse_dates:如果参数值为True,则尝试解析数据框的行索引; 如果参数为列表,则尝试解析对应的日期列;如果参数为嵌套列 表,则将某些列合并为日期列;如果参数为字典,则解析对应的 列(字典中的值),并生成新的字段名(字典中的键)。
thousands:指定原始数据集中的千分位符。
comment:指定注释符,在读取数据时,如果碰到行首指定的注释符,则跳过改行。
encoding:如果文件中含有中文,有时需要指定字符编码。
下面举例说明:
这里构造一个稍 微复杂点的数据集用于测试,数据存放在txt 中。
#读取文本文件中的数据user_income = pd.read_table ('data.txt',sep = ',',parse_dates= { 'birthday':[ 0,1,2] },skiprows=2,skipfooter=3,comment='#',encoding='utf8',thousands='&')user_income
代码说明:由于read_table函数在读取数 据时,默认将字段分隔符sep设置为Tab制表符,而原始数据集是用逗号 分割每一列,所以需要改变sep参数;parse_dates参数通过字典实现前三 列的日期解析,并合并为新字段birthday;skiprows和skipfooter参数分别 实现原数据集开头几行和末尾几行数据的跳过;由于数据部分的第四行 前面加了#号,因此通过comment参数指定跳过的特殊行;这里仅改变 字符编码参数encoding是不够的,还需要将原始的txt文件另存为UTF-8 格式;最后,对于收入一列,由于千分位符为&,因此为了保证数值型 数据的正常读入,需要设置thousands参数为&。
2、电子表格的读取
pd.read_excel(io,sheetname=0,header=0,skiprows=None,skip_footer=0,index_col=None,names=None,parse_cols-None,parse_dates=False,na_values=None,thousands=None,convert_float=True)
pd.read_excel参数介绍:
io:指定电子表格的具体路径。
sheetname:指定需要读取电子表格中的第几个Sheet,既可以传 递整数也可以传递具体的Sheet名称。
header:是否需要将数据集的第一行用作表头,默认为是需要 的。
skiprows:读取数据时,指定跳过的开始行数。
skip_footer:读取数据时,指定跳过的末尾行数。
index_col:指定哪些列用作数据框的行索引(标签)。
names:如果原数据集中没有字段,可以通过该参数在数据读取 时给数据框添加具体的表头。
parse_cols:指定需要解析的字段。
parse_dates:如果参数值为True,则尝试解析数据框的行索引; 如果参数为列表,则尝试解析对应的日期列;如果参数为嵌套列 表,则将某些列合并为日期列;如果参数为字典,则解析对应的 列(字典中的值),并生成新的字段名(字典中的键)。
na_values:指定原始数据中哪些特殊值代表了缺失值。
thousands:指定原始数据集中的千分位符。
convert_float:默认将所有的数值型字段转换为浮点型字段。
converters:通过字典的形式,指定某些列需要转换的形式。
三、数据类型转换及描述统计
内容主要介绍如何了解数据,例如读 入数据的规模如何、各个变量都属于什么数据类型、一些重要的统计指 标对应的值是多少、离散变量各唯一值的频次该如何统计等。下面以泰坦尼克号乘客信息为例:
# 数据读取 sec_cars = pd.read_table(r'train.csv', sep = ',') # 预览数据的前五行 sec_cars.head()
如上代码中,head方法可以返回 数据集的开头5行;如果读者需要查看数据集的末尾5行,可以使用tail 方法。进一步,如果还想知道数据集有多少观测和多少变量,以及每个 变量都是什么数据类型,可以按如下代码得知:
# 查看数据的行列数print('数据集的行列数:\n',sec_cars.shape)#查看数据集每个变量的数据类型print('各变量的数据类型:\n',sec_cars.dtypes)
接下来,需要对数据做到心中有数,即通过基本的统计量(如最小 值、均值、中位数、最大值等)描述出数据的特征。关于数据的描述性 分析可以使用describe方法:
# 数据的描述性统计 sec_cars.describe()
通过describe方法,直接运算了数据框中所有数值 型变量的统计值,包括非缺失个数、平均值、标
准差、最小值、下四分 位数、中位数、上四分位数和最大值。
以上都是有关数据的统计描述,但并不能清晰地知道数据的形状分布,如数据是否有偏以及是否属
于“尖峰厚尾”的特征,为了一次性统计 数值型变量的偏度和峰度,可以参考如下代码:
#挑出所有数值型变量num_variables = sec_cars.columns[sec_cars.dtypes !='object'] [1:]#自定义函数,计算偏度和峰度def skew_kurt(x):skewness = x.skew ()kurtsis =x.kurt()#返回偏度值和峰度值return pd.Series( [skewness, kurtsis], index = [ 'Skew', 'Kurt'])#运用apply方法sec_cars[num_variables ].apply(func = skew_kurt,axis = 0)
如上结果所示正是每个数值型变量的偏度和峰度,这三个变量都属 于右偏(因为偏度值均大于0),而且三个变量也是尖峰的(因为峰度 值也都大于0)。
代码说明:columns方法用于返回数据集的所有变量 名,通过布尔索引和切片方法获得所有的数值型变量;在自定义函数 中,运用到了计算偏度的skew方法和计算峰度的kurt方法,然后将计算 结果组合到序列中;最后使用apply方法,该方法的目的就是对指定轴 (axis=0,即垂直方向的各列)进行统计运算(运算函数即自定义函 数)。
以上的统计分析全都是针对数值型变量的,对于数据框中的字符型变量,该如何做统计描述
呢?仍然可以使用describe方法,所不同的是,需要设置该方法中的
include参数,具体代码如下:
# 离散型变量的统计描述 sec_cars.describe(include = ['object'])
对于离散型变量,运用describe方法只能得知哪个离散水平属于“明 星”值。如果读者需要统计的是
各个离散值的频次,甚至是对应的频率,该如何计算呢?这里直接给出如下代码(Sex为例):
# 离散变量频次统计 Freq = sec_cars.Sex.value_counts() Freq_ratio = Freq/sec_cars.shape[0] Freq_df = pd.DataFrame({'Freq':Freq,'Freq_ratio':Freq_ratio}) Freq_df.head()
如果需要把行标签设置为数据框中的列,可以使用 reset_index方法,具体操作如下:
# 将行索引重设为变量 Freq_df.reset_index(inplace = True) Freq_df.head()
reset_index方法的使用还是比较频繁的,它可以非常方便地将行标 签转换为数据框的变量。在如上代码中,将reset_index方法中的inplace参数设置为True,表示直接对原始数据集进行操作,响到原数据集的 变化,否则返回的只是变化预览,并不会改变原数据集。
四、字符与日期数据的处理
本节会介绍有关 日期型数据的处理,比方说,如何从日期型变量中取出年份、月份、星 期几等,如何计算两个日期间的时间差。
下面不啰嗦,直接举例说明:数据集查看2
下面演示一些常用的操作:
#数据读入df = pd.read_excel('data.xlsx')#各变量数据类型df.dtypes#将出生日期变量转换为日期型df.出生日期 = pd.to_datetime (df.出生日期,format = '%Y /%m/%d')#将手机号转换为字符串df .电话号码 = df.电话号码.astype ( 'str')#新增年龄和工龄两列df [ '年龄'] = pd.datetime.today ().year - df.出生日期.dt.yeardf [ '工龄'] = pd.datetime.today ().year - df.工作时间.dt.year#取出邮箱的域名df [ '邮箱域名'] = df.邮箱.apply(func = lambda x : x.split ('@') [1])#将手机号中间四位隐藏起来df.电话号码 = df.电话号码.apply(func = lambda x : x.replace(x[3:7],'**** '))#去除邮箱变量df.drop([ '邮箱' ], axis = 1, inplace = True)df.head()
常用的日期时间处理“方法”
接下来,挑选几个日期处理 “ 方法 ” 用以举例说明:
#常用日期处理方法dates = pd.to_datetime (pd.Series(['1989-8-18 13:14:55','1995-2-16']),format='%Y-%m-%d %H:%M:%S')print('返回日期值:\n ',dates.dt.date)print('返回季度:\n',dates.dt.quarter)print('返回几点钟:\n',dates.dt.hour)print('返回年中的天:\n',dates.dt.dayofyear)print ('返回年中的周:\n',dates.dt .weekofyear)#print('返回星期几的名称:\n',dates.dt.weekday_name)print('返回月份的天数: \n ',dates.dt.days_in_month)
五、常用的数据清洗方法
在数据处理过程中,一般都需要进行数据的清洗工作,如数据集是 否存在重复、是否存在缺失、数据是否具有完整性和一致性、数据中是 否存在异常值等。当发现数据中存在如上可能的问题时,都需要有针对 性地处理,本节将重点介绍如何识别和处理重复观测、缺失值和异常值。
1、重复观测处理
2、缺失值处理
3、异常值处理
六、数据子集的获取
有时数据读入后并不是对整体数据进行分析,而是数据中的部分子 集,例如,对于地铁乘客量可能只关心某些时间段的流量、对于商品的 交易可能只需要分析某些颜色的价格变动、对于医疗诊断数据可能只对 某个年龄段的人群感兴趣等。所以,该如何根据特定的条件实现数据子 集的获取将是本节的主要内容。
#构造数据集df1 = pd.DataFrame ( {'name':[ '张三','李四','王二','丁一','李五'],'gender':['男','女','女','女','男'],'age':[23,26,22,25,27]},columns = ['name' , 'gender' , 'age'])df1#取出数据集的中间三行{即所有女性),并且返回姓名和年龄两列#df1.iloc[1:4, [0,2]]#df1.loc[ 1 : 3,['name','age']]#df1.ix[1:3,[0.2]]
如上结果所示,如果原始数据的行号与行标签(名称)一致, iloc、loc和ix三种方法都可以取出满足条件的数据子集。所不同的是, iloc运用了索引的思想,故中间三行的表示必须用1:4,因为切片索引取 不到上限,同时,姓名和年龄两列也必须用数值索引表示;loc是指获 取行或列的标签(名称),由于该数据集的行标签与行号一致,所以 1:3就表示对应的3个行名称,而姓名和年龄两列的获取就不能使用数值 索引了,只能写入具体的变量名称;ix则混合了iloc与loc的优点,如果 数据集的行标签与行号一致,则ix对观测行的筛选与loc的效果一样,但 是ix对变量名的筛选既可以使用对应的列号(如代码所示),也可以使 用具体的变量名称。
# 将员工的姓名用作行标签 df2 = df1.set_index('name')df2 # 取出数据集的中间三行 df2.iloc[1:4,:] df2.loc[['李四','王二','丁一'],:] df2.ix[1:4,:]
很显然,在实际的学习和工作中,观测行的筛选很少是通过写入具 体的行索引或行标签,而是对某些列做条件筛选,进而获得目标数据。 例如,在上面的df1数据集中,如何返回所有男性的姓名和年龄,代码 如下:
# df1.iloc[df1.gender == '男',] df1.loc[df1.gender == '男',['name','age']] df1.ix[df1.gender == '男',['name','age']]
综上所述,ix方法几乎可以实现所有情况中数据子集的获取,是 iloc和loc两种方法的优点合成体,而且对于行号与行名称一致的数据集 来说(如df1数据集),名称索引的优先级在位置索引之前(如本节第 一段代码中的df1.ix[1:3,[0,2]])。
七、透视表功能
如果这样的汇总过程不是在Excel中,而是在Python中,该如何实现呢?Pandas模块提供了实现透视表功能的pivot_table函数,该函数简单易用,与Excel的作思想完全一致,相信读者一定可以快速掌握函数 的用法及参数含义。接下来向读者介绍一下有关该函数的参数含义:
pd.pivot_table(data,values=None,index=None,columns=None,aggfunc='mean', fill_value=None,margins=False,dropna=True, margins_name= 'Al1')
data:指定需要构造透视表的数据集。
values:指定需要拉入“数值”框的字段列表。
index:指定需要拉入“行标签”框的字段列表。
columns:指定需要拉入“列标签”框的字段列表。
aggfunc:指定数值的统计函数,默认为统计均值,也可以指定numpy模块中的其他统计函数。
fill_value:指定一个标量,用于填充缺失值。
margins:bool类型参数,是否需要显示行或列的总计值,默认为False。
dropna:bool类型参数,是否需要删除整列为缺失的字段,默认为True。
margins_name:指定行或列的总计名称,默认为All。
为了说明该函数的灵活功能,这里以上面的珠宝数据为例,重现 Excel制作成的透视表。首先来尝试一下单个分组变量的均值统计,具 体代码如下:
# 数据读取 diamonds = pd.read_table(r'diamonds.csv', sep = ',') # 单个分组变量的均值统计 pd.pivot_table(data = diamonds, index = 'color', values = 'price', margins = True,margins_name = '总计')
如上结果所示就是基于单个分组变量color的汇总统计(price的均 值),返回结果属于Pandas模块中的序列类型,该结果与Excel形成的透 视表完全一致。接下来看看如何构造两个分组变量的列联表,代码如下 所示:
#两个分组变量的列联表#导入numpy模块import numpy as nppd.pivot_table (data = diamonds,index m 'clarity', columns = 'cut ',values = 'carat',aggfunc - np.size,margins = True,margins_name - '总计')
八、表之间的合并与连接
(1)合并函数 concatPandas模块同样提供了关于多表之间的合并和连接操作函数,分别 是concat函数和merge函数,首先介绍一下这两个函数的用法和重要参数 含义。
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False)
objs:指定需要合并的对象,可以是序列、数据框或面板数据构 成的列表。
axis:指定数据合并的轴,默认为0,表示合并多个数据的行,如 果为1,就表示合并多个数据的列。
join:指定合并的方式,默认为outer,表示合并所有数据,如果 改为inner,表示合并公共部分的数据。
join_axes:合并数据后,指定保留的数据轴。
ignore_index:bool类型的参数,表示是否忽略原数据集的索引, 默认为False,如果设为True,就表示忽略原索引并生成新索引。
keys:为合并后的数据添加新索引,用于区分各个数据部分。
#构造数据集df1和 df2df1 = pd.DataFrame ( { 'name' :[ '张三','李四','王二'], 'age' : [21,25,22],'gender" :['男','女·,'男']})df2 = pd.DataFrame ( { 'name' : [ '丁一', '赵五'],'age' :[23,22],'gender ' : [ '女', '女']})#数据集的纵向合并pd.concat ([df1, df2] , keys =['df1', 'df2 '])#并如果df2数据集中的“姓名变量为Name”df2 = pd.DataFrame ( { 'Name ' : [ '丁一','赵五'],'age ' : [23,22],'gender ' : ['女', '女'] )#数据集的纵向合并pd.concat([df1,df2])
(2)连接函数 merge
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False)
left:指定需要连接的主表。
right:指定需要连接的辅表。
how:指定连接方式,默认为inner内连,还有其他选项,如左连 left、右连right和外连outer。
on:指定连接两张表的共同字段。left_on:指定主表中需要连接的共同字段。
right_on:指定辅表中需要连接的共同字段。
left_index:bool类型参数,是否将主表中的行索引用作表连接的 共同字段,默认为False。
right_index:bool类型参数,是否将辅表中的行索引用作表连接 的共同字段,默认为False。
sort:bool类型参数,是否对连接后的数据按照共同字段排序, 默认为False。
suffixes:如果数据连接的结果中存在重叠的变量名,则使用各自 的前缀进行区分。
九、分组聚合操作
df=pd.DataFrame({'A':['foo','bar','foo','bar','foo','bar','foo','foo'],'B':['one','one','two','three','two','two','one','three'],'C':np.random.randn(8),'D':np.random.randn(8)})df.groupby('A').sum()df.groupby(['A','B']).sum()