文章目录
pandas,python+data+analysis的组合缩写,是python中基于numpy和matplotlib的第三方数据分析库,与后两者共同构成了python数据分析的基础工具包,享有数分三剑客之名。
pandas与numpy的关系不是替代,而是互为补充。二者之间主要区别是:
从数据结构上看:
numpy的核心数据结构是ndarray,支持任意维数的数组,但要求单个数组内所有数据是同质的,即类型必须相同;而pandas的核心数据结构是series和dataframe,仅支持一维和二维数据,但数据内部可以是异构数据,仅要求同列数据类型一致即可
numpy的数据结构仅支持数字索引,而pandas数据结构则同时支持数字索引和标签索引
从功能定位上看:
numpy虽然也支持字符串等其他数据类型,但仍然主要是用于数值计算,尤其是内部集成了大量矩阵计算模块,例如基本的矩阵运算、线性代数、fft、生成随机数等,支持灵活的广播机制
pandas主要用于数据处理与分析,支持包括数据读写、数值计算、数据处理、数据分析和数据可视化全套流程操作
pandas主要面向数据处理与分析,主要具有以下功能特色:
按索引匹配的广播机制,这里的广播机制与numpy广播机制还有很大不同
便捷的数据读写操作,相比于numpy仅支持数字索引,pandas的两种数据结构均支持标签索引,包括bool索引也是支持的
类比SQL的join和groupby功能,pandas可以很容易实现SQL这两个核心功能,实际上,SQL的绝大部分DQL和DML操作在pandas中都可以实现
类比Excel的数据透视表功能,Excel中最为强大的数据分析工具之一是数据透视表,这在pandas中也可轻松实现
自带正则表达式的字符串向量化操作,对pandas中的一列字符串进行通函数操作,而且自带正则表达式的大部分接口
丰富的时间序列向量化处理接口
常用的数据分析与统计功能,包括基本统计量、分组统计分析等
集成matplotlib的常用可视化接口,无论是series还是dataframe,均支持面向对象的绘图接口
pandas核心数据结构有两种,即一维的series和二维的dataframe,二者可以分别看做是在numpy一维数组和二维数组的基础上增加了相应的标签信息。正因如此,可以从两个角度理解series和dataframe:
series和dataframe分别是一维和二维数组,因为是数组,所以numpy中关于数组的用法基本可以直接应用到这两个数据结构,包括数据创建、切片访问、通函数、广播机制等
series是带标签的一维数组,所以还可以看做是类字典结构:标签是key,取值是value;而dataframe则可以看做是嵌套字典结构,其中列名是key,每一列的series是value。所以从这个角度讲,pandas数据创建的一种灵活方式就是通过字典或者嵌套字典,同时也自然衍生出了适用于series和dataframe的类似字典访问的接口,即通过loc索引访问。
数据结构Series
# 是一种类似于一维数组对象,由数据和相关的标签(索引)组成
# 第一种:默认左侧标签是从0开始的索引值
pd.Series([4,5,6,7,8])
0 4
1 5
2 6
3 7
4 8
dtype: int64
# 第二种:还可以自定义左侧标签值
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
# 第三种:字典的key会自动变成左侧标签 值会变成数据
pd.Series({"a":1,"b":2})
a 1
b 2
dtype: int64
# 第四种:快速生成一个数据为0的Series结构
pd.Series(0,index=['a','b','c'])
a 0
b 0
c 0
dtype: int64
缺失值概念
st = {"tony":18,"yang":19,"bella":20,"cloud":21}
obj = pd.Series(st)
tony 18
yang 19
bella 20
cloud 21
dtype: int64
# 定义新索引
new_st = {'tony','yang','cloud','jason'}
# 修改原索引 字典有key做左侧标签了又人为指定了索引值
obj1 = pd.Series(st,index=new_st)
jason NaN
cloud 21.0
yang 19.0
tony 18.0
dtype: float64
'''
NaN是一个关键字,就是用来表示缺失值的意思
并且NaN属于浮点类型数据
'''
缺失值处理
dropna() # 过滤掉值为NaN的行
fillna() # 填充缺失数据
isnull() # 返回布尔数组
notnull() # 返回布尔数组
对数据进行处理返回一个新的结果原数据不变,如果想要直接影响原数据需要加参数inplace=True
布尔选择器
# 布尔值索引使用方式与numpy一致
# 布尔值索引还可以结合逻辑运算符
|或
&与
'''注意:逻辑运算符连接的条件最好都加上括号'''
# 筛选出价格大于200小于900的价格
(price>200) & (price<900)
索引
se = pd.Series([111,222,333,444],index=['ab','bc','cd','ef'])
ab 111
bc 222
cd 333
ef 444
dtype: int64
price = pd.Series([111,222,333,444],index=[1,2,3,4])
1 111
2 222
3 333
4 444
dtype: int64
# 通过索引值获取元素
se[1]
# 通过左侧标签名获取元素
se['ab']
# 如果左侧标签是数字那么会优先采用左侧标签名获取数据
price[1]
# 获取左侧所有的标签名
se.index
Index(['ab', 'bc', 'cd', 'ef'], dtype='object')
'''在pandas中字符串类型用object表示'''
########################################################
sr = pd.Series(np.arange(6))
0 0
1 1
2 2
3 3
4 4
5 5
res = sr[3:]
3 3
4 4
5 5
# 如果左侧标签是数字那么会优先采用左侧标签名获取数据
res[1] # 报错
*******************************************
'''自定义取值方式'''
res.loc[3] # 按照左侧标签名取值
res.iloc[1] # 按照从0开始的索引值依次往下取值
数据处理
res = pd.Series([11,22,33])
0 11
1 22
2 33
dtype: int64
res.iloc[1] = 777 # 修改已经存在的值
0 11
1 777
2 33
dtype: int64
res['a'] = 111 # 如果左侧标签不存在则新增一个
0 11
1 777
2 33
a 111
dtype: int64
res.append(pd.Series([99])) # 纵向合并Series
0 11
1 777
2 33
a 111
0 99
dtype: int64
del res[0]
1 777
2 33
a 111
dtype: int64
基本算术运算
"""
.add 加
.sub 减
.mul 乘
.div 除
"""
sr1 = pd.Series([12,23,34], index=['c','a','d'])
c 12
a 23
d 34
dtype: int64
sr3 = pd.Series([11,20,10,14], index=['d','c','a','b'])
d 11
c 20
a 10
b 14
dtype: int64
sr1.add(sr3) # 会按照左侧标签对应做运算,没有对应的直接NaN
a 33.0
b NaN
c 32.0
d 45.0
dtype: float64
sr1.add(sr3,fill_value=0) # 计算之前将没有对应的标签数据设为0
数据结构之DataFrame
表格型数据结构,相当于一个二维数组,含有一组有序的列也可以看作是由Series组成的共用一个索引的字典
第一种:
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的数据一般都是来自于读取外部excel文件
而不是自己手动去创建
"""
DataFrame常用方法
res = pd.DataFrame(
np.array([[10,20],[30,40]]),
index=['a','b'], # 自定义左侧标签名(行名称)
columns=['c1','c2']) # 自定义列名称
c1 c2
a 10 20
b 30 40
# 获取行索引(左侧标签名)
res.index # Index(['a', 'b'], dtype='object')
# 获取列名称
res.columns # Index(['c1', 'c2'], dtype='object')
# 行列互换
res.T
a b
c1 10 30
c2 20 40
# 获取表格中的数据 数组对象
res.values
array([[10, 20],
[30, 40]])
# 快速统计(默认只能统计数字)
res.describe()
res.describe(include='object') # 指定只统计字符串
数据读取方式1
pd.read_csv(filepath_or_buffer, sep=',', header='infer', names=None, usecols=None, skiprows=None, skipfooter=None, converters=None, encoding=None)
filepath_or_buffer:指定txt文件或csv文件所在的具体路径
sep:指定原数据集中各字段之间的分隔符,默认为逗号”,”
id name income
1 J 10
header:是否需要将原数据集中的第一行作为列名称,默认将第一行用作列名称
如果原始数据没有表头需要将该参数设置为None
names:如果原数据集中没有列名称,可以通过该参数在数据读取时给数据框添加具体的列名称
usecols:指定需要读取原数据集中的哪些变量名
skiprows:数据读取时,指定需要跳过原数据集开头的行数
有一些表格开头是有几行文字说明的,读取的时候应该跳过
skipfooter:数据读取时,指定需要跳过原数据集末尾的行数
converters:用于数据类型的转换(以字典的形式指定)
encoding:如果文件中含有中文,有时需要指定字符编码
案例
import pandas as pd
data01 = pd.read_csv(r'data_test01.txt',
skiprows = 2, # python能自动过滤掉完全无内容的空行(写2、3都行)
sep = ',', # 默认就是逗号 写不写都行
skipfooter = 3,
)
# 1.针对id原本是01、02自动变成了1、2...
converters = {'id':str}
# 2.点击文件另存修改文件编码之后再次读取出现乱码
encoding='utf-8'
# 3.移除收入千分位非逗号的其他符号
thousands = '&'
# 4.手动将文件表头行删除再次读取
header = None # 默认用索引
names = ['id','year','month','day','gender','occupation','income']
# 5.指定读取的字段
usecols = ['id','income']
数据读取方式2
pd.read_excel(io, sheet_name=0, header=0, skiprows=None, skip_footer=0, index_col=None, names=None,
na_values=None, thousands=None, convert_float=True)
io:指定电子表格的具体路径
sheet_name:指定需要读取电子表格中的第几个Sheet,既可以传递整数也可以传递具体的Sheet名称
header:是否需要将数据集的第一行用作表头,默认为是需要的
skiprows:读取数据时,指定跳过的开始行数
skip_footer:读取数据时,指定跳过的末尾行数
index_col:指定哪些列用作数据框的行索引(标签)
na_values:指定原始数据中哪些特殊值代表了缺失值
thousands:指定原始数据集中的千分位符
convert_float:默认将所有的数值型字段转换为浮点型字段
converters:通过字典的形式,指定某些列需要转换的形式
pd.read_excel(r'data_test02.xlsx',
sheet_name='***',
header = None,
names = ['ID','Product','Color','Size'],
converters = {'ID':str}
)
数据读取方式3
"""
截至目前为止我们总共学习了三种存储数据的方式
1.文本文件
2.表格文件
3.MySQL数据库
"""
要想读取MySQL里面的数据,首先需要跳过代码连接到MySQL服务端
需要借助于python模块pymysql
下载
!pip3 install pymysql
!conda install pymysql
# 可能会出现报错 切换到国内源
导入使用
import pymysql
# 链接MySQL服务端
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='db3',
charset='utf8'
)
# 利用pymysql创建好链接MySQL的链接之后即可通过该链接操作MySQL
pd.read_sql('select * from userinfo', con = conn)
数据概览
df = pd.read_csv(r'sec_cars.csv')
# 1.查看表格的前N条数据
df.head(5) # 不写默认也是五条
# 2.查看表格的后N条数据
df.tail(5) # 不写默认也是五条
# 3.查看表的行列
df.shape # 结果是一个元组(行数,列数)
# 4.获取表中所有的列名称
df.columns
# 5.获取表中所有的行索引
df.index
# 6.查看数据的数据类型
df.dtypes # 在处理数据之前应该用该方法查看一下数据类型
# 7.快速统计
df.describe() # 默认只会统计数字类型的数据
df.describe(include='object') # 可以通过include参数指定统计的数据类型
df.describe(include='all')
行列操作
df1 = pd.read_excel(r'data_test02.xlsx',header=None)
# 1.修改列名称
df1.rename(columns={0:'序号'})
df1.rename(columns={0:'序号',1:'类型',2:'颜色',3:'尺码'},inplace=True)
'''
# 能修改 但是会报个错误 可以添加下列配置
pd.set_option('mode.chained_assignment',None)
'''
df1['序号'] # 获取序号列对应的列数据
df1['库存'] = 666 # 加了赋值符号就是设值
1.如果该列名称存在则修改列数据
2.如果该列名称不存在则创建新的
df1['测试'] = df1['尺码']*df1['库存']
# 注意:新的列数据可以是直接写死的,也可以是通过其他字段动态计算
df1.insert(0,'插队',6969)
# 还可以控制插入列的位置
数据筛选
# 以二手车为例
# 1.获取列数据
df['Brand'] # 只获取一列数据
df[['Brand','Name','New_price']] # 获取多列一定要是表格的形式
# 2.获取行数据
df.loc[df['Brand']=='众泰'] # 只有一个条件的
'''逻辑运算符在连接条件的时候 条件都必须加括号'''
df.loc[(df['Brand']=='众泰')&(df['Discharge']=='国4')]
# 3.针对筛选出来的行数据 做列数据的过滤
df.loc[(df['Brand']=='众泰')&(df['Discharge']=='国4'),['Brand','Name']]
数据处理
sec_car = pd.read_csv(r'sec_cars.csv')
sec_car.head()
sec_car.dtypes
# 日期应该是日期类型而不是字符串类型
# 新车价格应该是数字类型而不是字符串类型
sec_car['Boarding_time'] = pd.to_datetime(sec_car['Boarding_time'],
format='%Y年%m月'
)
# 常见的日期符号 %Y %m %d %H %M %S %X
sec_car.dtypes
sec_car.New_price # 新车价格里面有字符串类型
# 1.需要先将字符串去除(方式1:字符串的替换 方式2:切片操作)
sec_car.New_price.str[:-1] # 一定要加上关键字str
# 2.之后才能数据类型转换
sec_car.New_price.str[:-1].astype(float) # astype数据类型转换
sec_car['New_price'] = sec_car.New_price.str[:-1].astype(float)
data = pd.read_excel(r'data_test04.xlsx')
data.head()
# 判断数据是否有重复项
data.duplicated()
# 删除重复的数据项
data.drop_duplicates()
缺失值处理
data05 = pd.read_excel(r'data_test05.xlsx')
# 1.当样本数据量特别多的时候,可以考虑直接删除个别缺失数据
# 2.当缺失数据占比较高的时候不能直接删除,而应该使用填充数据的方式
# 判断是否含有缺失值
data05.isnull() # 不太好
data05.isnull().any(axis=0) # 判断列是否有缺失数据
"""
axis=0表示列字段
axis=1表示行索引
"""
# 计算缺失数据的占比
data05.isnull().sum(axis = 0)/data05.shape[1]
# 不能够直接删除缺失数据data05.dropna()
# 也不能一股脑的瞎填充data05.fillna(66)
############################################################
data05.fillna(value = {
'gender':data05.gender.mode()[0], # 众数:可以有一个也可能是多个
'age':data05.age.mean(), # 平均值
'income':data05.income.median() # 薪资推荐使用:中位数
}, inplace = True)
############################################################
data05
数据汇总
data06 = pd.read_csv(r'diamonds.csv')
pd.pivot_table(data06, index = 'color', values='price', aggfunc='mean')
pd.pivot_table(data06,
index = 'color',
columns='clarity',
values='price',
aggfunc='size')
# 当数据量小于10万 基本上使用常用软件excel SPSS即可操作
# 高于10万之后才可能需要使用代码处理
分组与聚合
# 对颜色进行分组
data06.groupby(by=['color',]) # 按照单个字段分组
# 分组之后如果直接想看结果可以使用groups 粗略的查看
data06.groupby(by=['color',]).groups
# 分组一般都是配合聚合函数一起使用
gg = data06.groupby(by=['color',]) # 先分组
res = gg.aggregate({'color':np.size}) # 再聚合
res
# 可以对多个字段聚合 针对聚合函数最好都用np点出来
res1 = gg.aggregate({'color':np.size,
'price':np.mean,
'table':np.max
})
res1
# 还可以按照多个字段分组
######################################
gg1 = data06.groupby(by=['color','cut'])
res2 = gg1.aggregate({'color':np.size,
'price':np.mean,
'table':np.max
})
res2
######################################
# 调整变量名的顺序
result = pd.DataFrame(res2, columns=['price','color','table'])
result
# 数据集重命名
result.rename(columns={'color':'个数',
'price':'均价',
'table':'最大面积'},
inplace=True)
result
练习
# pandas中的read_html可以爬取页面上table标签里面的数据
cp = pd.read_html(r'https://baike.baidu.com/item/NBA%E6%80%BB%E5%86%A0%E5%86%9B/2173192?fr=aladdin')
# 结果是一个列表数据类型 我们只需要第一个元素
ch = cp[0]
# 列名称数据处理
ch.columns = ch.loc[0] # 将第一行数据替换成列名称
# 将第一行数据删除
ch.drop(axis=0,index=0,inplace=True)
"""
这里规律相反
axis=0表示行
axis=1表示列
如果记不住以后直接先查看该方法的案例提示再操作
"""
# 1.统计每个球队夺冠的次数
cpm = ch.groupby(by=['冠军',]) # 先分组
res = cpm.aggregate({"冠军":np.size})
res.rename(columns={'冠军':'夺冠次数'},inplace=True)
# 对夺冠次数进行排序
res['夺冠次数'].sort_values(ascending=False)
# 降序
cpm1 = ch.groupby(by=['冠军','FMVP'])
res1 = cpm1.aggregate({
'FMVP':np.size,
})
数据的纵向合并(添加数据项)
df1 = pd.DataFrame({
'name':['张三','李四','王二'],
'age':[21,25,22],
'gender':['男','女','男']}
)
df2 = pd.DataFrame({
'name':['丁一','赵五'],
'age':[23,22],
'gender':['女','女']}
)
pd.concat([df1,df2])
pd.concat([df1,df2] , keys = ['df1','df2'])
pd.concat([df1,df2]).reset_index().drop(columns=['index'])
# reset_index 生成一个从0开始的新的索引项
df2 = pd.DataFrame({
'Name':['丁一','赵五'],
'age':[23,22],
'gender':['女','女']}
)
****
变量名称必须相同
****
数据的横向合并(链表操作)
"""
dep
id name desc
emp
id name gender dep_id
select * from emp inner join dep on emp.dep_id = dep.id;
"""
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]})
merge1 = pd.merge(left = df3,
right = df4,
how = 'inner',
left_on='id',
right_on='Id')
merge2 = pd.merge(left = merge1,
right = df5,
how = 'left') # 不指定默认找相同的字段