首页 > 技术文章 > 数据分析之重要模块pandas

yangmeichong 2021-01-25 16:37 原文

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')

 

推荐阅读