统计分析
定义
统计分析是数据分析重要的组成部分,它几乎贯穿了整个数据分析的流程。
应用统计方法,将定量与定性结合,进行的研究活动叫统计分析。统计分析除了
包含单一数值型特征的数据集中趋势、离散趋势和峰度与偏度等统计知识外,还
包含了多个特征比较计算等知识。
导包
pip install pandas
在控制台显示所有数据设置
#显示所有行
pd.set_option("display.max_columns",None)
#显示所有列
pd.set_option("display.max_rows",None)
#显示的长度
pd.set_option("max_colwidth",200)
读写不同数据源的数据
读写文本文件
- 文本文件读取
- 文本文件是一种由若干行字符构成的计算机文件,它是一种典型的顺序文
件。 - csv 是一种逗号分隔的文件格式,因为其分隔符不一定是逗号,又被称为字
符分隔文件,文件以纯文本形式存储表格数据(数字和文本)。 - 注:(1)CSV 文件根据其定义也是一种文本文件;(2)文本文件是字符分隔文件。
使用 read_table 来读取文本文件
pandas.read_table(filepath_or_buffer,sep=’\t’,header=’infer’, names=None,index_col=None,dtype=None,engine=None,nrows=None)
使用 read_csv 函数来读取 csv 文件
pandas.read_csv(filepath_or_buffer,sep=’\t’,header=’infer’, names=None,index_col=None,dtype=None,engine=None,nrows=None)
order = pd.read_csv("meal_order_info.csv",sep=",",encoding="gbk")
read_table 和 read_csv 常用参数及其说明
- read_table 和 read_csv 函数中的 sep 参数是指定文本的分隔符的,如果分隔符
指定错误,在读取数据的时候,每一行数据将连成一片。 - header 参数是用来指定列名的,如果是 None 则会添加一个默认的列名。
- encoding 代表文件的编码格式,常用的编码有 utf-8、utf-16、gbk、gb2312、
gb18030 等。如果编码指定错误数据将无法读取,IPython 解释器会报解析误。
- 文本文件存储
文本文件的存储和读取类似,结构化数据可以通过 pandas 中的 to_csv 函数
实现以 csv 文件格式存储文件
DataFrame.to_csv(path_or_buf=None, sep=’,’, na_rep=”, columns=None, header=True, index=True,index_label=None,mode=’w’,encoding=None)
读写 Excel 文件
- Excel 文件读取:
pandas 提供了 read_excel 函数来读取“xls”“xlsx”两种 Excel 文件。、
pandas.read_excel(io, sheetname=0, header=0, index_col=None, names=None, dtype=None)
- Excel 文件储存:
- 将文件存储为 Excel 文件,可以使用 to_excel 方法。其语法格式如下。
DataFrame.to_excel(excel_writer=None, sheet_name=‘None’, na_rep=”, header=True, index=True, index_label=None, mode=’w’, encoding=None) - to_csv 方法的常用参数基本一致,区别之处在于指定存储文件的文件路径参
数名称为 excel_writer,并且没有 sep 参数,增加了一个 sheetnames 参数用来
指定存储的 Excel sheet 的名称,默认为 sheet1。
DataFrame 的常用操作
查看 DataFrame 的常用属性
基础属性:
查改增删 DataFrame 数据
- 查看访问 DataFrame 中的数据——数据基本查看方式:
-
对单列数据的访问:DataFrame 的单列数据为一个 Series。根据 DataFrame 的定义可以知晓 DataFrame 是一个带有标签的二维数组,每个标签相当每一列的列名。有以下两种方式来实现对单列数据的访问 以字典访问某一个 key 的值的方式使用对应的列名,实现单列数据的访问。
-
以属性的方式访问,实现单列数据的访问。
-
对某一列的某几行访问:访问 DataFrame 中某一列的某几行时,单独一列的 DataFrame可以视为一个 Series(另一种 pandas 提供的类,可以看作是只有一列的 DataFrame),而访问一个 Series 基本和访问一个一维的 ndarray 相同。
-
对多列数据访问:访问 DataFrame 多列数据可以将多个列索引名称视为一个列表,同时访问 DataFrame 多列数据中的多行数据和访问单列数据的多行数据方法基本相同。
-
对某几行访问:
如果只是需要访问 DataFrame 某几行数据的实现方式则和上述的访问多列多行相似,选择所有列,使用“:”代替即可
order5 = detail[:][1:6]
head 和 tail 也可以得到多行数据,但是用这两种方法得到的数据都是从开始或者末尾获取的连续数据。默认参数为访问 5 行,只要在方法后方的“()”中填入访问行数即可实现目标行数的查看。
import pandas as pd
#读取数据
detail = pd.read_excel("output.xlsx")
print(detail)
#取一列数据
amounts = detail["amounts"]
counts = detail["counts"]
print("读取一列数据:\n",amounts)
#取多列数据
#格式:变量名[["列名1","列名2"]]
detail_ac = detail[["amounts","counts"]]
print("读取多列数据:\n",detail_ac.describe())
#关于行的读取
# 格式:变量名.loc[行,列]
detail_ac_b5 = detail.loc[:5,["amounts","counts"]]
print("读取指定行,指定列:\n",detail_ac_b5)
#类别
detail['order_id'] = detail['order_id'].astype("category")
detail['dishes_name'] = detail['dishes_name'].astype("category")
print("订单详情表中关于订单编号和菜品名称的描述性统计结果为:\n"
,detail[["order_id","dishes_name"]].describe())
- 查看访问 DataFrame 中的数据——loc,iloc 访问方式:
- loc 方法是针对 DataFrame 索引名称的切片方法,如果传入的不是索引名称,那么切片
操作将无法执行。利用 loc 方法,能够实现所有单层索引切片操作。loc 方法使用方法
如下。
DataFrame.loc[行索引名称或条件, 列索引名称]
- iloc 和 loc 区别是 iloc 接收的必须是行索引和列索引的位置。iloc 方法的使用方法如下。
DataFrame.iloc[行索引位置, 列索引位置]
- 使用 loc 方法和 iloc 实现多列切片,其原理的通俗解释就是将多列的列名或者位置作为
一个列表或者数据传入。 - 使用 loc,iloc 方法可以取出 DataFrame 中的任意数据。
- 在 loc 使用的时候内部传入的行索引名称如果为一个区间,则前后均为闭区间;iloc 方
法使用时内部传入的行索引位置或列索引位置为区间时,则为前闭后开区间。 - loc 内部还可以传入表达式,结果会返回满足表达式的所有值。
- iloc 可以接收的数据类型不包括 Series
- loc 更加灵活多变,代码的可读性更高,iloc 的代码简洁,但可读性不高。
转换与处理时间序列数据
转换字符串时间为标准时间
- pandas 时间相关的类:
- 在多数情况下,对时间类型数据进行分析的前提就是将原本为字符串的时间转换为标准时间类型。
- pandas 继承了 NumPy 库和 datetime 库的时间相关模块,提供了 6 种时间相关的类。
- Timestamp 类型:
-
其中 Timestamp 作为时间类中最基础的,也是最为常用的。在多数情况下,时间相关的
字符串都会转换成为 Timestamp。pandas 提供了 to_datetime 函数,能够实现这一目标。pd.to_datetime(order[‘lock_time’])
-
值得注意的是,Timestamp 类型时间是有限制的
-
作者计算机中最早只能够表示至 1677 年 9 月 21 日,最晚只能表示至 2262 年 4 月 11日
print('最小时间为:',pd.Timestamp.min)
print('最小时间为:',pd.Timestamp.max)
- DatetimeIndex 与 PeriodIndex 函数:
- 除了将数据字原始 DataFrame 中直接转换为 Timestamp 格式外,还可以将数据单独提取
出来将其转换为 DatetimeIndex 或者 PeriodIndex。 - 转换为 PeriodIndex 的时候需要注意,需要通过 freq 参数指定时间间隔,常用的时间间隔有 Y 为年,M 为月,D 为日,H 为小时,T 为分钟,S 为秒。两个函数可以用来转换
数据还可以用来创建时间序列数据,其参数非常类似。
- DatetimeIndex 与 PeriodIndex 函数及其参数说明:
DatetimeIndex 和 PeriodIndex 两 者 区 别 在 日 常 使 用 的 过 程 中 相 对 较 小 , 其 中
DatetimeIndex 是用来指代一系列时间点的一种数据结构,而 PeriodIndex 则是用来指代
一系列时间段的数据结构。
提取时间序列数据信息
Timestamp 类常用属性:
- 在多数涉及时间相关的数据处理,统计分析的过程中,需要提取时间中的年份,月份等
数据。使用对应的 Timestamp 类属性就能够实现这一目的。 - 结合 Python 列表推导式,可以实现对 DataFrame 某一列时间信息数据的提取。
在 DatetimeIndex 和 PeriodIndex 中提取信息:
- 在 DatetimeIndex 和 PeriodIndex 中提取对应信息可以以类属性方式实现。
- 值得注意的是 PeriodIndex 相比于 DatetimeIndex 少了 weekday_name 属性,所以不能够
用该属性提取星期名称数据。若想要提取信息名称可以通过提取 weekday 属性,而后将
0-6 四个标签分别赋值为 Monday 至 Sunday。
print('dateIndex 中的星期名称数据前 5 个为:\n',dateIndex.weekday_name[:5])
print('periodIndex 中的星期标号数据前 5 个为:',periodIndex.weekday[:5])
加减时间数据
Timedelta 类:
Timedelta 是时间相关的类中的一个异类,不仅能够使用正数,还能够使用负数表示单
位时间,例如 1 秒,2 分钟,3 小时等。使用 Timedelta 类,配合常规的时间相关类能够
轻松实现时间的算术运算。目前 Timedelta 函数中时间周期中没有年和月。所有周期名
称,对应单位及其说明如下表所示。
time1 = order['lock_time']+pd.Timedelta(days = 1)
print('lock_time 在加上一天前前 5 行数据为:\n',order['lock_time'][:5])
print('lock_time 在加上一天前前 5 行数据为:\n',time1[:5])
- 使用 Timedelta ,可以很轻松地实现在某个时间上加减一段时间 。
- 除了使用 Timedelta 实现时间的平移外,还能够直接对两个时间序列进行相减,从而得
出一个 Timedelta。
#将字符日期数据转换为日期数据
order["lock_time"] = pd.to_datetime(order["lock_time"])
order["use_start_time"] = pd.to_datetime(order["use_start_time"])
timeDelta = order['lock_time'] - pd.to_datetime('2017-1-1')
print('lock_time 减去 2017 年 1 月 1 日 0 点 0 时 0 分后的数据:\n',timeDelta[:5])
print('lock_time 减去 time1 后的数据类型为:',timeDelta.dtypes)
汇总多个表数据
import pandas as pd
import xlrd # pip install xlrd
from pandas import DataFrame
#读取数据
wb = xlrd.open_workbook("meal_order_detail.xlsx")
#获取表的名称列表
sheets = wb.sheet_names() #['meal_order_detail1', 'meal_order_detail2', 'meal_order_detail3']
# print(sheets)
#总表数据
total = DataFrame()
#循环遍历所有表,汇总数据
for i in range(len(sheets)):
data = pd.read_excel("meal_order_detail.xlsx",sheet_name=i,index_col=False)
# print(type(data))#<class 'pandas.core.frame.DataFrame'> DataFrame:存储表的一种类型
# print(data.shape[0]) # 2779 3647 3611
#汇总数据
total = total.append(data)
# print(total)
# print(total.shape)
#保存
writer= pd.ExcelWriter("output.xlsx")
total.to_excel(writer,"Sheet1")
writer.save()
剔除数据中的空白值和相同值
删除数据语法
变量名.drop(col_isNull.index[i],axis=1,inplace=True)
- inplace 表示是否在表内进行操作
- print(std_isZero[i]) #值为value,std_isZero.index[i]值为key
标准差为0表示所有数据都为0,不适合做横向比较,不能用于分析
import pandas as pd
import matplotlib as plt
#读取数据
detail = pd.read_excel("output.xlsx")
#获取列数
col = detail.shape[1]
#print(col)
# 获取所有列属性对应的describe,返回值为布尔值
#count是属于详细统计信息describe中的个数统计内容
col_isNull = detail.describe().loc["count"] == 0
#print(col_isNull)
#去除全为空值的列、标准差为0的列
#标准差为0表示所有数据都为0,不适合做横向比较,不能用于分析
#剔除空值
for i in range(len(col_isNull)):
if col_isNull[i]:
#inplace 是否在表内进行操作
detail.drop(col_isNull.index[i],axis=1,inplace=True)
print(col_isNull.index[i])
#剔除0值
#std 是详细统计信息describe中的标准差,标准差为0表示数据没有变化,没有数据分析的意义
std_isZero = detail.describe().loc["std"] ==0
#print(std_isZero)
for i in range(len(std_isZero)):
if std_isZero[i]:
detail.drop(std_isZero.index[i],axis=1,inplace=True)
col = detail.shape[1]
print("剔除空值后的列数:",col)
数据预处理
分组聚合进行组内计算
groupby
原理图:
groupby 方法的参数及其说明:
- 该方法提供的是分组聚合步骤中的拆分功能,能根据索引或字段对数据进行分组。其常
用参数与使用格式如下。
DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
groupby 方法的参数及其说明——by 参数的特别说明:
- 如果传入的是一个函数则对索引进行计算并分组。
- 如果传入的是一个字典或者 Series 则字典或者 Series 的值用来做分组依据。
- 如果传入一个 NumPy 数组则数据的元素作为分组依据。
- 如果传入的是字符串或者字符串列表则使用这些字符串所代表的字段作为分组依据。
GroupBy 对象常用的描述性统计方法:
- 用 groupby 方法分组后的结果并不能直接查看,而是被存在内存中,输出的是内存地址。
实际上分组后的数据对象 GroupBy 类似 Series 与 DataFrame,是 pandas 提供的一种对
象。GroupBy 对象常用的描述性统计方法如下:
示例
import pandas as pd
import numpy as np
#显示所有行
pd.set_option("display.max_columns",None)
#读取数据
data = pd.read_excel("output.xlsx",sheet_name=0)
# print(data.head(1))
lite_data = data[["order_id","counts","amounts"]]
# print(lite_data.head(2))
data_group = lite_data.groupby(by = "order_id")
# print(data_group["order_id"])#<pandas.core.groupby.generic.SeriesGroupBy object at 0x000001A6DDD910B8>
# print(data_group.mean().head())
# print(data_group.std().head())
# print(data_group.size().head())
使用 agg 方法聚合数据
agg 和 aggregate 函数参数及其说明:
- agg,aggregate 方法都支持对每个分组应用某函数,包括 Python 内置函数或自定义函数。
同时这两个方法能够也能够直接对 DataFrame 进行函数应用操作。 - 在正常使用过程中,agg 函数和 aggregate 函数对 DataFrame 对象操作时功能几乎完全相
同,因此只需要掌握其中一个函数即可。它们的参数说明如下表。
DataFrame.agg(func, axis=0, *args, **kwargs)
DataFrame.aggregate(func, axis=0, *args, **kwargs)
agg 方法求统计量:
- 可以使用 agg 方法一次求出当前数据中所有菜品销量和售价的总和与均值,如
detail[['counts','amounts']].agg([np.sum,np.mean]))。
- 对于某个字段希望只做求均值操作,而对另一个字段则希望只做求和操作,可以使用字
典的方式,将两个字段名分别作为 key,然后将 NumPy 库的求和与求均值的函数分别
作为 value,如
detail.agg({'counts':np.sum,'amounts':np.mean}))。
- 在某些时候还希望求出某个字段的多个统计量,某些字段则只需要求一个统计量,此时
只需要将字典对应 key 的 value 变为列表,列表元素为多个目标的统计量即可,如
detail.agg({'counts':np.sum,'amounts':[np.mean,np.sum]}))
agg 方法与自定义的函数:
- 在 agg 方法可传入读者自定义的函数。
- 使用自定义函数需要注意的是 NumPy 库中的函数 np.mean,np.median,np.prod,np.sum,np.std,np.var 能够在 agg 中直接使用,但是在自定义函数中使用 NumPy 库中的这些函数,如果计算的时候是单个序列则会无法得出想要的结果,如果是多列数据同时计算则
不会出现这种问题。 - 使用 agg 方法能够实现对每一个字段每一组使用相同的函数。
- 如果需要对不同的字段应用不同的函数,则可以和 Dataframe 中使用 agg 方法相同。
示例
# agg--对不同的列做不同的聚合函数
#1.一次性进行多个聚合统计
lite_data = data[["counts","amounts"]]
mean = lite_data.agg(np.mean)
# data[["counts","amounts"]]与lite_data.agg([np.mean,np.sum])中的数据要对应
#所有列都执行聚合函数,这样求的是笛卡儿积
mean_sum_list = lite_data.agg([np.mean,np.sum])
# # counts amounts
# # mean 1.108499 44.821361
# # sum 11126.000000 449872.000000
# print(mean_sum_list)
#用字典指定想要进行的聚合函数,求指定的数据
mean_sum_zd = lite_data.agg({"counts":[np.mean,np.max],"amounts":np.sum})
# print(mean_sum_zd)
#自定义聚合操作
def double_sum(value):
return np.sum(value)*2
myfunc_data = lite_data.agg({"counts":double_sum},axis=0)
# print(myfunc_data.head(3))
使用 apply 方法聚合数据
- apply 方法类似 agg 方法能够将函数应用于每一列。不同之处在于 apply 方法相比 agg
方法传入的函数只能够作用于整个 DataFrame 或者 Series,而无法像 agg 一样能够对不
同字段,应用不同函数获取不同结果。 - 使用 apply 方法对 GroupBy 对象进行聚合操作其方法和 agg 方法也相同,只是使用 agg
方法能够实现对不同的字段进行应用不同的函数,而 apply 则不行。
DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
实例
#apply
#每列均值
data_apply1= data[["order_id","counts","amounts"]].apply(np.sum)
# print("订单详情表的菜品销量与售价的总和",data_apply1.head())
#每组的均值
data_group_apply = data[["order_id","counts","amounts"]].groupby("order_id")
# print(data_group_apply)#DataFrameGroupBy--<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000015C82E84A90>
# print('订单详情表中分组后每组的均值:\n',data_group_apply.apply(np.sum).head())
# print('订单详情表中分组后每组的标准差:\n',data_group_apply.apply(np.std).head())
使用 transform 方法聚合数据
- transform 方法能够对整个 DataFrame 的所有元素进行操作。且 transform 方法只有一个
参数“func”,表示对 DataFrame 操作的函数。 - 同时 transform 方法还能够对 DataFrame 分组后的对象 GroupBy 进行操作,可以实现组
内离差标准化等操作。 - 若在计算离差标准化的时候结果中有 NaN,这是由于根据离差标准化公式,最大值和
最小值相同的情况下分母是 0。而分母为 0 的数在 Python 中表示为 NaN。
示例
#transform 常与匿名函数联用
# print("订单详情表中销量与售价的两倍:\n",data[["counts","amounts"]].transform(lambda x:x*2).head())
# print("(分组后)订单详情表中销量与售价的两倍:\n",data_group_apply.transform(lambda x:x*2).head())
#离差标准化消除大单位和小单位的影响(消除量纲)变异大小的差异影响
# (当前值-最小值)/(最大值-最小值)
# def aa(x):
# # print(type(x),x)#x是一个序列
# fenzi = x - x.min()
# fenmu = x.max() - x.min()
# # print(fenzi/fenmu)
# # print(fenzi)
# return fenzi/fenmu
# print("订单详情表分组后实现组内离差标准化:\n",data_group_apply.transform(aa).head())
# print("订单详情表分组后实现组内离差标准化:\n",data_group_apply.transform(lambda x:(x - x.min())/(x.max() - x.min())).head())
创建透视表与交叉表
使用 povit_table 函数创建透视表
pivot_table 函数常用参数及其说明:
- 利用 pivot_table 函数可以实现透视表,pivot_table()函数的常用参数及其使用格式如下。
pands.pivot_table(data, values=None, index=None, columns=None, aggfunc=‘mean’, fill_value=None, margins=False, dropna=True, margins_name=‘All’)
pivot_table 函数主要的参数调节:
- 在不特殊指定聚合函数 aggfunc 时,会默认使用 numpy.mean 进行聚合运算,numpy.mean
会自动过滤掉非数值类型数据。可以通过指定 aggfunc 参数修改聚合函数。 - 和 groupby 方法分组的时候相同,pivot_table 函数在创建透视表的时候分组键 index 可
以有多个。 - 通过设置 columns 参数可以指定列分组。
- 当全部数据列数很多时,若只想要显示某列,可以通过指定 values 参数来实现。
- 当某些数据不存在时,会自动填充 NaN,因此可以指定 fill_value 参数,表示当存在缺
失值时,以指定数值进行填充。 - 可以更改 margins 参数,查看汇总数据。
使用 crosstab 函数创建交叉表
crosstab 函数:
- 交叉表是一种特殊的透视表,主要用于计算分组频率。利用 pandas 提供的 crosstab 函数
可以制作交叉表,crosstab 函数的常用参数和使用格式如下。 - 由于交叉表是透视表的一种,其参数基本保持一致,不同之处在于 crosstab 函数中的
index,columns,values 填入的都是对应的从 Dataframe 中取出的某一列。
pandas.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, dropna=True, normalize=False)
crosstab 的常用参数及其说明: