文章目录
1. pandas 介绍
pandas 是 Python 的数据分析核心库。它提供了一系列能够快速、便捷地处理结构化数据的数据结构和函数。pandas 兼具 Numpy 高性能的数组计算功能以及电子表格和关系数据库灵活的数据处理能力。它提供了复杂精细的索引功能,以便便捷地完成重塑、切片和切块、聚合以及选取数据子集等操作。
统计分析是数据分析的重要组成部分,它几乎贯穿了整个数据分析的流程。运用统计方法,将定量与定性结合,进行的研究活动叫作统计分析。统计分析除了包含单一数值型特征的数据集中趋势、离散趋势和峰度与偏度等统计知识外,还包含了多个特征比较计算等知识。
2. 读写不同数据源数据
数据读取是进行数据预处理、建模与分析的前提。不同的数据源,需要使用不同的函数读取。pandas 内置了 10 余种数据源读取函数和对应的数据写入函数。常见的数据源有三种,分别是数据库数据、文本文件(包括一般文本文件和 CSV 文件)和 Excel 文件。掌握这三种数据源读取方法,便能够完成 80% 左右的数据读取工作。
2.1 读写数据库数据
除了 pandas 库外,还需要使用 SQLAlchemy 库建立对应的数据库连接。SQLAlchemy 配合相应数据库的 Python 连接工具(例如,MySQL 数据库需要安装 mysqlclient 或者 pymsql 库,Oracle 数据库需要安装 cx_oracle 库),使用 create_engine 函数,建立一个数据库连接。在 create_engine 中输入的是一个连接字符串。
# SQLAlchemy连接MySQL数据库代码
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:[email protected]:3306/testdb?charset=utf8')
print(engine)
在使用 Python 的 SQLAlchemy 时,MySQL 和 Oracle 数据库连接字符串的格式如下:
数据库产品名+连接工具名://用户名:密码@IP地址:数据库端口号/数据库名称?charset=数据库数据编码
2.1.1 读取
函数 | 说明 |
---|---|
read_sql | 既能读取数据库中的某一个表,也能实现查询操作 |
read_sql_table | 只能读取数据库的某一个表,不能实现查询操作 |
read_sql_query | 只能实现查询操作,不能读取数据库中的某一个表 |
pandas.read_sql(sql, con, index_col=None, coerce_float=True, params=None, parse_dates=None,
columns=None, chunksize=None)
pandas.read_sql_table(table_name, con, schema=None, index_col=None, coerce_float=True, parse_dates=None,
columns=None, chunksize=None)
pandas.read_sql_query(sql, con, index_col=None, coerce_float=True, params=None, parse_dates=None,
chunksize=None)
# 使用read_sql_table、read_sql_qurey、read_sql函数读取数据库数据
import pandas as pd
formlist = pd.read_sql_query('show tables',con=engine)
print('testdb 数据库中数据表清单为:\n',formlist)
detail1 = pd.read_sql_table('meal_order_detail1',con=engine)
print('meal_order_detail1 表的长度为:',len(detail1))
detail2 = pd.read_sql('select * from meal_order_detail2',con=engine)
print("meal_order_detail2 表的长度为:",len(detail2))
detail3 = pd.read_sql('meal_order_detail3',con=engine)
print('meal_order_detail3 表的长度为:',len(detail3))
2.1.2 存储
DataFrame.to_sql(self, name, con, schema=None, if_exists='fail', index=True, index_label=None,
chunksize=None, dtype=None, method=None)
参数 | 说明 |
---|---|
name | 接收 string,代表数据库表名。 |
if_exists | 接收 fail、replace 和 append。默认 fail。fail 表示如果表名存在,则不执行写入操作;replace 表示如果存在,则将原数据表删除,再重新创建;append 则表示在原数据库表基础上追加数据。 |
index | 接收 boolean,默认为 True。表示是否将行索引作为数据写入数据库。 |
# 使用to_sql方法写入数据
detail1.to_sql('test1',con=engine,index=False,if_exists='replace')
formlist1 = pd.read_sql('show tables',con=engine)
print('新增一个表格后,testdb 数据库数据表清单为:\n',formlist1)
2.2 读写文本文件
文本文件是一种由若干行字符构成的计算机文件,它是一种典型的顺序文件。CSV 是一种用分隔符分隔的文件格式,因为其分隔符不一定是逗号,因此又被称为字符分隔文件。文件以纯文本的形式存储表格数据(数字和文本)。它是一种通用、相对简单的文件格式。
2.2.1 读取
pandas 提供了 read_table 函数读取文本文件,提供了 read_csv 函数来读取 CSV 文件。因为 CSV 文件也是一种文本文件,所以两个函数都可以读取 CSV 文件。
pandas.read_csv(filepath_or_buffer, sep=',', delimiter=None, header='infer', names=None, index_col=None,
usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None,
converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None,
skipfooter=0,nrows=None, na_values=None,keep_default_na=True, na_filter=True, verbose=False,
skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False,
date_parser=None, dayfirst=False, cache_dates=True, iterator=False, chunksize=None,
compression='infer', thousands=None, decimal: str = '.', lineterminator=None, quotechar='"',
quoting=0, doublequote=True, escapechar=None, comment=None, encoding=None, dialect=None,
error_bad_lines=True, warn_bad_lines=True, delim_whitespace=False, low_memory=True,
memory_map=False, float_precision=None)
pandas.read_table(filepath_or_buffer, sep='\t', delimiter=None, header='infer', names=None,
index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True,
dtype=None, engine=None, converters=None, true_values=None, false_values=None,
skipinitialspace=False, skiprows=None, skipfooter=0, nrows=None, na_values=None,
keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True,
parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None,
dayfirst=False, cache_dates=True, iterator=False, chunksize=None, compression='infer',
thousands=None, decimal: str = '.', lineterminator=None, quotechar='"', quoting=0,
doublequote=True, escapechar=None, comment=None, encoding=None, dialect=None,
error_bad_lines=True, warn_bad_lines=True, delim_whitespace=False, low_memory=True,
memory_map=False, float_precision=None)
参数名称 | 说明 |
---|---|
filepath_or_buffer | 接收 string。代表文件路径。 |
sep | 接收 string。代表分隔符。read_csv 默认为 “,”,read_table 默认为 “\t” 。 |
header | 接收 int 或 sequence。表示将某行数据作为列名。默认为 infer,表示自动识别。 |
names | 接收 array。表示列名。 |
engine | 接收 c 或者 python 。代表数据解析引擎。默认为 c。 |
nrow | 接收 int。表示读取前 n 行。默认为 None。 |
encoding | 接收特定 string。代表文件的编码格式。默认为 None。 |
# 使用read_table和read_csv函数读取菜品订单信息表
order = pd.read_table('meal_order_info.csv',sep=",",encoding='gbk')
print('使用 read_table 读取菜品订单信息表的长度为:',len(order))
order1 = pd.read_csv('meal_order_info.csv',encoding='gbk')
print('使用 read_csv 读取菜品订单信息表的长度为:',len(order1))
2.2.2 存储
DataFrame.to_csv(self, path_or_buf=None, sep=',',na_rep='',float_format=None, columns=None, header=True,
index=True, index_label=None, mode='w', encoding=None, compression='infer', quoting=None,
quotechar='"', line_terminator=None, chunksize=None, date_format=None, doublequote=True,
escapechar=None, decimal='.')
参数名称 | 说明 |
---|---|
path_or_buf | 接收 string。代表文件路径。无默认。 |
sep | 接收 string。代表分割符、默认为 “,” 。 |
na_rep | 接收 string。代表缺失值。默认为 “”。 |
columns | 接收 list.代表写出的列名。默认为 None。 |
header | 接收 boolean。代表是否将列名写出。默认为 True。 |
index | 接收 boolean。代表是否将行名(索引)写出。默认为 True。 |
encoding | 接收特定 string。代表存储文件的编码格式。默认为 None。 |
# 使用to_csv函数将数据写入CSV文件中
import os
print('添加文件前,当前目录文件列表为:\n',os.listdir('./'))
order.to_csv('newOrder.csv',sep=';',index=False)
print('添加文件后,当前目录文件列表为:\n',os.listdir('./'))
2.3 读写 Excel 文件
Excel 是微软公司的办公软件 Microsoft Office 的组件之一,它可以对数据进行处理、统计分析等操作。其文件保存依照程序版本的不同分为两种。
- Microsoft Office Excel 2007 之前的版本(不包括 2007)默认保存的文件扩展名为 .xls。
- Microsoft Office Excel 2007 之后的版本默认保存的文件扩展名为 .xlsx。
2.3.1 读取
pandas.read_excel(io, sheet_name=0, header=0, names=None, index_col=None, usecols=None, squeeze=False,
dtype=None, engine=None, converters=None, true_values=None, false_values=None, skiprows=None,
nrows=None, na_values=None, keep_default_na=True, verbose=False, parse_dates=False,
date_parser=None, thousands=None, comment=None, skipfooter=0, convert_float=True,
mangle_dupe_cols=True, **kwds)
# 使用read_excel函数读取菜品订单信息表
user = pd.read_excel('users.xlsx')
print('客户信息表的长度为:',len(user))
2.3.2 存储
DataFrame.to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None,
header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None,
merge_cells=True, encoding=None, inf_rep='inf', verbose=True, freeze_panes=None)
# 使用to_ecxcel函数将数据存储为Excel文件
import os
print('添加文件前,当前目录文件列表为:\n',os.listdir('./'))
user.to_csv('newUser.xlsx')
print('添加文件后,当前目录文件列表为:\n',os.listdir('./'))
3. DataFrame 的常用操作
DataFrame 是最常用的 pandas 对象,类似于 Excel 表格。完成数据读取后,数据就以 DataFrame 数据结构存储在内存中。但是此时并不能直接开始统计分析工作,需要使用 DataFrame 的属性与方法对数据的分布、大小等基本的数据状况有一个了解。只有对数据基本状况有了一个深度的了解,才能够依据数据的状况,进行量身定制的统计分析。
3.1 常用属性
属性名称 | 说明 |
---|---|
values | 获取数据框的元素(数据框的 Numpy 表示形式) |
index | 数据框的索引(行标签) |
columns | 数据框的列标签(列名) |
dtypes | 数据框中的元素类型 |
size | 数据框中元素的个数 |
ndim | 数据框的维度数 |
shape | 数据框维度的元组 |
T | 数据框的转置 |
# 查看DataFrame的常用属性
from sqlalchemy import create_engine
import pandas as pd
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/testdb?charset=utf8')
detail = pd.read_sql('meal_order_detail1',con=engine)
print('订单详情表的索引为:',detail.index)
print('订单详情表的所有值为:\n',detail.values)
print('订单详情表的列名为:\n',detail.columns)
print('订单详情表的数据类型为:\n',detail.dtypes)
print('订单详情表的元素个数为:',detail.size)
print('订单详情表的维度数为:',detail.ndim)
print('订单详情表的形状为:',detail.shape)
print('订单详情表转置后的形状为:',detail.T.shape)
3.2 增删改查
3.2.1 查看
(1)基本查看方式
DataFrame 的单列数据为一个 Series。DataFrame 是一个带有标签的二维数组,每个标签相当于每一列的列名。只要以字典访问某一个 key 的值的方式使用对应的列名,就可以实现单列数据的访问。
# 使用字典访问内部数据的方式访问单列数据
order_id = detail['order_id']
print('订单详情表中的order_id的形状为:',order_id.shape)
# 使用访问属性的方式访问单列数据(不建议使用,因为列名与DataFrame属性名可能冲突)
dishes_name = detail.dishes_name
print('订单详情表中的dishes_name的形状为:',dishes_name.shape)
# 获取单列多行数据
dishes_name5 = detail['dishes_name'][:5]
print('订单详情表中的dishes_name的前5个元素为:\n',dishes_name5)
# 获取多列多行数据
orderDish = detail[['order_id','dishes_name']][:5]
print('订单详情表中的order_id和dishes_name的前5个元素为:\n',orderDish)
# 获取多行数据
order5 = detail[:][1:6]
print('订单详情表的1-5行元素为:\n',order5)
# 使用head和tail函数获取多行数据(默认为5行,参数可以输入查看的行数)
print('订单详情表中前5行数据为:\n',detail.head())
print('订单详情表中后5行数据为:\n',detail.tail())
(2)loc、iloc 访问方式
loc 方法是针对 DataFrame 索引名称的切片方法,如果传入的不是索引名称,那么切片操作将无法执行。利用 loc 方法可以实现所有单层索引切片操作。iloc 接收的必须是行索引和列索引的位置。(两者的输入也可以是与被切片的轴长度相同的布尔数组)
DataFrame.loc[行索引名称或条件,列索引名称]
DataFrame.iloc[行索引位置,列索引位置]
# 使用loc、iloc实现单列切片
dishes_name = detail.loc[:,'dishes_name']
print('使用loc提取dishes_name列的元素个数为:',dishes_name.size)
dishes_name2 = detail.iloc[:,3]
print('使用iloc提取第4列的元素个数为:',dishes_name2.size)
# 使用loc、iloc实现多列切片
orderDish1 = detail.loc[:,['order_idr','dishes_name']]
print('使用loc提取order_id和dishes_name列的元素个数为:',orderDish1.size)
orderDish2 = detail.iloc[:,[1,3]]
print('使用iloc提取第2列和第4列的元素个数为:',orderDish2.size)
# 使用loc、iloc实现花式切片(loc与通常的python切片相反,开始和停止都包括在内)
print('列名为order_id和dishes_name行名为3的数据为:\n',detail.loc[3,['order_id','dishes_name']])
print('列名为order_id和dishes_name行名为2,3,4,5,6的数据为:\n',detail.loc[2:6,['order_id','dishes_name']])
print('列位置为1和3,行位置为3的数据为:\n',detail.iloc[3,[1,3]])
print('列位置为1和3,行位置为2,4,5,6的数据为:\n',detail.iloc[2:7,[1,3]])
# 使用loc、iloc实现条件切片
print('detail中order_id为458的dishes_nane为:\n',detail.loc[detail['order_id']=='458',['order_id','dishes_name']])
print('detail中order_id为458的第2,6列数据为:\n',detail.iloc[(detail['order_id']=='458').values,[1,5]])
(3)ix 切片方法
ix 方法更像 loc 和 iloc 两种切片方法的融合。 ix 方法在使用时既可以接收索引名称,也可以接收索引位置。但是当索引名称和位置存在部分重叠时,ix 默认优先识别名称。在数据量很大的时候,ix 的效率低于 loc 和 iloc。
DataFrame.ix[行索引的名称或位置或者条件,列索引名称或位置]
注意:在官方文档中,ix 方法已经过时,不推荐使用
print('列位置为5,行位置为2-6的数据为:\n',detail.ix[2:6,5])
3.2.2 更改
# 将order_id为458的改为45800
detail.loc[detail['order_id']=='458','order_id'] = '45800'
注意:数据更改是直接对 DataFrame 原数据更改,操作无法撤销。所以做出更改前,最好对原数据进行备份。
3.2.3 增加
为 DataFrame 添加一列的方法非常简单,只需要新建一个列索引,并对该索引下的数据进行赋值操作即可。
# 新增一列非定值
detail['payment'] = detail['counts']*detail['amounts']
print('detail新增列payment前5行为:\n',detail['payment'].head())
# 新增一列定值
detail['pay_way'] = "现金支付"
print('detail新增列pay_way前5行为:\n',detail['pay_way'].head())
3.2.4 删除
DataFrame.drop(self, labels=None, axis=0, index=None, columns=None, level=None, inplace=False,
errors='raise')
参数名称 | 说明 |
---|---|
labels | 接收 string 或 array。代表删除的行或列的标签。 |
axis | 接收 0 或 1。代表操作的轴向。0 代表行,1 代表列。默认为 0. |
inplace | 接收 boolean。代表操作是否对原数据生效。默认为 False。 |
# 删除某列
print('删除pay_way列前detail的列索引为:\n',detail.columns)
detail.drop(labels='pay_way',axis=1,inplace=True)
print('删除pay_way列后detail的列索引为:\n',detail.columns)
# 删除某几行
print('删除1-10行前detail的长度为:',len(detail))
detail.drop(labels=range(1,11),axis=0,inplace=True)
print('删除1-10行后detail的长度为:',len(detail))
3.3 描述分析
描述性统计是用来概括、表述事务整体状况,以及事务间关联、类属关系的统计方法。通过几个统计值可以表示一组数据的集中趋势和离散程度。
3.3.1 数值型特征
数值型特征的描述统计主要包括了计算数值型数据的完整情况、最小值、最大值、均值、中位数、四分位数、极差、标准差、方差、协方差和变异系数等。
函数名称 | 说明 | 函数名称 | 说明 |
---|---|---|---|
np.min | 最小值 | np.max | 最大值 |
np.mean | 均值 | np.median | 中位数 |
np.ptp | 极差 | np.std | 标准差 |
np.var | 方差 | np.cov | 协方差 |
函数名称 | 说明 | 函数名称 | 说明 |
---|---|---|---|
min | 最小值 | max | 最大值 |
mean | 均值 | median | 中位数 |
ptp | 极差 | std | 标准差 |
var | 方差 | cov | 协方差 |
sem | 标准误差 | mode | 众数 |
skew | 样本偏度 | kurt | 样本峰度 |
quantile | 四分位数 | count | 非空值数目 |
describe | 描述统计 | mad | 平均绝对离差 |
# 使用np.mean函数计算平均价格
import numpy as np
print('订单详情表中amount的平均值为:',np.mean(detail['amounts']))
# 使用pandas的mean函数计算平均价格
print('订单详情表中amount的平均值为:',detail['amounts'].mean())
# 使用describe方法实现数值型特征的描述性统计
print('订单详情表中counts和amounts两列的描述性统计为:\n',detail[['counts','amounts']].describe())
3.3.2 类别型特征
描述类别型特征的分布状况,可以使用频数统计表。pandas 库中实现频数统计的方法为 value_counts。除了使用频数统计表外,pandas 提供了 category 类,可以使用 astype 方法将目标的特征数据类型转换为 category 类型。describe 方法除了支持传统数值型数据以外,还支持对 category 类型的数据进行描述性统计,4 个统计量分别为列非空元素的数目、类别的数目、数目最多的类别和数目最多类别的数目。
# 对菜品名称进行频数统计
print('订单详情表dishes_name频数统计结果前10为:\n',detail['dishes_name'].value_counts()[0:10])
# 将object数据强制转换为category类型
detail['dishes_name'] = detail['dishes_name'].astype('category')
print('订单详情表dishes_name列转变数据类型后为:',detail['dishes_name'].dtypes)
# category类型特征的描述性统计
print('订单详情表dishes_name的描述统计结果为:\n',detail['dishes_name'].describe())
4. 转换与处理时间序列数据
数据分析的分析对象不仅仅限于数值型和类别型两种,常用的数据类型还包括了时间类型。通过时间类型数据能够获取到对应的年月日和星期等信息。但时间类型数据在读入 Python 后常常以字符串形式出现,无法实现大部分与时间相关的分析。pandas 库继承了 Numpy 库的 datetime64 以及 timedelta64 模块,能够快速实现时间字符串的转换、信息提取和时间运算。
4.1 转换字符串时间为标准时间
类名称 | 说明 |
---|---|
Timestamp | 最基础的时间类。表示某个时间点。绝大多数的场景中的时间数据都是 Timestamp 形式。 |
Period | 表示单个时间跨度,或者某个时间段,例如某一天、某一小时等。 |
Timedelta | 表示不同单位的时间,例如 1d、1.5h、3min、4s 等,而非具体的某个时间段 |
DatetimeIndex | 一组 Timestamp 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引。 |
PeriodtimeIndex | 一组 Period 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引。 |
TimedeltaIndex | 一组 Timedelta 构成的 Index,可以用来作为 Series 或者 DataFrame 的索引。 |
# 转换字符串时间为标准时间
import pandas as pd
order = pd.read_csv('meal_order_info.csv',encoding='gbk')
print('detail的lock_time前5条数据为:\n',order['lock_time'].head())
print('进行转换前订单信息表lock_time的类型为:',order['lock_time'].dtypes)
order['lock_time'] = pd.to_datetime(order['lock_time'])
print('进行转换后订单信息表lock_time的类型为:',order['lock_time'].dtypes)
print('转换后detail的lock_time前5条数据为:\n',order['lock_time'].head())
# Timestamp的最小时间和最大时间
print('最小时间为:',pd.Timestamp.min)
print('最大时间为:',pd.Timestamp.max)
# 时间字符串转换为 DatetimeIndex和PeriodIndex
dateIndex = pd.DatetimeIndex(order['lock_time'])
print('转换为DatetimeIndex后数据的类型为:',type(dateIndex))
periodIndex = pd.PeriodIndex(order['lock_time'],freq = 'S') # fre参数指定时间间隔
print('转换为PeriodIndex后数据的类型为:',type(periodIndex))
参数名称 | 说明 |
---|---|
data | 接收 array。表示 DatetimeIndex 的值。 |
freq | 接收 string。表示时间的间隔频率。Y(年)、M(月)、D(日)、H(小时)、T(分钟)、S(秒)。无默认。 |
start | 接收 string。表示生成规则时间数据的起始点。 |
end | 接收 string。表示生成规则时间数据的终结点。 |
tz | 接收 timezone。表示数据的时区。 |
name | 接收 int、string。指定 DatetimeIndex 的名字。 |
4.2 提取时间序列数据信息
属性名称 | 说明 | 属性名称 | 说明 | 属性名称 | 说明 |
---|---|---|---|---|---|
year | 年 | hour | 小时 | week | 一年中第几周 |
month | 月 | minute | 分钟 | weekofyear | 一年中第几周 |
day | 日 | second | 秒 | dayofyear | 一年中第几天 |
date | 日期 | time | 时间 | dayofweek | 一周第几天 |
quarter | 季节 | weekday_name | 星期名称(将删除) | days_in_month | 返回当月的天数 |
is_leap_year | 是否闰年 | daysinmonth | 返回当月的天数 |
注意:其中 weekday_name 属性将在以后版本删除,而是使用方法 day_name() 替代。weekday() 方法返回星期名称标号, 0 ~ 6 代表 Monday ~ Sunday。
# 提取datetime数据中的时间序列数据
year1 = [i.year for i in order['lock_time']]
print('lock_time中年份数据前5个为:',year1[:5])
month1 = [i.month for i in order['lock_time']]
print('lock_time中月份数据前5个为:',month1[:5])
day1 = [i.day for i in order['lock_time']]
print('lock_time中日期数据前5个为:',day1[:5])
weekday1 = [i.weekday_name for i in order['lock_time']]
print('lock_time中星期名称数据前5个为:',weekday1[:5])
weekday2 = [i.weekday() for i in order['lock_time']]
print('lock_time中星期名标号称数据前5个为:',weekday2[:5])
weekday3 = [i.day_name() for i in order['lock_time']]
print('lock_time中星期名称数据前5个为:',weekday3[:5])
# 提取DatetimeIndex和PeriodIndex中的数据
print('dateIndex中星期名称数据的前5个为:\n',dateIndex.weekday_name[:5])
print('periodIndex中的星期标号数据前5个为:\n',periodIndex.weekday[:5]) # 0-6代表Monday-Sunday
4.3 加减时间数据
时间数据的算数运算涉及 pandas 的 Timedelta 类。Timedelta 是时间相关类中一个异类,不仅能够使用正数,还能够使用负数表示单位时间。使用 Timedelta,可以轻松实现某个时间上加减一段时间。
周期名称 | 单位 | 说明 | 周期名称 | 单位 | 说明 |
---|---|---|---|---|---|
weeks | 无 | 星期 | seconds | s | 秒 |
days | D | 天 | miliseconds | ms | 毫秒 |
hours | h | 小时 | microseconds | us | 微秒 |
minutes | m | 分钟 | nanoseconds | ns | 纳秒 |
# 使用Timedelta实现时间数据的加运算
# 将lock_time数据向后平移一天
time1 = order['lock_time'] + pd.Timedelta(days = 1)
print('lock_time加上一天前数据的前5行为:\n',order['lock_time'].head())
print('lock_time加上一天后数据的前5行为:\n',time1.head())
# 使用Timedelta实现时间数据的减运算
timeDelta = order['lock_time'] - pd.to_datetime('2017-1-1')
print('lock_time减去2017年1月1日0点0时0分后的数据:\n',timeDelta.head())
print('timeDelta的数据类型为:',timeDelta.dtypes)
5. 分组聚合进行组内计算
依据某个或者某几个字段对数据集进行分组,并对各组应用一个函数,无论是聚合还是转换,都是数据分析的常用操作。pandas提供了 groupby 方法,配合 agg 方法或者 apply 方法,能够实现分组聚合操作。
5.1 groupby 方法(分组)
groupby 方法提供的是分组聚合中拆分数据的功能,能够根据索引或者字段对数据进行分组。
DataFrame.groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys= True,
squeeze=False, observed=False)
参数名称 | 说明 |
---|---|
by | 接收 list、string、mapping 或 generator。用于确定进行分组的依据。若传入的是一个函数,则对索引进行计算并分组;若传入的是一个字典或者 Series,则字典或者 Series 的值用来作为分组依据; 若传入的是一个 Numpy 数组,则数组的元素作为分组依据;如果传入的是字符串或者字符串列表,则这些字符串所代表的字段作为分组依据。 |
axis | 接收 int。表示操作的轴向。0(行),1(列),默认为 0。 |
sort | 接收 boolean。表示是否对分组依据、分组标签进行排序。默认为 True。 |
group_keys | 接收 boolean。表示是否显示分组标签的名称。默认为 True。 |
分组后的结果并不能直接查看,而是被存在内存中,输出的是内存地址。实际上,分组后的数据对象 GroupBy 类似于 Series 与 DataFrame,是 pandas 提供的一种对象。
方法名称 | 说明 | 方法名称 | 说明 |
---|---|---|---|
count | 计算分组的数目,包括缺失值。 | head | 返回每组的前 n 个值。n 默认为 5。 |
max | 返回每组的最大值。 | min | 返回每组的最小值。 |
median | 返回每组的中位数。 | mean | 返回每组的均值。 |
size | 返回每组的大小。 | std | 返回每组的标准差。 |
sum | 返回每组的和。 | cumcount | 对每个分组中的组员进行标记,0~n-1 |
# 对菜品订单表依据订单编号分组
import pandas as pd
import numpy as np
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/testdb?charset=utf8')
detail = pd.read_sql('meal_order_detail1',con=engine)
detailGroup = detail[['order_id','counts','amounts']].groupby(by='order_id')
print('分组后的订单详情表为:\n',detailGroup)
# GroupBy类的描述性统计
print('订单详情表分组后前5组每组的均值为:\n',detailGroup.mean().head())
print('订单详情表分组后前5组每组的标准差为:\n',detailGroup.std().head())
print('订单详情表分组后前5组每组的中位数为:\n',detailGroup.median().head())
print('订单详情表分组后前5组每组的大小为:\n',detailGroup.size().head())
5.2 agg 方法(聚合)
agg、aggregate 方法都支持对每个分组应用某函数,包括 Python 内置函数和自定义函数。同时,这两个方法能直接对 DataFrame 进行函数应用操作,因此只需掌握其中一个函数即可。agg 函数能够对 DataFrame 对象进行操作是从 pandas 0.20 版本才开始的。
DataFrame.agg(self, func, axis=0, *args, **kwargs)
DataFrame.aggregate(self, func, axis=0, *args, **kwargs)
# 使用agg求出当前数据对应统计量
print('订单详情表的菜品销量与售价的和与均值为:\n',detail[['counts','amounts']].agg([np.sum,np.mean]))
# 使用agg分别求字段的不同统计量
print('订单详情表的菜品销量总和与售价的均值为:\n',detail.agg({'counts':np.sum,'amounts':np.mean}))
# 使用agg方法求不同字段的不同数目统计量
print('订单详情表中菜品销量总和与售价的总和与均值为:\n',detail.agg({'counts':np.sum,'amounts':[np.mean,np.sum]}))
# 在agg方法中使用自定义函数
# 自定义函数求两倍的和
def DoubleSum(data):
return data.sum()*2
print('订单详情表中菜品销量的两倍总和为:\n',detail.agg({'counts':DoubleSum}))
print('订单详情表中菜品销量的两倍总和为:',detail['counts'].agg(DoubleSum))
# 使用agg方法做简单的聚合
print('订单详情表分组后前5组每组的均值为:\n',detailGroup.agg(np.mean).head())
print('订单详情表分组后前5组每组的标准差为:\n',detailGroup.agg(np.std).head())
# 使用agg方法对分组数据使用不同的聚合函数
print('订单详情表分组后前5组每组菜品总数和售价均值为:\n',detailGroup.agg({'counts':np.sum,'amounts':np.mean}).head())
5.3 apply 方法(聚合)
apply 方法类似于 agg 方法,能够将函数应用于每一列。但是与 agg 方法相比,apply 方法传入的函数只能够作用于整个 DataFrame 或者 Series,而无法像 agg 方法一样能够对不同字段应用不同函数来获取不同结果。
DataFrame.apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds)
# apply方法基本用法
print('订单详情表的菜品销量与售价的均值为:\n',detail[['counts','amounts']].apply(np.mean))
# 使用apply方法进行聚合操作
print('订单详情表分组后前5组每组的均值为:\n',detailGroup.apply(np.mean).head())
print('订单详情表分组后前5组每组的标准差为:\n',detailGroup.apply(np.std).head())插入代码片
5.4 transform 方法(聚合)
transform 方法能够对整个 DataFrame 的所有元素进行操作。
DataFrame.transform(self, func, axis=0, *args, **kwargs)
# 使用transform方法将销量和售价翻倍
print('订单详情表的菜品销量与售价的两倍为:\n',detail[['counts','amounts']].transform(lambda x:x*2).head())
6. 创建透视表与交叉表
数据透视表是数据分析中常见的工具之一,根据一个或多个键值对数据进行聚合,根据行或者列的分组键将数据划分到各个区域。
6.1 pivot_table 函数(透视表)
pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None,
margins=False, dropna=True, margins_name='All', observed=False)
参数名称 | 说明 |
---|---|
data | 接收 DataFrame。表示创建表的数据。 |
values | 接收 string。用于指定要聚合的数据字段名,默认使用全部数据。默认为 None。 |
index | 接收 string 或 list。表示行分组键。默认为 None。 |
columns | 接收 string 或 list。表示列分组键。默认为 None。 |
aggfunc | 接收 functions。表示聚合函数。默认为 mean。 |
dropna | 接收 boolean。表示是否删除全为 NaN 的列。默认为 True。 |
fill_value | 替换缺失值的值。默认为 None。 |
margins | 接收 boolean。默认为 False。设置为 True 后,结果集中会出现默认名为 “ALL” 的行和列。 |
# 使用订单号作为透视表索引制作透视表
detailPivot = pd.pivot_table(detail[['order_id','counts','amounts']],
index='order_id')
print('以order_id作为分组键创建的订单透视表前5行为:\n',detailPivot.head())
# 修改聚合函数后的透视表
detailPivot1 = pd.pivot_table(detail[['order_id','counts','amounts']],
index='order_id',aggfunc=np.sum)
print('以order_id作为分组键创建的订单销量与售价总和透视表前5行为:\n',detailPivot1.head())
# 使用订单号和菜品名称作为索引的透视表
detailPivot2 = pd.pivot_table(detail[['order_id','dishes_name','counts','amounts']],
index=['order_id','dishes_name'],aggfunc=np.sum)
print('以order_id和dishes_name作为分组键创建的订单销量与售价总和透视表前5行为:\n',detailPivot2.head())
# 指定菜品名称为列分组键的透视表
detailPivot3 = pd.pivot_table(detail[['order_id','dishes_name','counts','amounts']],
index='order_id',columns='dishes_name',aggfunc=np.sum)
print('以order_id和dishes_name作为行列分组键创建的透视表前5行为:\n',detailPivot3.head())
# 指定某些列制作透视表
detailPivot4 = pd.pivot_table(detail[['order_id','dishes_name','counts','amounts']],
index='order_id',values='counts',aggfunc=np.sum)
print('以order_id作为分组键counts作为值创建透视表前5行为:\n',detailPivot4.head())
# 对透视表中缺失值进行填充
detailPivot5 = pd.pivot_table(detail[['order_id','dishes_name','counts','amounts']],
index='order_id',columns='dishes_name',aggfunc=np.sum,
fill_value=0)
print('空值填0后以order_id和dishes_name作为行列分组键创建的透视表前5行4列为:\n',detailPivot5.iloc[:5,:4])
# 在透视表中添加汇总数据
detailPivot6 = pd.pivot_table(detail[['order_id','dishes_name','counts','amounts']],
index='order_id',columns='dishes_name',aggfunc=np.sum,
fill_value=0,margins=True)
print('添加margins后以order_id和dishes_name作为行列分组键创建的透视表后5行后4列:\n',detailPivot6.iloc[-5:,-4:])
6.2 crosstab 函数(交叉表)
交叉表是一种特殊的透视表,主要用于计算分组频率。利用 pandas 提供的 crosstab 函数可以制作交叉表。crosstab 函数的参数和 pivot_table 函数基本相同。不同之处在于,对于 crosstab 函数中的 index、columns、values,输入都是 DataFrame 中的一列。
pandas.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False,
margins_name='All', dropna=True, normalize=False)
# 使用crosstab函数制作交叉表
detailCross = pd.crosstab(index=detail['order_id'],
columns=detail['dishes_name'],
values=detail['counts'],
aggfunc=np.sum)
print('以order_id和dishes_name为分组键counts为值的透视表前5行5列为:\n',detailCross.iloc[:5,:5])