Pandas数据分析技能总结
载入和配置常见库
In [1]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
In [2]:
# plt.style.use('seaborn') # 改变图像风格
plt.rcParams['font.family'] = ['Arial Unicode MS', 'Microsoft Yahei', 'SimHei', 'sans-serif'] # 解决中文乱码
# plt.rcParams['axes.unicode_minus'] = False # simhei黑体字 负号乱码 解决
数据载入
常见数据形式
- CSV:.csv
- Excel:.xslx
In [ ]:
# a = pd.read_csv('data/a.csv', encoding='GBK')
# b = pd.read_excel('data/a.xlsx', None) # 读取所有单元表
# b['Sheet1'] # 读取某一单元表
测试数据
In [3]:
# DataFrame表格
df = pd.DataFrame({
'name': ['张三','李四','王五','李四','王五','王五','赵六'],
'chinese': [18, 53, 67, 63, 39, 70, 94],
'math': [82, 63, 41, 59, 46, 39, 58],
'english': [68, 52, 90, 86, 60, 98, 64],
'test': ['一','一','一','二','二','三','一']
})
df
Out[3]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
查询
检查数据
检查列缺失值,和列数据类型是否正常
In [4]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 5 columns):
name 7 non-null object
chinese 7 non-null int64
math 7 non-null int64
english 7 non-null int64
test 7 non-null object
dtypes: int64(3), object(2)
memory usage: 360.0+ bytes
快速统计
计算指标,发现异常值
In [5]:
df.describe()
Out[5]:
chinese | math | english | |
---|---|---|---|
count | 7.000000 | 7.000000 | 7.000000 |
mean | 57.714286 | 55.428571 | 74.000000 |
std | 24.260491 | 14.998413 | 17.281975 |
min | 18.000000 | 39.000000 | 52.000000 |
25% | 46.000000 | 43.500000 | 62.000000 |
50% | 63.000000 | 58.000000 | 68.000000 |
75% | 68.500000 | 61.000000 | 88.000000 |
max | 94.000000 | 82.000000 | 98.000000 |
显示前几行和后几行
In [10]:
df.head(3)
Out[10]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
In [11]:
df.tail(2)
Out[11]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
查询表格索引和形状
In [9]:
df.index # 行索引
Out[9]:
RangeIndex(start=0, stop=7, step=1)
In [12]:
df.columns # 列索引
Out[12]:
Index(['name', 'chinese', 'math', 'english', 'test'], dtype='object')
In [13]:
df.shape # 形状
Out[13]:
(7, 5)
索引查询和切片查询
In [14]:
df
Out[14]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
In [19]:
# 传统方式,查询列比较方便
df['name'] # 查询多列
df[['name', 'test']] # 查询多列
# loc方式
# 索引查询
df.loc[:,'name'] # 查询单列
df.loc[[1, 2, 4], ['name', 'chinese', 'test']] # 多行多列
# 切片查询
df.loc[2:5, 'chinese':'english'] # 查询连续值,loc切片包含结束值
Out[19]:
chinese | math | english | |
---|---|---|---|
2 | 67 | 41 | 90 |
3 | 63 | 59 | 86 |
4 | 39 | 46 | 60 |
5 | 70 | 39 | 98 |
布尔查询(过滤查询)
查询所有语文成绩及格同学的信息
In [21]:
df[df['chinese'] >= 60]
Out[21]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
缺失值处理和值类型转换
视图和副本
In [27]:
df2 = df.copy()
df2
Out[27]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
缺失值表示
np.nan系统底层是浮点数
In [28]:
df2.loc[4, 'english'] = np.nan
df2
Out[28]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68.0 | 一 |
1 | 李四 | 53 | 63 | 52.0 | 一 |
2 | 王五 | 67 | 41 | 90.0 | 一 |
3 | 李四 | 63 | 59 | 86.0 | 二 |
4 | 王五 | 39 | 46 | NaN | 二 |
5 | 王五 | 70 | 39 | 98.0 | 三 |
6 | 赵六 | 94 | 58 | 64.0 | 一 |
缺失值填充
fillna()
注意:没有修改原数据
In [29]:
df2.fillna(0)
Out[29]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68.0 | 一 |
1 | 李四 | 53 | 63 | 52.0 | 一 |
2 | 王五 | 67 | 41 | 90.0 | 一 |
3 | 李四 | 63 | 59 | 86.0 | 二 |
4 | 王五 | 39 | 46 | 0.0 | 二 |
5 | 王五 | 70 | 39 | 98.0 | 三 |
6 | 赵六 | 94 | 58 | 64.0 | 一 |
值类型转换
astype()
常见类型有:np.int , np.float
字符串类型不能转为数值型
In [39]:
# df2.fillna(0).astype(np.int) # 报错,只能将非字符串型列转为整型
df2.fillna(0)[['chinese', 'math', 'english']].astype(np.int)
df2[['chinese', 'math', 'english']].fillna(0).astype(np.int)
Out[39]:
chinese | math | english | |
---|---|---|---|
0 | 18 | 82 | 68 |
1 | 53 | 63 | 52 |
2 | 67 | 41 | 90 |
3 | 63 | 59 | 86 |
4 | 39 | 46 | 0 |
5 | 70 | 39 | 98 |
6 | 94 | 58 | 64 |
分组聚合
- 分组:groupby()
- 聚合:mean(), size()
In [40]:
df
Out[40]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
In [41]:
df.groupby('name').mean()
Out[41]:
chinese | math | english | |
---|---|---|---|
name | |||
张三 | 18.000000 | 82.0 | 68.000000 |
李四 | 58.000000 | 61.0 | 69.000000 |
王五 | 58.666667 | 42.0 | 82.666667 |
赵六 | 94.000000 | 58.0 | 64.000000 |
In [43]:
x = df.groupby(['name', 'test'])[['chinese', 'math']].mean()
x
Out[43]:
重塑,轴向旋转
unstack()
In [44]:
x.unstack()
Out[44]:
分组、聚合、旋转:实现透视表和交叉表
In [48]:
# 分组聚合实现透视表
# 分组、mean聚合、选择、填充缺失值、转值类型
df.groupby(['name', 'test'])[['chinese', 'math']].mean().unstack().fillna(0).astype(np.int)
Out[48]:
In [54]:
# 分组聚合实现交叉表
# 分组,size聚合,选择,填充缺失值,值类型转换
df.groupby(['name', 'test'])[['chinese', 'math']].size().unstack().fillna(0).astype(np.int)
df.groupby(['name', 'test']).size().unstack().fillna(0).astype(np.int)
Out[54]:
test | 一 | 三 | 二 |
---|---|---|---|
name | |||
张三 | 1 | 0 | 0 |
李四 | 1 | 0 | 1 |
王五 | 1 | 1 | 1 |
赵六 | 1 | 0 | 0 |
透视表方法:平均值
mean,len
# 示例:
tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'], columns='smoker', margins=True, aggfunc=len, fill_value=0)
In [56]:
df.pivot_table(['chinese', 'math'], index='name', columns='test', fill_value=0)
Out[56]:
交叉表:个数
# 示例
pd.crosstab(tips.time, [tips.smoker, tips.day], margins=True)
In [58]:
pd.crosstab(df.name, df.test, margins=True)
Out[58]:
test | 一 | 三 | 二 | All |
---|---|---|---|---|
name | ||||
张三 | 1 | 0 | 0 | 1 |
李四 | 1 | 0 | 1 | 2 |
王五 | 1 | 1 | 1 | 3 |
赵六 | 1 | 0 | 0 | 1 |
All | 4 | 1 | 2 | 7 |
表格操作
索引修改
- 将普通列转为行索引:.set_index()
- 行索引转为普通列(删除行索引):reset_index()
- 修改行列索引:.rename()
排序
- 按值排序:.sort_values()
- 按索引排序:.sort_index()
In [59]:
df
Out[59]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
In [62]:
y = df.set_index('name') # 将1列数据设为行索引
y
df.set_index('name', append=True) # 保留原索引,创建新索引
Out[62]:
chinese | math | english | test | ||
---|---|---|---|---|---|
name | |||||
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
行索引还原为数据列
In [63]:
y
Out[63]:
chinese | math | english | test | |
---|---|---|---|---|
name | ||||
张三 | 18 | 82 | 68 | 一 |
李四 | 53 | 63 | 52 | 一 |
王五 | 67 | 41 | 90 | 一 |
李四 | 63 | 59 | 86 | 二 |
王五 | 39 | 46 | 60 | 二 |
王五 | 70 | 39 | 98 | 三 |
赵六 | 94 | 58 | 64 | 一 |
In [66]:
df.set_index('name').reset_index() # 行索引还原为列数据
df.set_index('name').reset_index(drop=True) # drop=True,删掉行索引列
Out[66]:
chinese | math | english | test | |
---|---|---|---|---|
0 | 18 | 82 | 68 | 一 |
1 | 53 | 63 | 52 | 一 |
2 | 67 | 41 | 90 | 一 |
3 | 63 | 59 | 86 | 二 |
4 | 39 | 46 | 60 | 二 |
5 | 70 | 39 | 98 | 三 |
6 | 94 | 58 | 64 | 一 |
修改行列索引
In [71]:
df3 = df.rename(index={0: 'aaa', 1: 'bbb'}, columns={'name': '姓名', 'chinese': '语文'})
df3
Out[71]:
姓名 | 语文 | math | english | test | |
---|---|---|---|---|---|
aaa | 张三 | 18 | 82 | 68 | 一 |
bbb | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
In [72]:
df3.loc['aaa']
Out[72]:
姓名 张三
语文 18
math 82
english 68
test 一
Name: aaa, dtype: object
排序
- 按索引排序,按值排序
- 按行排序,按列排序
- 升序,降序
按索引排序
In [76]:
df
df.sort_index() # 默认:按行索引排序,升序
df.sort_index(ascending=False) # 降序
df.sort_index(axis=1, ascending=False) # 按列索引排序,降序
Out[76]:
test | name | math | english | chinese | |
---|---|---|---|---|---|
0 | 一 | 张三 | 82 | 68 | 18 |
1 | 一 | 李四 | 63 | 52 | 53 |
2 | 一 | 王五 | 41 | 90 | 67 |
3 | 二 | 李四 | 59 | 86 | 63 |
4 | 二 | 王五 | 46 | 60 | 39 |
5 | 三 | 王五 | 39 | 98 | 70 |
6 | 一 | 赵六 | 58 | 64 | 94 |
按值排序
In [79]:
df.sort_values(by='math') # 指定某列排序
df.sort_values(by='math', ascending=False) # 降序
Out[79]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
6 | 赵六 | 94 | 58 | 64 | 一 |
4 | 王五 | 39 | 46 | 60 | 二 |
2 | 王五 | 67 | 41 | 90 | 一 |
5 | 王五 | 70 | 39 | 98 | 三 |
Pandas绘图(结合Matplotlib)
柱状
In [80]:
df.plot.bar()
Out[80]:
<matplotlib.axes._subplots.AxesSubplot at 0x9009da0>
In [88]:
df.plot.bar(
stacked=True,
figsize=(18, 7),
alpha=0.5,
)
# matplotlib和pandas结合操作
plt.title('图像标题', fontsize=24)
plt.xticks(df.index, ['一','二','三','四','五','六','七'])
plt.show()
自定义函数
.apply()
用在什么地方:
当你想反复执行一个数据操作时,首先考虑能否使用Pandas自带的方法(交叉表、透视表、分组聚合、其他表格操作)实现,如果不能实现,就自定义一个函数使用apply调用自定义函数
如果不方便使用自定义函数,那就使用for循环遍历
注意:先用一组数据中的一个手动实现一遍程序,再封装为函数,不容易出错
例:查询语文成绩不及格的同学信息
In [89]:
df
Out[89]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
2 | 王五 | 67 | 41 | 90 | 一 |
3 | 李四 | 63 | 59 | 86 | 二 |
4 | 王五 | 39 | 46 | 60 | 二 |
5 | 王五 | 70 | 39 | 98 | 三 |
6 | 赵六 | 94 | 58 | 64 | 一 |
使用Pandas原生方法实现
In [93]:
df['chinese'] < 60
Out[93]:
0 True
1 True
2 False
3 False
4 True
5 False
6 False
Name: chinese, dtype: bool
In [92]:
df[df['chinese'] < 60]
Out[92]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
4 | 王五 | 39 | 46 | 60 | 二 |
DataFrame自定义函数原理
In [110]:
# Series自定义函数原理
def aaa(x):
# return x
return type(x)
df['chinese'].apply(aaa) # Series调用
Out[110]:
0 <class 'int'>
1 <class 'int'>
2 <class 'int'>
3 <class 'int'>
4 <class 'int'>
5 <class 'int'>
6 <class 'int'>
Name: chinese, dtype: object
In [120]:
# DataFrame自定义函数原理
def aaa(x):
# DataFrame返回值
# return x
# return type(x)
# return x[0]
return(type(x[0]))
df.apply(aaa) # DataFrame
df.apply(aaa, axis=0) # 按行运算,同上
df.apply(aaa, axis=1) # 按列运算
Out[120]:
0 <class 'str'>
1 <class 'str'>
2 <class 'str'>
3 <class 'str'>
4 <class 'str'>
5 <class 'str'>
6 <class 'str'>
dtype: object
使用自定义函数实现
In [116]:
# 方法1:Series实现
def bbb(x):
# if x < 60:
# return True
# else:
# return False
# 三元表达式
return True if x < 60 else False
df['chinese'].apply(bbb)
df[df['chinese'].apply(bbb)]
Out[116]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
4 | 王五 | 39 | 46 | 60 | 二 |
In [123]:
# 方法2:DataFrame实现
def ccc(x):
if x['chinese'] < 60:
return True
else:
return False
# 三元表达式
return True if x['chinese'] < 60 else False
df.apply(ccc, axis=1) # 按列运算
df[df.apply(ccc, axis=1)]
Out[123]:
name | chinese | math | english | test | |
---|---|---|---|---|---|
0 | 张三 | 18 | 82 | 68 | 一 |
1 | 李四 | 53 | 63 | 52 | 一 |
4 | 王五 | 39 | 46 | 60 | 二 |