Pandas
在数据分析工作中,Pandas 的使用频率是很高的,一方面是因为 Pandas 提供的基础数据结构 DataFrame 与 json 的契合度很高,转换起来就很方便。另一方面,如果我们日常的数据清理工作不是很复杂的话,通常用几句 Pandas 代码就可以对数据进行规整。
Pandas 可以说是基于 NumPy 构建的含有更高级数据结构和分析能力的工具包。在 NumPy 中数据结构是围绕 ndarray 展开的。
数据结构:Series 和 DataFrame
Series 是个定长的字典序列。 Series 有两个基本属性:index 和 values。
import pandas as pd
from pandas import Series, DataFrame
x1 = Series([1,2,3,4])
x2 = Series(data=[1,2,3,4], index=['a', 'b', 'c', 'd'])
print(x1)
print(x2)
结果输出:
0 1
1 2
2 3
3 4
dtype: int64
a 1
b 2
c 3
d 4
dtype: int64
结果中可以看出输出的内容是键值对应的,且默认情况下,键是从0开始递增的自然数。我们还可以使用字典方式来创建 Series。
d = {'a':1, 'b':2, 'c':3, 'd':4}
x3 = Series(d)
print(x3)
结果输出:
a 1
b 2
c 3
d 4
dtype: int64
DataFrame 类型数据结构类似数据库表。 它包括了行索引和列索引,我们可以将 DataFrame 看成是由相同索引的 Series 组成的字典类型。
import pandas as pd
from pandas import Series, DataFrame
data = {'Chinese': [66, 95, 93, 90,80],'English': [65, 85, 92, 88, 90],'Math': [30, 98, 96, 77, 90]}
df1 = DataFrame(data)
df2 = DataFrame(data, columns=['English', 'Math', 'Chinese'])
df3 = DataFrame(data, index=['ZhangFei', 'GuanYu', 'ZhaoYun', 'HuangZhong', 'DianWei'], columns=['English', 'Math', 'Chinese'])
print(df1)
print(df2)
print(df3)
结果输出
Chinese English Math
0 66 65 30
1 95 85 98
2 93 92 96
3 90 88 77
4 80 90 90
English Math Chinese
0 65 30 66
1 85 98 95
2 92 96 93
3 88 77 90
4 90 90 80
English Math Chinese
ZhangFei 65 30 66
GuanYu 85 98 95
ZhaoYun 92 96 93
HuangZhong 88 77 90
DianWei 90 90 80
由此看出,df2 可以重新对列进行顺序调整,df3 可以为行增加一个可识别的索引。那么下面再看下在处理数据过程中 Series 和 DataFrame 的用法。
数据导入和输出
Pandas 允许直接从 xlsx,csv 等文件中导入数据,也可以输出到 xlsx, csv 等文件,非常方便。
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'))
score.to_excel('data1.xlsx')
print(score)
在执行过程中,发现报错,缺少 xlrd 和 openpyxl(第一次只报了 xlrd 缺失,安装后会报 openpyxl 缺失)。执行 pip3 install 进行安装。
全部安装完毕后执行,可以看到所读取的 Excel 的数据内容,并且会在同目录下生成一个 data1.xlsx 的文件,生成的文件与源文件最大的不同是多了一列默认索引。
在原来代码基础上做个调整:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '职位', '邮箱', '手机号'])
score.to_excel('data1.xlsx')
print(score)
根据输出结果,可以看出,生成的新文件和打印的数据均重新将列进行了排序。
数据清洗
数据清洗是数据准备过程中必不可少的环节. 我这里按课程中老师最后留的课后作业作为数据样本。用之前读取 Excel 文件的代码执行后,结果输出:
姓名 语文 数学 英语
0 张飞 66 65 NaN
1 关羽 95 85 98.0
2 赵云 95 92 96.0
3 黄忠 90 88 77.0
4 典韦 80 90 90.0
一般,数据清洗会遇到如下一种或几种情况。
1. 删除行或列
Pandas 提供了一个便捷的方法 drop() 函数来删除我们不想要的列或行。比如我们想把“语文”这列删掉。
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.drop(columns=['语文'])
print(df)
结果输出:
姓名 数学 英语
0 张飞 65 NaN
1 关羽 85 98.0
2 赵云 92 96.0
3 黄忠 88 77.0
4 典韦 90 90.0
想把“张飞”这行删掉(即第一行)。
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.drop(index=[0])
print(df)
结果输出:
姓名 语文 数学 英语
1 关羽 95 85 98.0
2 赵云 95 92 96.0
3 黄忠 90 88 77.0
4 典韦 80 90 90.0
从结果可以看出,数据索引 0 的这行已经被删除了。
2. 重命名列名
如果你想对 DataFrame 中的 columns 进行重命名,可以直接使用 rename(columns=new_names, inplace=True) 函数,比如我把列名 英语 改成 English。
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.rename(columns={'英语': 'English'})
print(df)
结果输出:
姓名 语文 数学 English
0 张飞 66 65 NaN
1 关羽 95 85 98.0
2 赵云 95 92 96.0
3 黄忠 90 88 77.0
4 典韦 80 90 90.0
3. 去重
数据采集可能存在重复的行,这时只要使用 drop_duplicates() 就会自动把重复的行去掉。
df = df.drop_duplicates()
4. 格式化
4.1 更改数据格式
这是个比较常用的操作,因为很多时候数据格式不规范,我们可以使用 astype 函数来规范数据格式,比如我们把语文字段的值改成 str 类型,或者 int64 可以这么写:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score['语文'].astype('str')
print(df)
结果输出:
0 66
1 95
2 95
3 90
4 80
4.2 数据间的空格
有时候我们先把格式转成了 str 类型,是为了方便对数据进行操作,这时想要删除数据间的空格,我们就可以使用 strip 函数:
#删除左右两边空格
df2['语文']=df2['语文'].map(str.strip)
#删除左边空格
df2['语文']=df2['语文'].map(str.lstrip)
#删除右边空格
df2['语文']=df2['语文'].map(str.rstrip)
如果数据里有某个特殊的符号,我们想要删除怎么办?同样可以使用 strip 函数,比如 语文 字段里有美元符号,我们想把这个删掉,可以这么写:
df2['语文']=df2['语文'].str.strip('$')
4.3 大小写转换
大小写是个比较常见的操作,比如人名、城市名等的统一都可能用到大小写的转换,在 Python 里直接使用 upper(), lower(), title() 函数,方法如下:
#全部大写
df2.columns = df2.columns.str.upper()
#全部小写
df2.columns = df2.columns.str.lower()
#首字母大写
df2.columns = df2.columns.str.title()
4.4 查找空值
数据量大的情况下,有些字段存在空值 NaN 的可能,这时就需要使用 Pandas 中的 isnull 函数进行查找。比如,刚刚的数据例子:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.isnull()
print(df)
结果输出:
姓名 语文 数学 英语
0 False False False True
1 False False False False
2 False False False False
3 False False False False
4 False False False False
如果我想知道哪列存在空值,可以使用 df.isnull().any(),结果如下:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.isnull().any()
print(df)
结果输出:
姓名 False
语文 False
数学 False
英语 True
dtype: bool
5. 使用 apply 函数对数据进行清洗
apply 函数是 Pandas 中自由度非常高的函数,使用频率也非常高。比如我们想对 name 列的数值都进行大写转化可以用:
df['name'] = df['name'].apply(str.upper)
也可以 apply 一个自定义的函数。
数据统计
在数据清洗后,我们就要对数据进行统计了。Pandas 和 NumPy 一样,都有常用的统计函数,如果遇到空值 NaN,会自动排除。
常用的统计函数包括:
- count(): 统计个数,空值 NaN 不计算。
- describe(): 一次性输出多个统计指标,包括 count,mean,std,min,max等
- min(): 最小值
- max(): 最大值
- sum(): 总和
- mean(): 平均值
- median(): 中位数
- var(): 方差
- std(): 标准差
- argmin(): 统计最小值的索引位置
- argmax(): 统计最大值的索引位置
- idxmin(): 统计最小值的索引值
- idxmax(): 统计最大值的索引值
其中有一个 describe() 函数,统计函数千千万,describe() 函数最简便。它是个统计大礼包,可以快速让我们对数据有个全面的了解。例如:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
df = score.describe()
print(df)
结果输出:
语文 数学 英语
count 5.000000 5.000000 4.000000
mean 85.200000 84.000000 90.250000
std 12.357184 10.931606 9.464847
min 66.000000 65.000000 77.000000
25% 80.000000 85.000000 86.750000
50% 90.000000 88.000000 93.000000
75% 95.000000 90.000000 96.500000
实践:增加一列总分项
根据上面的数据样例,我期望对数据最后增加一列总和列,程序如下:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
# 张飞的英语是 NaN, 这里给 NaN 赋予默认值 0。一般数据分析的场景下,也可以使用平均值或者中位数来填充无法获取的数据。
score['英语'].fillna(0, inplace=True)
# 自定义一个加总的函数
def total_score(df):
df['总分'] = df['语文'] + df['英语'] + df['数学']
return df
df = score.apply(total_score, axis=1)
print(df)
结果输出:
姓名 语文 数学 英语 总分
0 张飞 66 65 0.0 131.0
1 关羽 95 85 98.0 278.0
2 赵云 95 92 96.0 283.0
3 黄忠 90 88 77.0 255.0
4 典韦 80 90 90.0 260.0
在练习中,我根据评论中其他人的写法,又调整了程序,并增加了一个需求是增加一行数据,用于总计语文、数学、英语、总分的总和。代码如下:
import pandas as pd
from pandas import Series, DataFrame
score = DataFrame(pd.read_excel('data.xlsx'), columns=['姓名', '语文', '数学', '英语'])
# 张飞的英语是 NaN, 这里给 NaN 赋予默认值 0。一般数据分析的场景下,也可以使用平均值或者中位数来填充无法获取的数据。
score['英语'].fillna(0, inplace=True)
score['总分'] = score.sum(axis=1)
df1 = score.sum(axis=0).drop(index='姓名')
df2 = score.append(df1, ignore_index=True)
df2['姓名'].fillna('总计', inplace=True)
print(df2)
结果输出:
姓名 语文 数学 英语 总分
0 张飞 66 65 0.0 131.0
1 关羽 95 85 98.0 278.0
2 赵云 95 92 96.0 283.0
3 黄忠 90 88 77.0 255.0
4 典韦 80 90 90.0 260.0
5 总计 426 420 361.0 1207.0
补充:axis 说明
这里面有个概念让我也懵了一下,就是axis的设置,翻查了资料(资料来源简书:行走的程序猿),总结如下:
从这张图可以看出:
axis=0、axis=index,指的是遍历每个index、行号,即在纵向上遍历每列,所以做sum()、mean()等运算时,是对每列数据做操作,而drop(index, axis=0),传入的参数指定了某一行号,所以会在纵向上遍历每列,去掉行号对应位置的数据。
axis=1、axis=columns,指的是遍历每个columns、列名,即在横向上遍历每行,所以做sum()、mean()等运算时,是对每行数据做操作,而drop(col, axis=1),传入的参数指定了某一列名,所以会在横向上遍历每行,去掉列名对应位置的数据。
总结
今天根据教程,尝试对一个数据表进行程序级别的数据批量处理,这里使用的都是基础方法,在实际的数据准备过程中,数据清洗将会有更多的复杂情况,但基本都是这些方法的组合使用。