文章目录
- 1 认识Pandas
- 2 pandas常用数据结构
- 2.1 Series
- 2.1.1 Series创建
- 2.1.2 数据类型转换
- 2.1.3 查看Series对象的属性
- 2.1.4 预览数据head、tail
- 2.1.5 通过索引获取数据
- 2.2 DataFrame
- 2.2.1 创建DataFrame对象
- 2.2.2 获取行、列、值
- 2.2.3 数据预览
- 2.2.4 通过索引获取数据
- 2.2.5 增加列数据
- 2.2.6 删除
- 2.2.7 修改数据
- 2.2.8 index 对象
- 3 索引操作
- 3.1 Series索引操作
- 3.2 DataFrame的索引操作
- 4 算术运算与函数应用
- 4.1 运算与对齐
- 4.1.1 Series 相加
- 4.1.2 DataFrame相加
- 4.1.3 Series和DataFrame之间的运算
- 4.2 常见函数应用
- 4.2.1 map函数应用
- 4.2.2 apply函数应用
- 4.2.3 applymap函数应用
- 4.2.4 索引排序
- 4.2.5 值排序
- 4.2.6 描述性统计与计算
- 5 数据清洗
- 5.1 处理缺失数据
- 5.1.1 判断缺失值
- 5.1.2 丢弃缺失值
- 5.1.3 填充缺失值
- 5.2 处理重复数据
- 5.2.1 判断是否存在重复数据
- 5.2.2 删除重复值
- 5.3 替换数据
- 6 分组和聚合
- 6.1 数据分组
- 6.2 数据聚合
- 7 数据规整
- 7.1 层级索引
- 7.1.1 设置层级索引
- 7.1.2 获取数据子集
- 7.1.3交换层级顺序
- 7.1.4 层级索引排序
- 7.2 数据合并
- 7.3 数据连接
1 认识Pandas
在此只简单介绍matplotlib一些常用操作,当作小字典工具使用就可,具体详细内容请转到matplotlib中文网
扩展库pandas是基于numpy和matplotlib的数据分析模块。它提供了大量标准数据模型、高效操作大型数据集所需的工具以及快速便捷处理数据的函数和方法。扩展库pandas是使Python成为强大且高效的数据分析首选语言的重要因素之一。
扩展库numpy是Python中进行科学计算的基础包,扩展库pandas需要numpy的支持。二者在功能和使用上存在一定区别。
- numpy中的N维数组对象ndarray用于处理多维数值型数组,重点用于数值运算,ndarray数组没有索引;pandas中的一维数组对象Series和多维数组对象DataFrame都有索引,方便进行数据查询和筛选等,pandas重点用于数据分析;
- 在数学与统计方法上,numpy中的ndarray只能进行数值型统计,而pandas中的DataFrame可以容纳不同数据类型,既可以进行数值型统计,也可以进行非数值型统计。
import numpy as np # pandas的使用基础是numpy,所以先导入numpy
import pandas as pd
2 pandas常用数据结构
pandas提供了两种常用数据结构:Series
和DataFrame
-
Series适用于一维数据;
-
DataFrame适用于多维数据;
它们是在numpy数组对象ndarray的基础上加入索引形成的高级数据结构。
2.1 Series
Series是pandas提供的一维数组,由值和索引两部分构成,类似于字典与numpy一维数据的结合。
Series的值可以是不同类型的数据,如布尔型、字符串、数字类型等。
创建Series对象时,可以指定Series对象的索引,也可以自动生成索引。Series对象默认索引是从0开始的非负整数。
2.1.1 Series创建
创建Series对象,格式如下:
pd.Series(data=None,index=None,dtype=None,name=None,copy=False,fastpath=False)
# 参数data可以是列表、元组、字典、numpy数组等类型的数据,用于创建一个Series对象。
# 参数index指定Series对象的索引。如果创建Series对象时没有指定索引,默认生成的索引是从0到N-1(N是值的长度)的数值。
# 可选参数name用于给Series对象命名,默认值为None
# 可选参数dtype为Series对象的值指定数据类型,默认值为None。
#通过list创建Series对象
ser_obj1=pd.Series([1,2,-3,4]) #创建简单的Series对象
print(ser_obj1) # series对象以 数据在右、索引在左的形式显示
"""
0 1
1 2
2 -3
3 4
dtype: int64
"""
tup=(1,2,3) # 通过元组创建Series对象
s=pd.Series(tup,name="元组")
print(s) # 不指定index, 则默认index为[0,1,len(s)-1]
"""
0 1
1 2
2 3
Name: 元组, dtype: int64
"""
# 创建一个series对象并指定索引
ser_obj2=pd.Series(np.random.rand(5),index=['a','b','c','d','e'])
print(ser_obj2)
"""
a 0.247488
b 0.298460
c 0.048297
d 0.353809
e 0.100917
dtype: float64
"""
#通过字典创建Series对象
ser_obj3=pd.Series({'apple':20,'orange':30,'banana':10})
ser_obj3
"""
apple 20
orange 30
banana 10
dtype: int64
"""
2.1.2 数据类型转换
将Python基本数据类型的数据转换为一个Series对象
s=pd.Series(10)
print(s)
"""
0 10
dtype: int64
"""
pd.Series(10, dtype=np.float64)
"""
0 10.0
dtype: float64
"""
s=pd.Series("字符串",index=range(3))#指定index为[0,1,2]
print(s)
"""
0 字符串
1 字符串
2 字符串
dtype: object
"""
s=pd.Series(True,index=range(3))#指定index为[0,1,2]
print(s)
"""
0 True
1 True
2 True
dtype: bool
"""
2.1.3 查看Series对象的属性
Series对象的属性:
- values:获取数据
- index:获取索引
- name:values的name
- index_name:index的name
series对象及其索引都有name属性,分别指定series对象的值名称与索引名称
ser_obj=pd.Series(np.arange(10,20,5),index=['a','b'],name="数组")
print(ser_obj)
"""
a 10
b 15
Name: 数组, dtype: int64
"""
ser_obj.index.name='索引'
ser_obj
"""
索引
a 10
b 15
Name: 数组, dtype: int64
"""
2.1.4 预览数据head、tail
pandas的head()
方法从一个Series对象的头部开始预览部分数据,根据位置默认显示对象的前五行数据,也可以指定参数值确定要显示的数据行数。类似地,tail()
方法从一个Series对象的尾部开始预览数据,根据位置默认显示对象的后五行数据。
# 预览数据head()
# 创建一个series对象,预览前五个数据
ser_obj = pd.Series(np.random.randint(0,1000,100))
print(ser_obj.head())
"""
0 488
1 541
2 341
3 834
4 561
dtype: int64
"""
print(ser_obj.tail(3))
"""
97 307
98 34
99 907
dtype: int64
"""
2.1.5 通过索引获取数据
一个Series对象的值,可以通过**索引名(字符串)或者索引位置(整数)**获取。
#通过索引名获取数据
ser_obj = pd.Series(np.random.rand(3), index=['a', 'b', 'c'])
print(ser_obj)
ser_obj['b']
"""
a 0.141387
b 0.919519
c 0.035027
dtype: float64
0.9195187923528984
"""
也可以通过Series对象的loc
属性获取索引名对应的数据
ser_obj.loc['b']
"""
0.9195187923528984
"""
2.2 DataFrame
DataFrame是由多种数据类型的列构成的有标签二维数组,类似于Excel表格、SQL数据表,或者是一个series对象的字典。
DataFrame是pandas最常用的数据结构之一,每个DataFrame对象由行索引 ( index) 、列索引 (columns)和值(values)三部分组成
2.2.1 创建DataFrame对象
DataFrame对象支持多种类型的输入数据,包括列表、字典或Series对象构成的字典;一维或二维的ndarray数组;一个Series对象或其它DataFrame对象等。
pd.DataFrame(data=None,index=None,columns=None,dtype=None,copy=False)
# index:行索引
# column:列索引
#构建dataframe
array = np.random.randint(0,100,20).reshape(5,4)
df_obj = pd.DataFrame(array)
print(df_obj)
"""
0 1 2 3
0 84 68 90 46
1 98 86 39 29
2 93 81 31 54
3 80 42 48 84
4 0 72 12 85
"""
# 传入等长列表构建DataFrame
data={'name':['Tom','Lily','Lucy','Bob'],
'sex':['male','female','female','male'],
'age':['18','19','17','20']}
df_obj2=pd.DataFrame(data)
print(df_obj2)
"""
name sex age
0 Tom male 18
1 Lily female 19
2 Lucy female 17
3 Bob male 20
"""
df_obj=pd.DataFrame(data,columns=['name','age','sex','class']) ##指定列索引
print(df_obj)
# 创建的DataFrame对象中,数据按照指定顺序排列,未定义的数据标记为NaN
"""
name age sex class
0 Tom 18 male NaN
1 Lily 19 female NaN
2 Lucy 17 female NaN
3 Bob 20 male NaN
"""
# 通过元素为字典的列表创建一个DataFrame对象
data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
print(pd.DataFrame(data2))
"""
a b c
0 1 2 NaN
1 5 10 20.0
"""
# 通过Series对象创建一个DataFrame对象。创建的DataFrame对象中,一个Series对象作为一列,通过参数name指定DataFrame对象的列索引
ser = pd.Series([1,2,3],index=['a','b','c'],name='ser1')
print(pd.DataFrame(ser))
"""
ser1
a 1
b 2
c 3
"""
2.2.2 获取行、列、值
可以通过index
、columns
和values
属性访问DataFrame对象的行索引、列索引和值。
#传入等长列表构建DataFrame
data={'name':['Tom','Lily','Lucy','Bob'],
'sex':['male','female','female','male'],
'age':['18','19','17','20']}
df_obj2=pd.DataFrame(data)
"""
name sex age
0 Tom male 18
1 Lily female 19
2 Lucy female 17
3 Bob male 20
"""
df_obj2.columns # 获取列索引
"""
Index(['name', 'sex', 'age'], dtype='object')
"""
df_obj2.index # 获取行索引
"""
RangeIndex(start=0, stop=4, step=1)
"""
df_obj2.values #获取数值
"""
array([['Tom', 'male', '18'],
['Lily', 'female', '19'],
['Lucy', 'female', '17'],
['Bob', 'male', '20']], dtype=object)
"""
2.2.3 数据预览
通过head()
方法和tail()
方法分别从DataFrame对象的头部和尾部开始预览指定行数的数据,默认值为5
df_obj2.head(3) # 获取前三行数据
df_obj2.tail(3) # 获取后三行数据
2.2.4 通过索引获取数据
DataFrame对象是一种类似表格的数据结构,可以基于列索引或者基于行索引获取DataFrame对象的值,也可以通过**索引名(字符串)或者索引位置(整数)**获取DataFrame对象的值。
#获取列数据和索引
df_obj2['name'] # df_obj2.name
"""
0 Tom
1 Lily
2 Lucy
3 Bob
Name: name, dtype: object
"""
DataFrame对象可以被视为由很多数据类型不一样的列Series组成的二维表格。从每一行看,DataFrame对象可以看作由一行行的Series对象上下堆积而成,每个Series的索引就是列索引;从每一列看,DataFrame对象可以看作是一列列的Series序列左右堆积而成,每个Series的索引就是行索引。这里,通过列索引“name”获取DataFrame对象中这一列的数据,所以其数据类型为Series对象。
type(df_obj2['name'])
"""
pandas.core.series.Series
"""
df_obj2['name'][1] # 从列开始
"""
'Lily'
"""
还可以通过DataFrame对象的loc
属性获取索引位置上的数据。通过DataFrame对象的loc
属性基于行索引的索引名获取数据。
df_obj2.loc[1] #通过loc属性基于行索引的索引名获取数据
"""
name Lily
sex female
age 19
Name: 1, dtype: object
"""
print(type(df_obj2.iloc[1]))
"""
<class 'pandas.core.series.Series'>
"""
还可以通过DataFrame对象的iloc
属性按照索引位置进行切片索引获取数据。
print(df_obj2.iloc[0:2]) #通过iloc属性基于行索引的索引位置获取数据
"""
name sex age
0 Tom male 18
1 Lily female 19
"""
2.2.5 增加列数据
可以通过简单的df['new_column']=values
形式为DataFrame对象增加一列数据,这里“values”可以为Series对象、列表或ndarray对象等。
#增加列数据
df_obj2['year'] = pd.Timestamp('20230303')
print(df_obj2)
"""
name sex age year
0 Tom male 18 2023-03-03
1 Lily female 19 2023-03-03
2 Lucy female 17 2023-03-03
3 Bob male 20 2023-03-03
"""
也可以用df[['col']]=values
新增一列数据,或者使用df[[‘col1’,‘col2’,...]]=values
新增多列数据,其中**“values”必须为DataFrame对象**;当values为Series或者DataFrame对象时,需要注意索引问题,即保证等号右边对象的索引与左边对象的索引一致,否则对象中只会保存一致的索引对应的值,不存在的赋值为NaN.
df_obj2[['姓名','性别','年龄',"年份"]] = df_obj2
print(df_obj2)
"""
name sex age year 姓名 性别 年龄 年份
0 Tom male 18 2023-03-03 Tom male 18 2023-03-03
1 Lily female 19 2023-03-03 Lily female 19 2023-03-03
2 Lucy female 17 2023-03-03 Lucy female 17 2023-03-03
3 Bob male 20 2023-03-03 Bob male 20 2023-03-03
"""
2.2.6 删除
可以使用drop()
方法删除DataFrame对象的一行或一列数据,格式如下:
DataFrame.drop(labels=None,axis=0, index=None, columns=None, inplace=False)
# 参数labels:指定要删除的行或列的名字,一般为索引名的列表
# 参数axis:默认值为0,表示删除行,如果删除列数据,需要指定axis=1
# 参数index:直接指定要删除的行
# 参数columns:直接指定要删除的列
# 参数inplace:默认值为False,表示该删除操作不改变原数据,返回一个执行删除操作后的DataFrame对象;若inplace=True,则直接在原数据上进行删除操作,删除后没有返回值。
因此,删除行有两种方式:参数labels
指定要删除的行索引列表, 并且参数axis
取值为0;或者参数index
直接指定要删除的行索引。
删除列也有两种方式:参数labels
指定要删除的列索引列表, 并且参数axis
取值为1;或者参数columns
直接指定要删除的列索引
# 删除列
df_obj2.drop(columns=['year'])
2.2.7 修改数据
修改DataFrame中的数据,实际上是将这部分数据提取出来,重新赋值为新的数据。需要注意的是,数据修改直接针对DataFrame对象的原数据操作,操作无法撤销,修改数据之前需要确认或对数据进行备份
df_obj2.loc[0,'age']=25 # 思路:先用loc找到要更改的值,再用赋值(=)的方法实现更换值
df_obj2.iloc[0,6]=25 # iloc:用索引位置来查找
print(df_obj2['age'][0])
"""
25
"""
2.2.8 index 对象
扩展库pandas中索引帮助使用者便捷地获取数据集的子集,进行分片、分块等操作。pandas对象的索引不可更改,保证了数据的安全性。
Series对象和Dataframe对象的索引是index对象,Series对象中的index属性、Dataframe对象中的index属性和columns属性都是index对象。创建Series对象和Dataframe对象时用到的数组、字典或其它序列类型的索引名也都会转换为index对象。Index对象负责管理索引名和其他元素,具有不可修改、有序及可切片等特征。
ind=pd.Index(np.arange(1,5)) #创建index对象
ind
"""
Int64Index([1, 2, 3, 4], dtype='int64')
"""
ind[1]=5 # 会报错
Index对象有多种类型,常见的有Index、Int64Index、MultiIndex、DatetimeIndex和PeriodIndex等。Index可以看作其他类型的父类,表示为一个Python对象的numpy数组;Int64Index是针对整数的索引;MultiIndex是多层索引;DatetimeIndex存储时间戳;PeriodIndex针对时间间隔数据。
#构建series对象并查看index
ser_obj = pd.Series(range(10, 20, 4))
ser_obj
"""
0 10
1 14
2 18
dtype: int64
"""
ser_obj.index
"""
RangeIndex(start=0, stop=3, step=1)
"""
ser_obj.index[2] #查看位置为2的index值
"""
2
"""
可以使用reset_index()
方法重置索引,格式如下:
DataFrame.reset_index(level=None, drop=False, inplace=False)
# 参数level:表示索引的级别,默认情况下,移除所有的索引
# 参数drop:指定移除的索引是否删除,默认值为False表示移除的索引插入到数据框中作为新列;当drop设置为True,表示丢弃原来的索引列。
# 参数inplace:指定是否将数据库原地替换。
print(ser_obj.reset_index()) #使用reset_index()重置索引
"""
index 0
0 0 10
1 1 14
2 2 18
"""
ser_obj.reset_index(drop=True) #使用reset_index()重置索引
"""
0 10
1 14
2 18
dtype: int64
"""
3 索引操作
扩展库pandas的一个特点是可以通过索引获取pandas对象的值。通过索引操作,可以获取单个数据也可以获取连续数据。关于pandas对象的索引操作,主要关注两个方面:访问方式和接收参数的类型。索引的访问方式可以分为四种:loc属性、iloc属性、类似字典的访问方式以及使用标识符“.”访问
3.1 Series索引操作
可以使用obj[‘label’]
或者obj[pos]
获取单个数据,其中label表示Series对象的索引名,通常为字符串;pos表示Series对象的索引位置,一般为正整数
ser_obj = pd.Series(range(4), index=['a', 'b', 'c','d'])
ser_obj
"""
a 0
b 1
c 2
d 3
dtype: int64
"""
ser_obj.b
ser_obj['b']
ser_obj.loc['b']
ser_obj[1]
ser_obj.iloc[1]
"""
1
"""
切片操作用于获取连续数据,可以通过指定索引的开始位置和结束位置获取该区间内的数据。可以传入索引名或者索引位置,使用ser_obj[‘label1’: ‘label2’]
或者ser_obj[pos1: pos2]
获取位置连续的数据。这里label1和label2分别表示开始位置和结束位置的索引名;pos1和pos2分别表示操作开始和结束的索引位置
需要注意的是,按索引名进行切片操作时,返回结果包含结束位置的数据。
ser_obj[1:3] # 获取1、2行数据
"""
b 1
c 2
dtype: int64
"""
ser_obj['b':'d'] #获取索引为‘b’、‘d’之间的数据
"""
b 1
c 2
d 3
dtype: int64
"""
可以使用ser_obj[[‘label1’’,‘label2’’,...,‘labeln’]]
传入索引名列表获取不连续位置上的数据,也可以使用ser_obj[[pos1, pos2,...,posn]]
传入索引位置列表获取不连续的数据。这里的“labeln”和“posn”分别表示索引名和索引位置,其中n为正整数。
ser_obj[[0, 1, 3]]
"""
a 0
b 1
d 3
dtype: int64
"""
ser_obj[['b', 'd']]
b 1
d 3
dtype: int64
3.2 DataFrame的索引操作
一个Series对象可以被视为一个字典,而一个DataFrame对象可以看做每个元素是Series的字典,所以可以使用类似访问字典的方式访问Series对象和DataFrame对象。需要注意的是,类似访问字典的方式只能访问DataFrame对象的列索引。
#构建DataFrame并使用列索引获取数据
country1 = pd.Series({'Name': '中国',
'Language': 'Chinese',
'Area': '9.600M km2',
'Happiness Rank': 79})
country2 = pd.Series({'Name': '美国',
'Language': 'English (US)',
'Area': '9.370M km2',
'Happiness Rank': 14})
country3 = pd.Series({'Name': '澳大利亚',
'Language': 'English (AU)',
'Area': '7.692M km2',
'Happiness Rank': 9})
df = pd.DataFrame([country1, country2, country3], index=['CH', 'US', 'AU'])
print(df)
"""
Name Language Area Happiness Rank
CH 中国 Chinese 9.600M km2 79
US 美国 English (US) 9.370M km2 14
AU 澳大利亚 English (AU) 7.692M km2 9
"""
df['Area'] # 获取‘Area’列数据
"""
CH 9.600M km2
US 9.370M km2
AU 7.692M km2
Name: Area, dtype: object
"""
type(df['Area'])
"""
pandas.core.series.Series
"""
可以使用类似访问字典的方式访问DataFrame对象中不连续索引,获取相应位置的数据。语法格式为:df_obj[[‘label1’, ‘label2’]]
,其中“labeln”表示索引名。
print(df[['Area', 'Name']]) #获取‘Area’、‘Name’列数据
"""
Area Name
CH 9.600M km2 中国
US 9.370M km2 美国
AU 7.692M km2 澳大利亚
"""
使用索引获取DataFrame对象的行数据,主要通过loc属性或iloc属性实现。
df.loc['CH'] # 获取‘CH’行数据
"""
Name 中国
Language Chinese
Area 9.600M km2
Happiness Rank 79
Name: CH, dtype: object
"""
df.iloc[1] #获取第1行数据
"""
Name 美国
Language English (US)
Area 9.370M km2
Happiness Rank 14
Name: US, dtype: object
"""
print(df.loc['CH']['Area'])
print(df.iloc[0]['Area'])
print(df['Area']['CH'])
print(df['Area'].loc['CH'])
print(df['Area'].iloc[0])
"""
9.600M km2
9.600M km2
9.600M km2
9.600M km2
9.600M km2
"""
扩展库pandas中DataFrame对象的布尔索引与扩展库numpy中布尔值索引在功能上类似,即使用布尔数组作为索引获取满足指定条件的数据。
# 找出说英语的国家
df['Language'].str.contains('English')
"""
CH False
US True
AU True
Name: Language, dtype: bool
"""
print(df[df['Happiness Rank'] <= 20]) # 找出排名前20的国家
"""
Name Language Area Happiness Rank
US 美国 English (US) 9.370M km2 14
AU 澳大利亚 English (AU) 7.692M km2 9
"""
4 算术运算与函数应用
扩展库pandas支持不同索引的对象之间进行算术运算。两个pandas对象进行加减乘除算术运算时,如果两个对象存在不同的索引对,那么运算结果的索引就是该索引对的并集。
4.1 运算与对齐
4.1.1 Series 相加
两个Series对象相加,会自动进行数据对齐操作,相同索引对应的值会相加,而不同索引对应的值用NaN填充。Series对象间的算术运算不要求Series对象的大小一致。
obj1=pd.Series(range(12,20,3),index=range(3))
obj1
"""
0 12
1 15
2 18
dtype: int64
"""
obj2=pd.Series(range(20,25,2),index=range(1,4))
obj2
"""
1 20
2 22
3 24
dtype: int64
"""
obj3=pd.Series(range(20,30,2),index=range(1,6))
obj3
"""
1 20
2 22
3 24
4 26
5 28
dtype: int64
"""
obj1+obj2
"""
0 NaN
1 35.0
2 40.0
3 NaN
dtype: float64
"""
4.1.2 DataFrame相加
Series对象之间的运算,可以通过索引获取对应的值。而DataFrame对象需要从行、列两个维度获取对应的值进行运算。两个DataFrame对象之间进行算术运算时,行索引和列索引一起进行对齐操作。运算结果的行索引是两者行索引的并集,运算结果的列索引是两者列索引的并集;而行列对应的数值是两个DataFrame对象中相同行索引和相同列索引共同定位到的那个数值相加。如果两个DataFrame对象的行索引或者列索引不相同,则对应数值用NaN填充。
df1=pd.DataFrame(np.ones((3,3)),columns=['a','b','d'])
print(df1)
"""
a b d
0 1.0 1.0 1.0
1 1.0 1.0 1.0
2 1.0 1.0 1.0
"""
df2=pd.DataFrame(np.ones((3,3)),columns=['a','b','c'])
print(df2)
"""
a b c
0 1.0 1.0 1.0
1 1.0 1.0 1.0
2 1.0 1.0 1.0
"""
df3 = df1+df2
print(df3)
"""
a b c d
0 2.0 2.0 NaN NaN
1 2.0 2.0 NaN NaN
2 2.0 2.0 NaN NaN
"""
4.1.3 Series和DataFrame之间的运算
DataFrame对象和Series对象之间的算术运算,类似于扩展库numpy中不同维度数组间的操作。默认情况下,会使用Series对象的索引与DataFrame对象的列索引进行对齐操作,并在行上进行广播,实现算术运算。总结:采用加减运算符(+、-、*、/),Series对象进行 行广播,DataFrame与Series之间进行 行与行的运算;要进行列与列之间的运算,要采用sub、add、mul、div等,并且需要指定轴标记(axis),对Series进行列广播
# numpy的广播机制
arr1 = np.arange(1,6)
arr2 = np.random.randint(1,20,10).reshape(2,5)
print(arr1)
print(arr2)
arr3 = arr1 + arr2
print(arr3)
"""
[1 2 3 4 5]
[[15 7 2 19 15]
[ 3 13 18 1 3]]
[[16 9 5 23 20]
[ 4 15 21 5 8]]
"""
frame = pd.DataFrame(np.arange(12).reshape((3,4)),columns=['one','two','three','four'], index=list("bde"))
print(frame)
"""
one two three four
b 0 1 2 3
d 4 5 6 7
e 8 9 10 11
"""
series = frame.iloc[0]
print(series)
"""
one 0
two 1
three 2
four 3
Name: b, dtype: int64
"""
frame2 = frame-series
print(frame2)
"""
one two three four
b 0 0 0 0
d 4 4 4 4
e 8 8 8 8
"""
obj4=pd.Series([1,2,3],index=['two','four','six'])
print(frame+obj4)
"""
four one six three two
b 5.0 NaN NaN NaN 2.0
d 9.0 NaN NaN NaN 6.0
e 13.0 NaN NaN NaN 10.0
"""
如果将Series对象的索引与DataFrame对象的行索引匹配,并对列进行广播,必须使用算术方法(如:sub、add、mul和div等)中的一种,并且需要指定轴标记,即设置参数axis为“axis=0”或“axis=‘index’”,表示对齐DataFrame对象的行索引
frame =pd.DataFrame(np.arange(12).reshape(3,4),index=['b','d','e'],columns=['one','two','three','four'])
print(frame)
"""
one two three four
b 0 1 2 3
d 4 5 6 7
e 8 9 10 11
"""
series3=frame['one']
print(series3)
"""
b 0
d 4
e 8
Name: one, dtype: int64
"""
frame.sub(series3,axis='index')
"""
one two three four
b 0 1 2 3
d 0 1 2 3
e 0 1 2 3
"""
4.2 常见函数应用
4.2.1 map函数应用
map函数用来将定义好的函数作用于Series对象的每个元素
# 实现列表数据开根号
ser=pd.Series(range(3))
"""
a 0
b 1
c 2
dtype: int64
"""
ser.map(np.sqrt)
"""
a 0.000000
b 1.000000
c 1.414214
dtype: float64
"""
4.2.2 apply函数应用
apply
函数用来将定义好的函数应用于DataFrame对西昂中一行或一列形成的一维数组
df1=pd.DataFrame(np.arange(1,10).reshape(3,3),index=['app','win','mac'],columns=['a','b','c'])
print(df1)
df1.apply(np.mean)
"""
a b c
app 1 2 3
win 4 5 6
mac 7 8 9
a 4.0
b 5.0
c 6.0
dtype: float64
"""
4.2.3 applymap函数应用
applymap()
函数用来将定义好的函数应用于DataFrame对象的每个元素
print(df1)
print(df1.applymap(np.sqrt))
"""
a b c
app 1 2 3
win 4 5 6
mac 7 8 9
a b c
app 1.000000 1.414214 1.732051
win 2.000000 2.236068 2.449490
mac 2.645751 2.828427 3.000000
"""
4.2.4 索引排序
Series对象和DataFrame对象可以使用sort_index
方法对索引进行排序。对Series对象排序时,默认为升序排列,通过设置参数ascending=False可以按照降序排列。
obj=pd.Series(range(4),index=['d','a','b','c'])
obj.sort_index()
"""
a 1
b 2
c 3
d 0
dtype: int64
"""
对DataFrame对象排序时,可以通过axis参数指定按照行索引还是列索引排序,也可以通过ascending参数指定升序还是降序排列。sort_index
方法默认对行索引升序排列。通过设置参数axis置为1,参数ascending=False可以按照列索引降序排序。
df=pd.DataFrame(np.arange(12).reshape(3,4),index=['three','one','two'],columns=['d','a','b','c'])
print(df)
"""
d a b c
three 0 1 2 3
one 4 5 6 7
two 8 9 10 11
"""
print(df.sort_index()) # 按行索引升序排序
"""
d a b c
one 4 5 6 7
three 0 1 2 3
two 8 9 10 11
"""
print(df.sort_index(axis=1,ascending=False)) #按列索引降序排序
"""
d c b a
three 0 3 2 1
one 4 7 6 5
two 8 11 10 9
"""
4.2.5 值排序
Series与DataFrame对象可以使用sort_values(by,ascending)
方法对值进行排序。其中参数by可以是一个列索引名,指定依据哪一列进行排序;也可以是一个列索引元素的列表,用于指定依据哪些列索引名排序。参数ascending=True表示升序排列,ascending=Flase表示降序排列。
print(df)
"""
d a b c
three 0 1 2 3
one 4 5 6 7
two 8 9 10 11
"""
print(df.sort_values(by=['b','c'],ascending=False))
"""
d a b c
two 8 9 10 11
one 4 5 6 7
three 0 1 2 3
"""
4.2.6 描述性统计与计算
扩展库pandas提供了许多常用的数学和统计学方法,其中大部分属于约简或汇总统计,用于从Series对象中提取单个值,或者从DataFrame对象的行或列中提取一个Series对象。常用的描述性统计方法及其说明如下图所示。
describe()
方法返回Series对象或者DataFrame对象的统计摘要,一次性返回多个统计量,包括平均值、标准差、最大值、最小值等,用于快速查看每列数据的统计信息。其中count
返回每列中非空数据的个数;mean
返回每列数据的均值;std
表示每列数据的标准差;min
求每列数据的最小值;25%表示第1四分位数,即第25百分位数;50%表示第2四分位数,即第50百分位数;75%表示第3四分位数,即第75百分位数;max
求每列数据的最大值。
pandas的强大之处在于可以处理缺失的数据,如果在统计的过程中出现缺失数据,就不会对这些数据进行统计。
max()
方法和min()
方法分别求最大值和最小值;idxmax()
方法和idmin()
方法分别返回最大值对应的索引和最小值对应的索引;mad()
方法用来求平均绝对误差,是描述各个变量值之间差异程度的数值之一;var()
方法用于求方差,std()
方法用于求标准差,这两种方法可以描述变量的离散程度;cumsum()
方法用于求累加和。
5 数据清洗
数据清洗是数据分析的关键步骤,它直接影响数据分析、处理及建模的质量。数据处理之前,需要明确几个问题:数据是否需要修改?哪些数据需要修改?数据应该如何调整才能适应接下来的分析和挖掘?在实际项目开发过程中,数据清洗实际上是一个迭代的过程。数据清洗步骤涉及的常见操作有:处理缺失数据、处理重复数据和处理无效数据等。
5.1 处理缺失数据
5.1.1 判断缺失值
扩展库pandas支持使用isnull()
方法判断指定数据中是否为缺失值,如果返回值是True表示该数据缺失,否则返回False。
print(log_data.head().isnull())
any()
用于判断给定范围内的数据是否有一个为缺失值,如果有就返回True,否则返回False。isnull()
方法可以与any()
方法结合使用,用于判断某行或某列数据是否包含缺失值,存在缺失值返回True,反之返回False
log_data.isnull().any() #每列是否存在缺失值
log_data.isnull().any(axis=1) #每行是否存在缺失值
5.1.2 丢弃缺失值
扩展库pandas支持使用dropna()
方法丢弃带有缺失值的数据行。语法格式如下:
dropna(axis=0, how='any', thresh=None, subset= None, inplace=False)
# 参数how='any'时表示只要某行包含缺失值就丢弃,how='all'时表示某行数据全部是缺失值才丢弃
# 参数thresh指定保留几个非缺失值的数据行
# 参数subset指定在判断缺失值时只考虑哪些列。
# 参数inplace=False表示需要一个新对象而不对原来的对象做任何修改;如果inplace=True,表示在原始数据上进行操作。
5.1.3 填充缺失值
扩展库pandas支持使用fillna()
方法填充缺失值。语法格式如下:
fillna(value=None, method=None, axis= None, inplace=False , limit=None, downcast= None)
# 参数value指定要替换的值,该值可以是常数、字典、Series对象或DataFrame对象
# 参数method指定填充缺失值的方式,值为'pad'或'ffill'表示使用扫描过程中遇到的最后一个有效值填充前面遇到的缺失值,直至遇到下一个有效值,可称向前填充法;值为'backfill'或' bfill'表示使用扫描过程中缺失值之后遇到的第一个有效值填充前面遇到的所有连续缺失值,可称向后填充的方法;
# 参数limit指定设置了参数method时最多填充多少个连续的缺失值;此外,也可以使用ffill()方法按照向前填充法填充缺失值,或使用bfill()方法按照向后填充法进行缺失值填充。这两种方法涉及数据填充的方向,需要注意给定数据的排列顺序。
5.2 处理重复数据
5.2.1 判断是否存在重复数据
记录失误等原因可能导致数据中存在重复值。可以使用DataFrame对象的duplicated()
方法判断数据中是否存在重复值,语法格式如下:
duplicated(subset= None, keep='first')
# 参数subset用于指定判断不同行的数据是否重复时依据的列名称,默认使用整行所有列的数据进行比较
# 参数keep='first'表示重复数据第一次出现的标记为False,keep='last'表示重复数据最后一次出现的标记为False,keep=False表示将所有重复数据标记为True
data = pd.DataFrame(
{'age': [20, 21, 18, 20],
'gender': ['M', 'M', 'M', 'F'],
'surname': ['Liu', 'Li', 'Chen', 'Liu']}
)
print(data)
"""
age gender surname
0 20 M Liu
1 21 M Li
2 18 M Chen
3 20 F Liu
"""
data.duplicated() #判断是否存在重复数据
"""
0 False
1 False
2 False
3 False
dtype: bool
"""
data.duplicated(subset=['age', 'surname']) #指定'age', 'surname'列重复时去重
"""
0 False
1 False
2 False
3 True
dtype: bool
"""
5.2.2 删除重复值
扩展库pandas支持删除重复数据的drop_duplicated()
方法,语法格式如下:
drop_duplicates(subset=None,keep='first',inplace=Flase)
# subset:接收字符串或序列,指定去重依据的列,默认使用全部列进行比较
# keep:接收待定字符串,用于指定保留重复数据中的第几个,默认值为'first',表示保留第一次出现的重复值数据,'last'表示保留最后一次出现的重复值,keep=Flase表示不保留重复值
# inplace:接收布尔型数据,指定是否在原数据上进行操作,默认值为False
print(data.drop_duplicates(subset=['age', 'surname'], keep='last')) #去重时保留最后出现的数据
"""
age gender surname
1 21 M Li
2 18 M Chen
3 20 F Liu
"""
5.3 替换数据
扩展库pandas支持使用replace(to_replace)
方法将查询到的数据值替换为指定的值,其中参数to_replace
表示需要替换的值,可以是数值、字符串、列表、字典等
print(data)
"""
age gender surname
0 20 M Liu
1 21 M Li
2 18 M Chen
3 20 F Liu
"""
print(data.replace(21, 30)) # 数值替换
"""
age gender surname
0 20 M Liu
1 30 M Li
2 18 M Chen
3 20 F Liu
"""
print(data.replace([20, 21, 18, 20], 18)) # 列表替换为值
"""
age gender surname
0 18 M Liu
1 18 M Li
2 18 M Chen
3 18 F Liu
"""
print(data.replace({'Liu': 'Chen', 'Li': 'Zhao'})) # 按字典替换
"""
age gender surname
0 20 M Chen
1 21 M Zhao
2 18 M Chen
3 20 F Chen
"""
6 分组和聚合
对数据集进行分组,并对各组应用一个聚合函数或转换函数,通常是数据分析的重要组成部分。在数据载入、合并,完成数据准备之后,通常需要计算分组统计或生成数据透视表。pandas提供了灵活高效的groupby()
方法,方便用户对数据集进行切片、切块和摘要等操作。
6.1 数据分组
扩展库pandas支持使用groupby()
方法根据指定的列或行对数据集分组,返回一个GroupBy对象,然后通过该对象根据需求调用不同的方法实现整组数据计算功能。
groupby()方法实现分组聚合的过程可以分为三个阶段。
- 拆分(split)阶段:将数据按照标准拆分成多个组;
- 应用(apply)阶段:将一个指定函数应用于拆分后的每一组数据,产生一个新值;
- 合并(combine)阶段:将各组产生的结果合并成一个新的对象。
pandas对象支持的groupby()方法语法格式如下:
groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False)
# 参数by用于指定分组依据,可以是函数、字典、Series对象、DataFrame对象的列名等;
# 参数axis表示分组轴的方向,可以是0或'index',1或'columns',默认值为0;
# 参数level表示如果某个轴是一个MultiIndex对象(层级索引),则按照特定级别或多个级别分组;
# 参数as_index=False表示用来分组的列中的数据不作为结果DataFrame对象的index;
# 参数sort指定是否对分组标签进行排序,默认值为True。
# 使用groupby()方法可以实现两种分组方式,返回的对象结果不同。如果仅对DataFrame对象中的数据进行分组,将返回一个DataFrameGroupBy对象;如果是对DataFrame对象中某一列数据进行分组,将返回一个SeriesGroupBy对象。
obj3 = data.groupby('Region') #按单列分组
print(type(obj3))
data.groupby('Region')
obj2= data.groupby(data['Region']) # 按单列分组
print(type(obj2))
data.groupby(data['Region'])
6.2 数据聚合
将数据分组之后,可以根据应用需求,调用不同的计算方法实现分组数据的计算功能。GroupBy对象支持大量方法对列数据进行求和、求均值等操作,并自动忽略非数值数据,是数据分析时经常使用的。
7 数据规整
实际应用中,数据可能分布在多个文件或数据库中,存储形式也不利于分析。
7.1 层级索引
常见的数据形式是每行或每列数据只有一个索引,通过这个索引可以获取对应的数据。层级索引是pandas的重要特性,它允许数据在一个轴上拥有两个或两个以上索引级别。层级索引提供了一种以低维度形式处理高维度数据的方法。
层级索引能够将DataFrame对象转化为Series数据结构,也可以将超过二维的数据结构转化为等价的DataFrame对象,达到类似数据降维的效果。
创建一个Series对象ser_obj时,输入的索引index是由两个列表元素组成的列表。输出该Series对象的索引类型,发现得到一个Multilndex对象。这是层级索引的索引对象,其中外层索引是输入参数index中的第一个列表元素,内层索引是输入参数index中的第二个列表元素。
import pandas as pd
import numpy as np
ser_obj = pd.Series(np.arange(6),index=[
['a', 'a', 'b', 'b', 'c', 'c'],
[0, 1, 2, 3, 0, 1,]
])
print(ser_obj)
"""
a 0 0
1 1
b 2 2
3 3
c 0 4
1 5
dtype: int64
"""
print(ser_obj.index)
"""
MultiIndex([('a', 0),
('a', 1),
('b', 2),
('b', 3),
('c', 0),
('c', 1)],
)
"""
ser_obj['b']
ser_obj.loc['b']
"""
2 2
3 3
dtype: int64
"""
ser_obj[:,1]
"""
a 1
c 5
dtype: int64
"""
ser_obj['b',3]
ser_obj.loc['b',3]
"""
3
"""
7.1.1 设置层级索引
扩展库pandas支持使用set_index(['a', 'b'], inplace=True)
方法设置多个索引列,其中a表示外层索引,b表示内层索引。
7.1.2 获取数据子集
对于含有层级索引的pandas对象,可以通过不同级别的索引获取数据子集,即可以直接利用外层索引的标签获取数据子集,也可以通过内层索引获取数据。
这时需要在列表中传入两个元素,前者表示要选取的外层索引,后者表示要选取的内层索引。
如果根据外层索引获取数据子集,也可以使用loc['outer_index']
;如果根据内层索引获取数据子集,也可以使用loc['out_index', "inner_index']
。
ser_obj['b'] #运行结果等同ser_obj.loc['b’]
ser_obj['b',3] #运行结果等同ser_obj.loc['b',3]
7.1.3交换层级顺序
对于含有层级索引的pandas对象,可以使用swaplevel()
方法交换内层索引与外层索引的顺序。
data.swaplevel()
7.1.4 层级索引排序
对于含有层级索引的pandas对象,可以使用sort_index(level=None)
方法按索引排序,其中参数level指定按哪一级索引进行排序。
data.sort_index(level=1)
7.2 数据合并
扩展库pandas支持使用concat()
函数按照指定的轴方向对多个pandas对象进行数据合并,常用于多个DataFrame对象的数据合并。语法格式及常用参数如下:
pd.concat((objs, axis=0, join='outer', join_axes=None, keys=None, levels=None, names=None, ignore_index=False, verify_integrity=False, copy=True)
# 参数objs表示需要连接的多个pandas对象,可以是Series对象,DataFrame或Panel对象构成的列表或字典;
# 参数axis指定需要连接的轴向,默认axis=0表示按行进行纵向合并和扩展,axis=1表示按列进行横向合并和扩展。
# 参数join指定连接方式,默认值为outer,表示按照外连接(并集)方式合并数据;如果join='inner',表示按照内连接(交集)方式合并数据。
# 创建dataframe
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index=[0, 1, 2, 3])
df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7']},
index=[4, 5, 6, 7])
df3 = pd.DataFrame({'C': ['A8', 'A9', 'A10', 'A11'],
'D': ['B8', 'B9', 'B10', 'B11'],
'E': ['C8', 'C9', 'C10', 'C11'],
'F': ['D8', 'D9', 'D10', 'D11']},
index=[0, 1, 2, 3])
print(df1)
"""
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
"""
print(df2)
"""
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
"""
print(df3)
"""
C D E F
0 A8 B8 C8 D8
1 A9 B9 C9 D9
2 A10 B10 C10 D10
3 A11 B11 C11 D11
"""
print(pd.concat([df1, df2], axis=0)) # 纵向合并
"""
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
"""
print(pd.concat([df1, df3], axis=0)) # 列名不同
"""
A B C D E F
0 A0 B0 C0 D0 NaN NaN
1 A1 B1 C1 D1 NaN NaN
2 A2 B2 C2 D2 NaN NaN
3 A3 B3 C3 D3 NaN NaN
0 NaN NaN A8 B8 C8 D8
1 NaN NaN A9 B9 C9 D9
2 NaN NaN A10 B10 C10 D10
3 NaN NaN A11 B11 C11 D11
"""
print(pd.concat([df1, df3], axis=1) # 横向合并
"""
A B C D C D E F
0 A0 B0 C0 D0 A8 B8 C8 D8
1 A1 B1 C1 D1 A9 B9 C9 D9
2 A2 B2 C2 D2 A10 B10 C10 D10
3 A3 B3 C3 D3 A11 B11 C11 D11
"""
print(pd.concat([df1, df2], axis=1)) # index不同
"""
A B C D A B C D
0 A0 B0 C0 D0 NaN NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 NaN NaN NaN NaN
4 NaN NaN NaN NaN A4 B4 C4 D4
5 NaN NaN NaN NaN A5 B5 C5 D5
6 NaN NaN NaN NaN A6 B6 C6 D6
7 NaN NaN NaN NaN A7 B7 C7 D7
"""
7.3 数据连接
扩展库pandas提供了一个与数据表连接操作类似的merge(
)函数。DataFrame对象的merge()函数可以根据单个或多个键将不同DataFrame对象的行连接起来,语法格式如下:
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:合并操作中左侧的DataFrame对象或Series对象
# right:合并操作中右侧的DataFrame对象或Series对象
# how:数据连接方式,可以是'inner'、'outer'、'left'或'right',默认'inner'
# on:需要连接的列名,两个待合并的DataFrame对象都有的列名
# left_on:表示左侧DataFrame对象用作连接键的列名
# right_on:表示右侧DataFrame对象用作连接键的列名
# left_index:使用左侧的行索引作为连接键,若为MultiIndex,则是多个键
# right_index:使用右侧的行索引作为连接键,若为MultiIndex,则是多个键
# sort:指定是否对合并后的数据进行排序,默认值为False
# suffixes:重叠情况下,添加为列名后缀的字符串元组,默认('_x', '_y')
扩展库pandas中的merge()
函数根据共同列或者索引对行进行连接,实现的是数据库的连接操作,包括外连接、内连接、左连接和右连接等。
如果两个DataFrame对象含有相同的列名(不是要合并的列)时,merge()函数会自动为重叠的列名添加后缀以示区别
# 创建dataframe
staff_df = pd.DataFrame([{'姓名': '张三', '部门': '研发部'},
{'姓名': '李四', '部门': '财务部'},
{'姓名': '赵六', '部门': '市场部'}])
student_df = pd.DataFrame([{'姓名': '张三', '专业': '计算机'},
{'姓名': '李四', '专业': '会计'},
{'姓名': '王五', '专业': '市场营销'}])
print(staff_df)
"""
姓名 部门
0 张三 研发部
1 李四 财务部
2 赵六 市场部
"""
print(student_df)
"""
姓名 专业
0 张三 计算机
1 李四 会计
2 王五 市场营销
"""
# 外连接
print(pd.merge(staff_df, student_df, how='outer', on='姓名'))
# 或者
# staff_df.merge(student_df, how='outer', on='姓名')
"""
姓名 部门 专业
0 张三 研发部 计算机
1 李四 财务部 会计
2 赵六 市场部 NaN
3 王五 NaN 市场营销
"""
# 内连接
print(pd.merge(staff_df, student_df, how='inner', on='姓名'))
# 或者
# staff_df.merge(student_df, how='inner', on='姓名')
"""
姓名 部门 专业
0 张三 研发部 计算机
1 李四 财务部 会计
"""
# 左连接
print(pd.merge(staff_df, student_df, how='left', on='姓名'))
# 或者
# staff_df.merge(student_df, how='left', on='姓名')
"""
姓名 部门 专业
0 张三 研发部 计算机
1 李四 财务部 会计
2 赵六 市场部 NaN
"""
# 右连接
print(pd.merge(staff_df, student_df, how='right', on='姓名'))
# 或者
# staff_df.merge(student_df, how='right', on='姓名')
"""
姓名 部门 专业
0 张三 研发部 计算机
1 李四 财务部 会计
2 王五 NaN 市场营销
"""
实际应用中,用于数据连接的键也许是DataFrame对象的索引。这种情况下,可以通过参数left_index=True
或right_index=True
来表示索引用作数据连接的键。
# 设置"姓名"为索引
staff_df.set_index('姓名', inplace=True)
student_df.set_index('姓名', inplace=True)
print(staff_df)
"""
部门 地址
姓名
张三 研发部 天津
李四 财务部 北京
赵六 市场部 上海
"""
print(student_df)
"""
专业 地址
姓名
张三 计算机 天津
李四 会计 上海
王五 市场营销 广州
"""
print(pd.merge(staff_df, student_df, how='left', left_index=True, right_index=True))
"""
部门 地址_x 专业 地址_y
姓名
张三 研发部 天津 计算机 天津
李四 财务部 北京 会计 上海
赵六 市场部 上海 NaN NaN
"""
print(staff_df.merge(student_df, how='left', left_index=True, right_index=True))
"""
部门 地址_x 专业 地址_y
姓名
张三 研发部 天津 计算机 天津
李四 财务部 北京 会计 上海
赵六 市场部 上海 NaN NaN
"""