文章目录
Python 之 数据处理分析模块 Pandas
Pandas 是基于 Numpy 的一套数据分析工具,该工具是为了解决数据分析任务而创建的。
- 数据模型繁多:Pandas 纳入了大量标准的数据模型,提供了高效地操作大型数据集所需的工具;
- 导入数据方便:Pandas 可以从各种文件格式中导入数据,如:CSV、JSON、SQL、Excel 等;
- 处理数据便捷:Pandas 是 Python 语言的一个扩展程序库,提供了许多快速便捷地处理数据的函数和方法。
小贴士:
- 本文中多处使用到 Numpy 模块,若对其不熟悉,可先浏览:AI算法工程师 | 03人工智能基础-Python科学计算和可视化(一)Numpy
一、Pandas 开发环境搭建
说明
- Pandas 是第三方程序库,在使用 Pandas 前需确保安装了 Pandas 库。
- 如果使用的是 Anaconda Python 开发环境,那么 Pandas 已经被集成进 Anaconda,并不需要单独安装。(由于自己使用的是 Miniconda Python 开发环境,需要单独安装 Pandas )
测试与安装
若不清楚 Python 环境是否安装了 Pandas,可先测试:
- Windows电脑中按键
win + R
后输入“cmd
” → 在 cmd 的命令窗口输入python
→ - 进入 python 环境中后,输入
import pandas
并回车 → - 若输入后提示模块没有找到“
ModuleNotFoundError: No module named 'pandas'
”,说明未安装 Pandas;若什么都没显示,则说明已安装。 - 注:输入
exit()
可退出 python 环境。
cmd 中使用 pip 命令安装 Pandas:(注意该命令是在 cmd 中,而非 python 环境中)
# 安装命令1(不指定版本)
pip install pandas
# 安装命令2(指定下载源,这样下载得更快)
pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple
安装完成后,可以测试一下 Pandas 是否安装成功。下面是导入 pandas 模块的语句(该语句是在 python 环境中输入),若不报错,说明 Pandas 已经安装成功了。
# 导入 pandas 模块,并起别名为 pd
# 注意:每次使用 pandas 前均需写导入模块的语句
import pandas as pd
# 扩:这是查看 pandas 库的版本号的命令
import pandas
pandas.__version__ # 查看版本号(命令中,version前后均分别有两个下划线)
图示
二、Pandas 数据类型
Pandas 中两个重要的数据类型:Series 和 DataFrame
- Series:表示数据列表(一维)
- DataFrame:表示二维数据集(二维)
1. Series 对象创建
Series 语法格式
pandas.Series( data, index, dtype, name, copy)
'''
参数说明:
· data :一组数据(ndarray 类型)
· index:数据索引标签(若不指定,默认从 0 开始)
· dtype:数据类型,默认会自己判断
· name :设置名称(默认为 False)
'''
示例1 - 使用列表创建
- 使用列表创建 Series 对象
import pandas as pd # 导包
data = pd.Series([1,2,3,4,5]) # 返回一个序列
data
Series 对象本质上是调用 numpy 创建了一个一维数组,之后在其身上赋予了一个索引,从而组成了一个 Series。即:Series 对象中有 ① numpy 数组、② index 索引。
因此,pandas 中有两个重要的属性 values(Series 对象的原始数据) 和 index(对应 Series 对象的索引对象)
- Series 对象中两个重要的属性 values 和 index
data.values # 查看属性 values
data.index # 查看属性 index
- 若不想让索引从 0 开始,可以指定索引值(index 属性)
data = pd.Series([4,3,6,7,9],index=['one','two','three','four','five'])
data
- 也可使用 list 列表指定 index 属性
data = pd.Series([4,3,6,7,9],index=list('abcde'))
data
示例2 - 使用字典创建
- 使用字典创建 Series 对象
import pandas as pd # 导包
# 使用字典创建 Series 对象,默认将 key 作为 index 属性
population_dict={'bj':3000,'gz':1500,'sh':2800,'sz':1200} # 可传字典
population_series=pd.Series(population_dict)
population_series
- 用字典创建 Series 对象,可指定 index 属性值。若 key 不存在,则值为 NaN(代表空值)
# 如果存在取交集
sub_series=pd.Series(population_dict,index=['bj','sh'])
sub_series
# 如果不存在则值为 NaN
sub_series=pd.Series(population_dict,index=['bj','xa'])
sub_series
示例3 - 使用标量与 index 创建
- 标量与 index 属性创建 Series 对象
import pandas as pd # 导包
data = pd.Series(6,index=[6,5,8,9,3])
data
2. DataFrame 对象创建
关于 DataFrame
DataFrame 可以看成一个数据框(表格),有行与列。其中,每一列可以是不同的值类型,如:数值、布尔、字符串等。
图示 - 由 Series 组成的 DataFrame:
DataFrame 语法格式:
pandas.DataFrame( data, index, columns, dtype, copy)
'''
参数说明:
· data :一组数据(ndarray、series, map, lists, dict 等类型)
· index :数据索引标签(相当于:行索引)
· columns:列索引(默认为 RangeIndex 0, 1, 2, …, n)
· dtype :数据类型
· copy :拷贝数据(默认为 False)
'''
示例1 - 使用 Series 及字典创建
- 将两个 Series 对象作为字典的值,就可以创建一个 DataFrame 对象
import pandas as pd # 导包
# 字典
population_dict={'beijing':3000,'shanghai':1200,'guangzhou':1800}
area_dict={'beijing':300,'guangzhou':200,'shanghai':180}
# 创建 2 个 series
population_series=pd.Series(population_dict)
area_series=pd.Series(area_dict)
# 通过 DataFrame 创建数据框
citys=pd.DataFrame({'area':area_series,'population':population_series})
citys
- 查看 DataFrame 对象的 values、index 和 columns 属性
citys.index # 数据索引标签(相当于:行索引)
citys.values # 数据值
citys.columns # 列索引
示例2 - 使用列表创建
- 既然可以用列表创建 Series 对象,那是不是也可以使用列表创建 DataFrame 对象呢?
import pandas as pd # 导包
# 字典
population_dict={'beijing':3000,'shanghai':1200,'guangzhou':1800}
area_dict={'beijing':300,'guangzhou':200,'shanghai':180}
# 使用列表创建 DataFrame 对象
citys=pd.DataFrame([population_dict,area_dict]) # 直接把两个字典放在列表中
citys # 会将‘beijing’‘shanghai’ ‘guangzhou’作为表头(列索引)
从结果中可以看到,使用列表创建的 DataFrame 对象,若未指定 index,则行索引默认从 0 开始。
- 使用列表创建 DataFrame 对象,可通过指定 index 设置行索引
import pandas as pd # 导包
# 字典
population_dict={'beijing':3000,'shanghai':1200,'guangzhou':1800}
area_dict={'beijing':300,'guangzhou':200,'shanghai':180}
# 创建 DataFrame 对象,并设置行索引
citys=pd.DataFrame([population_dict,area_dict], index=['population','area']) # 指定索引号名称
citys
- 对于上方的代码而言,可通过指定 columns,实现取某列(或过滤)的效果
pd.DataFrame([population_dict,area_dict],
index=['population','area'],columns=['beijing','guangzhou']) # 指定 columns,设置列索引
- 创建 DataFrame 对象,并通过指定 columns 设置列索引
import pandas as pd # 导包
# 创建 1 个 series
population_dict={'beijing':3000,'shanghai':1200,'guangzhou':1800}
population_series=pd.Series(population_dict)
# 设置列索引
pd.DataFrame(population_series,columns=['population']) # 指定列名
# pd.DataFrame({'population':population_series}) # 与上一行的代码等价
- 使用列表创建 Dataframe 对象
import pandas as pd # 导包
pd.DataFrame([{'a':i,'b':i*2} for i in range(3)])
# pd.DataFrame([{'a':i,'b':i*2} for i in range(3)], index=['一','二','三']) # 指定行名称
示例3 - 使用二维数组创建
- 使用一个二维数组并指定 columns 和 index 创建 DataFrame 对象
import pandas as pd # 导包
import numpy as np
# 指明行名称、列名称
pd.DataFrame(np.random.randint(0, 100, (4,3)), index=['a','b','c','d'], columns=['a','b','c'])
示例4 - 使用字典创建
- 创建一个数据集
import pandas as pd # 导包
pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
})
data # 字典中所有的键都作为表头(列索引)
- 指定列索引
import pandas as pd # 导包
data = pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
},columns=['Sno','Sex','Age','Score'])
data
- 指定行索引与列索引
import pandas as pd # 导包
data = pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
},columns=['Sno','Sex','Age','Score'],index=['zs','lisi','ww'])
data
小贴士:
- 关于 DataFrame 的详细介绍,可参考: 菜鸟教程——Pandas 数据结构 - DataFrame
3. 获取 Series 对象的值
示例
- Series 对象的切片与索引
import pandas as pd # 导包
data = pd.Series([45,6,2,3,5],index=list('abcde'))
display('根据key获取:',data['a'])
display('切片获取:',data['a':'d']) # 注意,这里(标签索引)不是左闭右开,会取到 d
display('索引获取:',data[1]) # 取第二个
display('索引切片:',data[2:4])# 这里(位置索引)取值时,是左闭右开
可以看到,Series 和 ndarray 数组类似,可以通过索引来访问元素。但 Series 对象的索引可分为位置索引和标签索引。
其中,标签索引进行切片时:左闭右闭;而位置索引:左闭右开。
- 当 Series 对象的标签索引和位置索引存在相同时,无法区分是按哪种索引获取,怎么办?
data=pd.Series([5,6,7,8],index=[1,2,3,4])
data[1]
上方的 Series 对象 data 中,位置索引与标签索引有相同值 1,则 data[1] 代表的含义便不明确,此时需使用 loc(标签索引)、iloc(位置索引)。
- 使用 Series 对象中 loc 与 iloc 获取值
data.loc[1] # 根据名字(标签索引)取值
data.iloc[1] # 根据位置索引取值
4. 获取 DataFrame 的值
获取 DataFrame 的值的介绍
DataFrame 对象获取列的数据:
- 可传入具体的列名 —— 通过列名选择数据的方式叫做普通索引;
- 还可以传入具体列的位置( iloc 方法)—— 传入列的位置选择数据的方式叫做位置索引。
其中,获取连续的某几列时,用普通索引和位置索引都可以做到。由于要获取的列是连续的,所以需要对列进行切片来获取数据。
获取行的方式主要有两种:
- 一种是普通索引,即传入具体行索引的名称,需要用到 loc 方法;
- 另外一种是位置索引,即传入具体的行数,需要用到 iloc 方法。
示例1 - 获取列
- 创建 DataFrame 对象
import pandas as pd # 导包
import numpy as np
data=pd.DataFrame(np.arange(12).reshape(3,4),index=list('abc'),columns=list('ABCD'))
data
- 获取列:① 传入具体的列名
print('获取‘B’列:')
print(data['B']) # 如果要获取一列,则只需要传入一个列名
print('获取‘A’‘C’两列:')
print(data[['A','C']]) # 如果是同时选择多列,则传入多个列名(用一个 list 存放)即可
- 获取列:② 传入具体列的位置
print('获取第 1 列:')
print(data.iloc[:,0])
print('获取第 1 列和第 3 列:')
print(data.iloc[:,[0,2]])
# 说明:iloc 后的方括号中
# ① 逗号之前的部分表示要获取的行的位置。只输入一个冒号,不输入任何数值表示获取所有的行;
# ② 逗号之后的方括号表示要获取的列的位置,列的位置同样也是从 0 开始计数。
- 获取连续的几列(切片获取)
print('获取 A B 两列,使用位置索引获取:')
print(data.iloc[:,0:2])
print('获取 A B C 三列,使用普通索引获取:')
print(data.loc[:,'A':'C'])
示例2 - 获取行
- 获取某一行或某几行
print('获取 a 行,普通索引获取:')
print(data.loc['a'])
print('获取 a c 行,普通索引获取:')
print(data.loc[['a','c']])
print('获取第 1 行,位置索引获取:')
print(data.iloc[0])
print('获取第 1 行第 3 行,位置索引获取:')
print(data.iloc[[0,2]])
- 获取连续的几行(切片获取)
# 选择连续的某几行和选择连续某几列类似:把连续行的位置用一个区间表示
print('选择第 1 行 第 2 行,使用位置索引:')
print(data.iloc[0:2])
print('选择 a 行 b 行,使用普通索引:')
print(data.loc['a':'b'])
示例3 - 同时获取行和列
- 同时选择连续的部分行和部分列
print('同时获取 a b c 行,A B 列,使用普通索引:')
print(data.loc['a':'c','A':'B'] )
print('同时获取 a b 行,A B 列,使用位置索引:')
print(data.iloc[0:2,0:2])
- 同时选择不连续的部分行和部分列
print('同时获取 a c 行,ABD 列,使用普通索引:')
print(data.loc[['a','c'],['A','B','D']])
print('同时获取 a c 行,ABD 列,使用位置索引:')
print(data.iloc[[0,2],[0,1,3]])
5. Series 的方法
Series 对象中有很多常用的方法可以对数据进行各种处理。例如:
方法 | 描述 |
---|---|
mean | 对某一列数据取平均数 |
min | 获取最小值 |
max | 获取最大值 |
std | 获取标准差 |
示例 - 对数据集进行各种运算,并排序
- 创建一个数据集
import pandas as pd # 导包
data = pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
},columns=['Sno','Sex','Age','Score'],index=['zs','lisi','ww'])
display('数据集',data)
- 计算平均值、最大值、最小值、标准差
ages = data['Age'] # 获取数据集中 Age 列的所有
print(ages)
ages.mean(), ages.min(), ages.max(), ages.std()
- 排序(正序)
ages.sort_values() # 对 Age 列进行排序(默认正序排序)
- 降序排序
ages.sort_values(ascending=False) # 对 Age 列进行降序排序
6. Series 的条件过滤
Series 对象也可像 SQL 语句一样,通过指定条件进行数据的过滤。
示例
- 筛选出成绩大于平均值的数据
import pandas as pd # 导包
# 创建数据集
data = pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
},columns=['Sno','Sex','Age','Score'],index=['zs','lisi','ww'])
# 取出 'Score' 列的数据,并对数据进行筛选
scores = data['Score']
print(scores.mean()) # 平均值
scores[scores>scores.mean()] # 筛选出大于平均值的
7. DataFrame 的条件过滤
DataFrame 与 Series 类似,也可使用条件进行过滤。
示例
- 一个条件进行筛选:输出数据中所有成绩大于平均值的记录
import pandas as pd # 导包
# 创建数据集
data = pd.DataFrame({
'Name':['zs','lisi','ww'],
'Sno':['1001','1002','1003'],
'Sex':['man','woman','man'],
'Age':[17,18,19],
'Score':[80,97,95]
},columns=['Sno','Sex','Age','Score'],index=['zs','lisi','ww'])
# 输出数据中所有成绩大于平均值的记录
scores = data['Score'] # 取出 'Score' 列的数据
print(scores.mean())
data[scores>scores.mean()] # data 中取值
- 多个条件进行筛选
# 且
ages = data['Age'] # 获取数据集中 Age 列的所有
data[(scores>scores.mean()) & (ages<19)] # 成绩大于平均值,且 年龄小于 19
# 或
data[(scores>scores.mean()) | (ages<19)] # 成绩大于平均值,或 年龄小于 19
- 根据条件进行筛选后,再通过 loc 取值
# 获取成绩大于平均值的所有记录,只显示 Sno Age Score 三列
data[scores>scores.mean()].loc[:,['Sno','Age','Score']]
三、处理缺失值
何谓缺失值?缺失值是指由某些原因导致部分数据为空。
对于为空的这部分数据,一般有两种处理方式:
- 方式一:删除,即把含有缺失值的数据删除;
- 方式二:填充,即把缺失的那部分数据用某个值代替。
1. 缺失值查看
在对缺失值进行处理前,需要把缺失值找出来,即查看哪列有缺失值。
Pandas 中缺失值用 NaN 表示,通过调用 info() 方法可看到返回的每一列的缺失情况。
示例
- info() 方法查看缺失值
import pandas as pd # 导包
import numpy as np
df=pd.DataFrame([[1,2,np.nan],[4,np.nan,6],[5,6,7]])
df.info() # 缺失值查看
- isnull() 方法可判断哪个值是缺失值,若为缺失值则返回 True,否则返回 False
import pandas as pd # 导包
import numpy as np
data=pd.Series([3,4,np.nan,1,5,None])
print(data.isnull(),'\n') # isnull()方法判断是否是缺值
print(data[data.isnull()],'\n') # 获取缺值
print(data[data.notnull()]) # 获取非空值
2. 缺失值删除
缺失值分为两种:
- 一种是:一行中某个字段是缺失值;
- 另一种是:一行中的字段全部为缺失值,即为一个空白行。
调用 dropna() 方法删除缺失值:
- dropna() 方法默认删除含有缺失值的行,即只要某一行有缺失值就把这一行删除;
- 如果想按列为单位删除缺失值,需要传入参数
axis=’columns’
。
示例
- dropna() 方法删除缺失值
import pandas as pd # 导包
import numpy as np
df=pd.DataFrame([[1,2,np.nan],[4,np.nan,6],[5,6,7]]) # 3行 3列
new_df = df.dropna() # 缺失值删除(默认为以行为单位剔除)
new_df
- 按列进行删除缺失值(传入参数 axis=’columns’)
df.dropna(axis='columns') # 以列为单位剔除
- 若想删除空白行,需给 dropna() 方法中传入参数 how=’all’ ,默认值为 any
df.dropna(how='all') # 某行中所有数据为 nan 时才剔除该行
3. 缺失值填充
由于数据是宝贵的,一般情况下如果数据缺失比例不高,尽量不会选择删除,而是选择填充。
调用 fillna() 方法填充缺失值:
- 在 fillna() 方法中输入要填充的值,可对数据表中的所有缺失值进行填充;
- 还可以通过 method 参数使用前一个数和后一个数来进行填充。
示例
- Series 对象缺失值填充
import pandas as pd # 导包
import numpy as np
data=pd.Series([3,4,np.nan,1,5,None])
print('以 0 进行填充:')
display(data.fillna(0))
print('以前一个数进行填充:')
display(data.fillna(method='ffill'))
print('以后一个数进行填充:')
display(data.fillna(method='bfill'))
print('先按后一个,再按前一个')
display(data.fillna(method='bfill').fillna(method='ffill'))
- DataFrame 对象缺失值填充
import pandas as pd # 导包
import numpy as np
df=pd.DataFrame([[1,2,np.nan],[4,np.nan,6],[5,6,7]])
print('使用数值 0 来填充:')
display(df.fillna(0))
print('使用行的前一个数来填充:')
display(df.fillna(method='ffill'))
print('使用列的后一个数来填充:')
display(df.fillna(method='bfill' ,axis=1))
- 使用列的平均值进行填充(常用)
import pandas as pd # 导包
import numpy as np
df=pd.DataFrame([[1,2,np.nan],[4,np.nan,6],[5,6,7]])
display(df) # 原数据 df
for i in df.columns:
df[i]=df[i].fillna(np.nanmean(df[i])) # 将填充完后的序列赋值给原序列(达到修改原数据的效果)
display(df) # 填充后的数据 df
四、拼接
示例1 - Series 对象拼接
- 使用 concat() 方法对 Series 对象进行拼接
import pandas as pd # 导包
ser1=pd.Series([1,2,3],index=list('ABC'))
ser2=pd.Series([4,5,6],index=list('DEF'))
pd.concat([ser1,ser2])
示例2 - DataFrame 对象拼接
- 多个 df 对象拼接,默认找相同的列索引进行合并
import pandas as pd # 导包
# 声明一个函数
def make_df(cols,index):
data={c:[str(c)+str(i) for i in index] for c in cols}
return pd.DataFrame(data,index=index)
# 调用函数,创建两个 DataFrame 对象
df1=make_df('AB',[1,2])
df2=make_df('AB',[3,4])
# 拼接(默认找相同的列索引进行合并)
pd.concat([df1, df2])
- 添加 axis 参数,可按列拼接
pd.concat([df1, df2], axis=1) # 按列拼接,axis=1与 axis='columns' 等价
从上图结果中可以看到,多个 df 对象按列拼接时,若行索引不同,行也会往下拼接,并用 NaN 填充没有的数据。
多个 df 对象拼接,当设置 axis=1 按列拼接时,相同的行索引会进行合并,如:
示例3 - DataFrame 对象拼接的索引重复问题
- 多个 df 对象拼接,索引(行索引)重复问题
import pandas as pd # 导包
# 声明一个函数
def make_df(cols,index):
data={c:[str(c)+str(i) for i in index] for c in cols}
return pd.DataFrame(data,index=index)
# 调用函数,创建两个 DataFrame 对象
df1=make_df('AB',[1,2])
df2=make_df('AB',[1,2])
# 拼接(默认找相同的列索引进行合并)
pd.concat([df1, df2])
- 上方的行索引有重复,若想让结果的索引重新排放,可加 ignore_index 属性
# 解决索引重复问题,方式 1 :加 ignore_index 属性
pd.concat([df1,df2], ignore_index=True)
- 还可通过加 keys 属性,解决索引重复问题
# 解决索引重复问题,方式 2 :加 keys 属性
pd.concat([df1,df2],keys=['df1', 'df2'])
示例4 - join 拼接
- 两个 df 对象拼接,join 内连接做交集
import pandas as pd # 导包
# 声明一个函数
def make_df(cols,index):
data={c:[str(c)+str(i) for i in index] for c in cols}
return pd.DataFrame(data,index=index)
# 调用函数,创建两个 DataFrame 对象
a=make_df('ABC',[1,2])
b=make_df('BCD',[3,4])
# 内连接(取列相交部分)
pd.concat([a,b],join='inner')
五、merge 的使用
pandas 中 的 merge 和 concat 类似,但主要是用于两组有 key column 的数据,统一索引的数据。通常也被用在 Database 的处理当中。
使用 merge 合并时有 4 种方式 how = [‘left’, ‘right’, ‘outer’, ‘inner’],默认为 inner。
示例 - merge 的使用
- 默认以 how=’inner’ 进行合并
import pandas as pd # 导包
# 创建 DataFrame 对象
left=pd.DataFrame({'key':['k0','k1','k2','k3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3'],
}) # 通过字典方式传入3个列
right=pd.DataFrame({'key':['k0','k1','k4','k3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3'],
})
# 合并
pd.merge(left, right) # 默认合并,此处以 'key' 作为公共部分(可能会丢数据)
# 语句说明:
# 先看left、right这两内容 是否有相同名字这一列,
# 对相同列 key 的数据做交集,再把交集所对应的其他列数据合并到结果中去
- 参数 how=’outer’ 进行合并
pd.merge(left, right, how='outer') # 数据全部保留
- 参数 how=’left’ 进行合并
pd.merge(left, right, how='left') # 左侧的全部保留
- 参数 how=’right’ 进行合并
pd.merge(left, right, how='right')# 右侧的全部保留
—— 说明:本文写于 7.30~8.1,文中内容基于 python3,使用工具 Jupyter Notebook
编写的代码
(本文中使用函数前的 np 代表 Numpy 的别名,pd 为 Pandas 的别名,每次使用前需导入相关模块)