1.简介
基于Numpy构建 pandas的出现,让Python语言成为使用最广泛而且强大的数据分析环境之一 pandas的主要功能 - 具备诸多功能的两大数据结构 Series、DataFrame(核心) 都是基于Numpy构建出来的 公司中使用频繁的是DataFrame,而Series是构成DataFrame的基础,即一个DataFrame可能由N个Series构成 - 集成时间序列功能 - 提供丰富的数学运算和操作(基于Numpy) - 灵活处理缺失数据
安装方法:
# python环境下
pip install pandas
# anaconda环境下
conda install pandas
'''anaconda已经自动帮助我们下载好了数据分析相关的模块,其实无需我们再下载'''
导入方法
import pandas as pd
掌握目标
1. 掌握外部数据的读取 2. 如何快速地认知数据的概览信息 3. 数据子集的筛选与清洗 4. 数据的汇总处理 5. 数据的合并与连接
常用pandas解析函数
pandas提供了一些用于将表格型数据读取为DataFrame对象的函数
函数 | 描述 | |
---|---|---|
read_csv | 从文件、url或者文件型对象读取分割好的数据,逗号是默认分隔符 | |
read_table | 从文件、url或者文件型对象读取分割好的数据,制表符('\t')是默认分隔符 | |
read_fwf | 读取定宽格式数据(无分隔符) | |
read_clipboard | 读取剪贴板中的数据,可以看做read_table的剪贴板。再将网页转换为表格 | |
read_excel | 从Excel的XLS或者XLSX文件中读取表格数据 | |
read_hdf | 读取pandas写的HDF5文件 | |
read_html | 从HTML文件中读取所有表格数据 | |
read_json | 从json字符串中读取数据 | |
read_pickle | 从Python pickle格式中存储的任意对象 | |
read_msgpack | 二进制格式编码的pandas数据 | |
read_sas | 读取存储于sas系统自定义存储格式的SAS数据集 | |
read_stata | 读取Stata文件格式的数据集 | |
read_feather | 读取Feather二进制文件格式 | |
read_sql | 将SQL查询的结果(SQLAlchemy)读取为pandas的DataFrame |
2.数据结构之Series
# 是一种类似于一维数组对象,由数据和相关的标签(索引)组成 # 左侧是行标签,右侧是真实数据 第一种: pd.Series([4,5,6,7,8]) 第二种: pd.Series([4,5,6,7,8],index=['a','b','c','d','e']) a 4 b 5 c 6 d 7 e 8 dtype: int64 第三种: pd.Series({"a":1,"b":2}) 第四种: pd.Series(0,index=['a','b','c'])
2.1 Series缺失数据概念
st = {"tony":18,"yang":19,"bella":20,"cloud":21} obj = pd.Series(st) obj new_st = {'tony','yang','cloud','jason'} # new_st obj1 = pd.Series(st,index=new_st) obj1 # 这么写就会出现缺失数据 # tony 18.0 # jason NaN # cloud 21.0 # yang 19.0 # dtype: float64 """NaN表示缺失数据,本身属于浮点型""" # 下面的方法就没有缺失数据 obj.index = new_st obj 小疑问:为什么运算完之后数据类型会由原来的int64变成float64? 因为NaN其实是float类型 type(np.nan) 结果是:float
2.2 处理缺失数据
dropna() # 过滤掉值为NaN的行 fillna() # 填充缺失数据 isnull() # 返回布尔数组,判断缺失值 notnull() # 返回布尔数组,取反:不是缺失值 补充: 对数据进行处理返回一个新的结果原数据不变,如果想要直接影响原数据需要加参数inplace=True 举例:根据2.1 obj1.isnull() # tony False # jason True # cloud False # yang False # dtype: bool obj1.notnull() # tony True # jason False # cloud True # yang True # dtype: bool obj1.fillna(value=666) # tony 18.0 # jason 666.0 # cloud 21.0 # yang 19.0 # dtype: float64 """
在notebook环境下,如果一个编辑命令有结果,说明该编辑命令没有修改原数据 如果一个修改命令没有结果,说明修改的是原数据
如果你想直接修改源数据,可以加一个苦丁的参数inplace=True
obj1.fillna(666,inplace=True)
obj1
# tony 18.0
# jason 666.0
# cloud 21.0
# yang 19.0
# dtype: float6
""" obj1.dropna() # tony 18.0 # cloud 21.0 # yang 19.0 # dtype: float64
2.3 布尔选择器
mask = pd.Series([True,False,False,True,False]) price = pd.Series([321312,123,324,5654,645]) # price[mask] # 拿出True # price|mask #或 # price&mask #逻辑运算 '''涉及到逻辑运算符,符号左右两边的条件必须加上括号''' (price>200) & (price<900) # 逻辑运算 price[(price>200) & (price<900)] # 布尔求值
2.4 索引
s1 = pd.Series([321312,123,324,5654,645]) s1[0] # 321212 0不是索引值,是行标签或者叫行索引 # s2= pd.Series([321312,123,324,5654,645],index=[1,2,3,4,5]) # s2[0] # 报错,行标签没有索引0 s2= pd.Series([321312,123,324,5654,645],index=['a','b','c','d','e']) s2['b'] # 行标签 s2[0] # 行索引:当左侧行索引不是数字时,可以支持从0开始的索引值 sr = pd.Series(np.arange(10)) sr1 = sr[3:] sr1 # sr1[1] # 报错 """ 为了能够自己主动区分到底是用左侧的标签名还是索引值 """ sr1 #使用索引取值 sr1.iloc[0] #3 以索引下标解释 # 使用行标签 sr1.loc[3] #3,不看左侧标签了,直接按数字0标签开始取值
2.5 数据操作-增删改查
res = pd.Series([11,22,33,44,55,66]) # 增加一行数据 res['cxiong']=18 # 修改 res.iloc[0] = 1111 # 查询 # res.loc[1] # res.iloc[1:3] # 数据纵向合并 # res1=pd.Series(['a','b','c','d']) # res.append(res1) # 删除 del res[0]
2.6 基本算术方法
""" add 加 sub 减 div 除 mul 乘 """ sr1 = pd.Series([12,23,34], index=['c','a','d']) sr3 = pd.Series([11,20,10,14], index=['d','c','a','b']) sr1.add(sr3,fill_value=0) # 没有对象进行填充 # a 33.0 # b 14.0 # c 32.0 # d 45.0 # dtype: float64
3. 数据结构之DataFrame
# 表格型数据结构,相当于一个二维数组,含有一组有序的列也可以看作是由Series组成的共用一个索引的字典 # DataFrame就是表格型数据 # 创建DataFrame的方式 第一种: res = pd.DataFrame({'one':[1,2,3,4],'two':[4,3,2,1]}) 第二种: pd.DataFrame({'one':pd.Series([1,2,3],index=['a','b','c']),'two':pd.Series([1,2,3],index=['b','a','c'])}) 第三种: pd.DataFrame(np.array([10,20],[30,40]),index=['a','b'],columns=['c1','c2']) 更多 pd.DataFrame([np.arange(1,8),np.arange(11,18)]) s1 = pd.Series(np.arange(1,9,2)) s2 = pd.Series(np.arange(2,10,2)) s3 = pd.Series(np.arange(5,7),index=[1,2]) df5 = pd.DataFrame({'c1':s1,'c2':s2,'c3':s3}) """
以上创建方式都仅仅做一个了解即可 因为工作中dataframe的数据一般都是来自于读取外部文件数据,而不是自己手动去创建
外部文件:1.普通文本文件 2.excel文件(10W行以上用这个) 3.关系型数据库表数据 """
4.DataFrame常用属性
""" 1.index 行索引 2.columns 列索引 3.T 转置 4.values 值索引 5.describe 快速统计 """ res = pd.DataFrame({'one':[1,2,3,4],'two':[4,3,2,1]}) # 查看表行索引 res.index # 查询列字段 res.columns # 字符串类型在DataFrame中用object表示 # 修改行索引 res.index=[11,22,33,44] # 修改列字段 res.columns=['id','uid'] # 转置:列名称变为行索引,行索引变为列名称 res.T # 获取表单数据(不要行索引和列名称) res.values # 结果 # array([[1, 4], # [2, 3], # [3, 2], # [4, 1]], dtype=int64) # 快速统计: res.describe() #默认只统计数值类型 # 当res不是数字类型 res= pd.DataFrame({'username':['xc','cx','xc','kk'],'pwd':[123,321,222,333]}) res.describe(include='all') # res.describe(include='object/all/int')
5. 外部文件读取
5.1数据读取之read_csv
# # 读取text文件和csv 结尾的文件 pd.read_csv(filepath_or_buffer, sep=‘,', header='infer', names=None, usecols=None, skiprows=None, skipfooter=None, converters=None, encoding=None) read_csv参数: filepath_or_buffer:指定txt文件或csv文件所在的具体路径 sep:指定原数据集中各字段之间的分隔符,默认为逗号”,” id name income 1 cxiong 10 header:是否需要将原数据集中的第一行作为表头,默认将第一行用作字段名称 如果原始数据没有表头需要将该参数设置为None names:如果原数据集中没有字段,可以通过该参数在数据读取时给数据框添加具体的表头 usecols:指定需要读取原数据集中的哪些变量名 skiprows:数据读取时,指定需要跳过原数据集开头的行数 有一些表格开头是有几行文字说明的,读取的时候应该跳过 skipfooter:数据读取时,指定需要跳过原数据集末尾的行数 converters:用于数据类型的转换(以字典的形式指定) encoding:如果文件中含有中文,有时需要指定字符编码
举例:
import pandas as pd # 读取文本文件和CSV,空白行自动过滤 df1 = pd.read_csv(r'data_test01.txt', # 读取数据时跳过开头2行 skiprows=2, # 读取数据时跳过末尾3行 skipfooter=3, # 指定字符编码 encoding='utf8', # 不将第1行作为列名 header = None, # 自定义列名 names = ['id','year','month','day','gender','occupation','income'], # 声明数据类型 converters = {'id':str}, #将指定千分位在这个表格里用什么表示,移除千分位符号 thousands='&', usecols=['occupation','income'] ) df1
5.2数据读取之read_excel
# excel表格读取 pd.read_excel(io, sheetname=0, header=0, skiprows=None, skip_footer=0, index_col=None, names=None, na_values=None, thousands=None, convert_float=True) io:指定电子表格的具体路径 sheetname:指定需要读取电子表格中的第几个Sheet,既可以传递整数也可以传递具体的Sheet名称 header:是否需要将数据集的第一行用作表头,默认为是需要的 skiprows:读取数据时,指定跳过的开始行数 skip_footer:读取数据时,指定跳过的末尾行数 index_col:指定哪些列用作数据框的行索引(标签) na_values:指定原始数据中哪些特殊值代表了缺失值 thousands:指定原始数据集中的千分位符 convert_float:默认将所有的数值型字段转换为浮点型字段 converters:通过字典的形式,指定某些列需要转换的形式
# 读取xls,xlsx表格文件 df2 = pd.read_excel(r'data_test02.xlsx', header= None, names =['id','class','color','count'], converters = {'id':str}, ) df2.index df2.columns df2['class'] # class是关键字 df2.color df2
5.3 数据读取之read_sql
# # 读取关系型数据库表数据 ''' 在notebook内如何下载第三方模块 !pip install pymysql ''' # 在anaconda环境下直接安装模块 !conda install pymysql # pymysql模块 import pymysql conn = pymysql.connect(host,port,user,password, database, charset) host:指定需要访问的MySQL服务器 port:指定访问MySQL数据库的端口号 charset:指定读取MySQL数据库的字符集,如果数据库表中含有中文,一般可以尝试将该参数设置为 “utf8”或“gbk” user:指定访问MySQL数据库的用户名 password:指定访问MySQL数据库的密码 database:指定访问MySQL数据库的具体库名 # 利用pymysql创建好链接MySQL的链接之后即可通过该链接操作MySQL pd.read_sql('select * from user', con = conn) conn.close() # 关闭链接
5.4 网页数据获取read_html
# read_html可以直接获取到页面上table标签里的数据 res = pd.read_html('https://baike.baidu.com/item/NBA%E6%80%BB%E5%86%A0%E5%86%9B/2173192?fr=aladdin') res
6.DataFrame数据概览
import pandas as pd df3 = pd.read_csv(r'sec_cars.csv') df3.index df3.columns df3.shape # 元组(行数,列数) df3.dtypes # 每个字段存储的数据类型 df3.describe() # 快速统计 df3.head() # 默认前5行 df3.tail(5) #默认后5行
6.1 数据概览之行列操作
# 修改列名称 df3.rename(columns={'Brand':'品牌'}) # 创建列 df3['haha']= 666 # 删除列 df3.drop(columns=['haha',],axis=1,inplace=True) # 自定义列位置 # df3.insert(0,'NB',123) # df3.insert(0,'计算',df3['Km(W)']*df3['Sec_price']) df3.drop(columns=['计算','NB'],axis=1,inplace=True)
6.2 数据筛选
# 获取指定列的数据 #指定单列数据 # df3['Name'] # 方式二:一定要避免与关键字和内置方法名冲突 # df3.Name # 获取多列数据 df3[['Name','Sec_price']] # 获取行数据 df3[0:10] # 1.获取brand是众泰的 df3.loc[df3['Brand']=='众泰',] # 2.获取brand是DS并且Discharge是国4 的 df3.loc[(df3['Brand']=='DS')&(df3['Discharge']=='国4')] # 3.获取brand是众泰并且Discharge是国4 的数据的车名和新车价格 df3.loc[(df3['Brand']=='众泰') & (df3['Discharge']=='国4'),['Name','New_price']]
"""知识回顾:逻辑运算符链接条件 条件最好用括号括起来"""
6.3 数据处理
# 1.日期处理:2021年1月25日转为 2021-1-25 # 日期类型转换 df3['Boarding_time'] = pd.to_datetime(df3['Boarding_time'], format = '%Y年%m月') # 2.字符串转数字:利用字符串切片:22.95万 -->22.95 # .astype转换类型 df3['New_price']= df3['New_price'].str[:-1].astype('float')
# 3.判断数据是否重复并进行处理
df4=pd.read_excel(r'data_test04.xlsx') df4.head() # 判断数据是否重复,如果想单独判断某列是否有重复需要加参数subset df4.duplicated() # 针对重复数据进行删除 df4.drop_duplicates()
6.4 异常值识别与处理
异常值的识别与处理 1. Z得分法 2. 分位数法 3. 距离法
缺失值处理:
缺失值的识别与处理
1. df.isnull
2. df.fillna
3. df.dropna
data05 = pd.read_excel(r'data_test05.xlsx') # data05.isnull() # 默认整体挨个判断是否缺失 data05.isnull().any() # 可以看那几个字段有缺失数据 # 计算缺失数据的比例 data05.isnull().sum(axis = 0)/data05.shape[0] # uid 0.0 # regit_date 0.0 # gender 0.1 # age 0.3 # income 0.2 # dtype: float64 """ 针对缺失数据: 1.删除缺失数据(要看缺失比例) 2.填充(根据不同的数据采用不能的填充策略) 1.针对收入采用中位数 2.年龄采用平均数(男均和女均) 3.针对性别采用众数 4. k近龄 """ data05.fillna(value = { 'gender':data05.gender.mode()[0], # 众数:可以有一个也可能是多个 'age':data05.age.mean(), # 平均值 'income':data05.income.median() # 中位数 }, inplace = True)
6.5.数据透视表
# 透视表功能 pd.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All') data:指定需要构造透视表的数据集 values:指定需要拉入“数值”框的字段列表 index:指定需要拉入“行标签”框的字段列表 columns:指定需要拉入“列标签”框的字段列表 aggfunc:指定数值的统计函数,默认为统计均值,也可以指定numpy模块中的其他统计函数 fill_value:指定一个标量,用于填充缺失值 margins:bool类型参数,是否需要显示行或列的总计值,默认为False dropna:bool类型参数,是否需要删除整列为缺失的字段,默认为True margins_name:指定行或列的总计名称,默认为All
例如: data06 = pd.read_csv(r'diamonds.csv') data06.shape data06.head() pd.pivot_table(data06, index = 'color', values='price', aggfunc='mean') pd.pivot_table(data06, index = 'color', columns='clarity', values='price', aggfunc='size')
6.6 分组与聚合
import numpy as np # 单个字段分组 .groups直接输出结果 # data06.groupby(by = ['color']).groups # 通过groupby方法,指定分组变量,先按颜色color分组,再按切片分组 grouped = data06.groupby(by = ['color','cut']) # 对分组变量进行统计汇总 result = grouped.aggregate({'color':np.size, 'carat':np.min, 'price':np.mean, 'table':np.max}) # 调整字段变量名的顺序 result = pd.DataFrame(result, columns=['color','carat','price','table']) # 数据集重命名 result.rename(columns={'color':'counts', 'carat':'min_weight', 'price':'avg_price', 'table':'max_table'}, inplace=True)
实战练习
# 分析NBA各球队冠军次数及球员FMVP次数
res = pd.read_html('https://baike.baidu.com/item/NBA%E6%80%BB%E5%86%A0%E5%86%9B/2173192?fr=aladdin') ### 返回的是一个列表 列表中是当前页面的所有表格数据 type(res) res # 获取有效数据 champion = res[0] champion # 针对冠军字段分组
# ch.groupby(['冠军']).aggregate({'冠军':np.size}).rename(columns={'冠军':"次数"})
champion.groupby('冠军').groups # 获取分组之后的各分组大小 champion.groupby('冠军').size() # 获取各组冠军次数 champion.groupby('冠军').size().sort_values(ascending=False) # 升序 # 分组字段可以一次性取多个 champion.groupby(['冠军', 'FMVP']).size()
6.7 数据合并
# 纵向合并
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None) objs:指定需要合并的对象,可以是序列、数据框或面板数据构成的列表 axis:指定数据合并的轴,默认为0,表示合并多个数据的行,如果为1,就表示合并多个数据的列 join:指定合并的方式,默认为outer,表示合并所有数据,如果改为inner,表示合并公共部分的数据 join_axes:合并数据后,指定保留的数据轴 ignore_index:bool类型的参数,表示是否忽略原数据集的索引,默认为False,如果设为True,就表示忽略原索引并生成新索引 keys:为合并后的数据添加新索引,用于区分各个数据部分
# 构造数据集df1和df2 df1 = pd.DataFrame({ 'name':['张三','李四','王二'], 'age':[21,25,22], 'gender':['男','女','男']} ) df2 = pd.DataFrame({ 'name':['丁一','赵五'], 'age':[23,22], 'gender':['女','女']} ) # 1.数据集的纵向合并 pd.concat([df1,df2] , keys = ['df1','df2']) # 加keys参数可以在合并之后看到数据来源 # 重置数据的行索引:reset_index() pd.concat([df1,df2] , keys = ['df1','df2']).reset_index() # 删除数据的老索引 pd.concat([df1,df2] , keys = ['df1','df2']).reset_index().drop(labels ='level_1', axis = 1).rename(columns = {'level_0':'Class'}) # 如果df2数据集中的“姓名变量为Name” # 列名不一致会导致多出一列,并且出现NaN数据 df2 = pd.DataFrame({ 'Name':['丁一','赵五'], 'age':[23,22], 'gender':['女','女']} ) # 数据集的纵向合并 pd.concat([df1,df2]) # concat行合并,数据源的变量名称要完全相同(变量名顺序没有要求)
2.数据的横向合并
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y')) left:指定需要连接的主 right:指定需要连接的辅表 how:指定连接方式,默认为inner内连,还有其他选项,如左连left、右连right和外连outer on:指定连接两张表的共同字段 left_on:指定主表中需要连接的共同字段 right_on:指定辅表中需要连接的共同字段 left_index:bool类型参数,是否将主表中的行索引用作表连接的共同字段,默认为False right_index:bool类型参数,是否将辅表中的行索引用作表连接的共同字段,默认为False sort:bool类型参数,是否对连接后的数据按照共同字段排序,默认为False suffixes:如果数据连接的结果中存在重叠的变量名,则使用各自的前缀进行区分
# 数据的横向合并(inner/left/right join)
# 构造数据集 df3 = pd.DataFrame({ 'id':[1,2,3,4,5], 'name':['张三','李四','王二','丁一','赵五'], 'age':[27,24,25,23,25], 'gender':['男','男','男','女','女']}) df4 = pd.DataFrame({ 'Id':[1,2,2,4,4,4,5], 'score':[83,81,87,75,86,74,88], 'kemu':['科目1','科目1','科目2','科目1','科目2','科目3','科目1']}) df5 = pd.DataFrame({ 'id':[1,3,5], 'name':['张三','王二','赵五'], 'income':[13500,18000,15000]}) # 首先df3和df4连接 merge1 = pd.merge(left = df3, right = df4, how = 'left', left_on='id', right_on='Id') # 再将连接结果与df5连接 merge2 = pd.merge(left = merge1, right = df5, how = 'left')