第5章数据预处理 【学习目标】 学完本章之后,读者将掌握以下内容。  数据预处理的必要性和主要步骤。  基于Python的重复值、缺失值和噪声的检测与处理。  基于Python的数据列冗余与数据值冲突的判断与处理。  基于Python的属性子集选择和抽样方法。  基于Python的数据合并、抽取和计算的数据变换方法。 5.1数据预处理的必要性 数据分析依赖于数据质量。低质量的数据将导致低质量的分析结果。然而,真实世界中的数据通常会存在大量脏数据,即数据通常不完整(如属性值空缺等)、不一致(如相同数据类型或值等不一致等)、受噪声(如存在偏离期望的孤立点)侵扰。数据预处理是数据分析、知识发现的重要过程。数据预处理是指在主要的处理以前对数据进行的一些处理,将原始数据转换为可以理解或者适合分析的样式,改进数据质量,提高分析的精度,为数据分析做铺垫。 数据质量涉及许多因素,如准确性、完整性和一致性是数据质量的三个基本要素。 导致数据不正确或不准确的原因有多种。收集数据的设备可能出故障; 在数据输入时出现人或计算机的错误; 当用户不希望提交个人信息时,可能故意向强制输入字段输入不正确的值。错误也可能在数据传输中出现。不正确的数据可能是由命名约定或所用的数据代码不一致,或输入字段的格式不一致而导致的。例如表51,同样是 2021年1月5日, 可以有很多种时间格式。重复元组也需要数据清理。 表51不同时间表示格式 5th January,2021 20210105 Jan 5, 2021 01/05/2021 2021.01.05 2021/01/05 导致数据不完整的原因也有多种。有些感兴趣的属性并非总能得到,如学生原生态家庭的精神情况或病史; 输入时认为信息不重要而未收集; 由于理解错误,或者因为设备故障而导致的相关数据没有记录; 由于与其他记录不一致,而造成的数据删除等。此外,历史或修改的数据可能被忽略。缺失的数据,特别是某些属性列上具有缺失值的记录,可能需要将其缺失值推导出来并补齐。 数据不一致性,是指各类数据的矛盾性、不相容性。数据不一致性的原因也有多种。数据冗余,重复存放的数据未能进行一致性地更新。例如,学生入伍造成的学习状态调整,即学生处的学生状态已经改为休学或退学,而教务处未做相应更改,产生矛盾的就读状态。此外,如果软硬件出现故障或者操作错误导致数据丢失或数据损坏,也将引起数据不一致。 除了上述三个基本要素外,数据时效性(Timeliness)、可信性(Believability)、可解释性(Interpretability)也同样影响数据质量。 一般来说,数据预处理的主要步骤包括数据清洗、数据集成、数据规约和数据变换,如图51所示。 图51数据预处理步骤  数据清洗主要填写缺失值,光滑噪声,识别和处理离群点,解决不一致性以“清理数据”。  数据集成是把不同来源、格式、特点性质的数据在逻辑上或物理上有机地集中,从而更容易进行数据分析。  数据归约是指在尽可能保持数据原貌的前提下,最大限度地精简数据量,用替代的、较小的数据表示形式替换原数据,得到信息内容的损失最小化。  数据变换是对数据进行规范化处理,将数据转换成“适当的”形式,以适用分析任务及算法的需要。 这四个大步骤在做数据预处理时未必都要执行,也不是互斥的。例如,冗余数据的删除既是一种数据清洗形式,也是一种数据归约。 5.2数据清洗 在数据分析时,海量的原始数据中存在大量不一致、不完整、有噪声的数据,严重影响数据分析的结果。在脏数据之上即使使用最好的分析方法,也将产生错误结果,并误导业务本身。因此在数据分析过程中,数据清洗尤为重要并占据了很大的工作量。数据清洗又叫数据清理或数据净化,是数据分析的第一步。本节介绍的主要内容包括重复值、缺失值和噪声检测与处理。 5.2.1重复值检测与处理 在实际的数据采集、处理和分析中,经常会遇到重复数据。重复数据的产生可能是由于记录时的错误操作,也可能是真实存在的重复记录。重复数据在进行数据分析的过程中,对输出结果有重要的影响。例如,在逻辑回归分析中,重复数据会影响模型的拟合优度。需要说明的是,重复数据的处理也是选择性的,并不是所有情况下都要做。 以下主要介绍基于pandas库中的函数进行重复值的识别与处理。 1. 重复值检测 pandas库中的duplicated()函数可以实现查找并显示数据表中的重复值。此函数返回一个布尔型的Series,显示是否有重复行,没有重复的行显示为FALSE。其语法格式为: duplicated(subset=None, keep=‘first’) 其参数描述如表52所示。 表52duplicated()函数主要参数描述 参数 描述 subset 列标签或标签序列,可选。仅对某些列进行重复项标识,默认情况下使用所有列 keep 查找重复值的模式。 有三个不同的值,默认值为“first”: keep='first': 除了第一次出现外,其余相同的数据被标记为重复,默认值。 keep='last': 除了最后一次出现外,其余相同的数据被标记为重复。 keep=False: 所有相同的数据都被标记为重复 指定subset参数可控制检测重复行的粒度。当subset不指定时,检测数据表中记录行是否重复,即当两条记录中所有列数据都相等时才判断为重复行; 当subset指定了列标签或列标签序列时,则只在指定列或列的组合上的所有数据重复才被判断为重复行,其余未指定列不检测。 2. 删除重复值 使用drop_duplicates()函数可实现重复值删除。此函数返回一个移除了重复行的数据框对象DataFrame。其语法格式为: drop_duplicates (subset=None, keep='first', inplace=False, ignore_index= False) 其中参数描述如表53所示。 表53drop_duplicates()函数的主要参数描述 参数 描述 subset 列标签,默认为None,去除重复项时要考虑的标签。当subset=None时所有列都相同才认为是重复项 keep 表示是否保留。默认为“first”。 keep='first': 去重时每组重复数据保留第一条数据,其余数据丢弃。 keep='last': 去重时每组重复数据保留最后一条数据,其余数据丢弃。 keep=False: 去重时每组重复数据全部丢弃,不保留 inplace 布尔值,表示直接在原来数据上修改还是保留一个副本,默认为False。 inplace=False: 去重之后不覆盖原表格数据。 inplace =True: 去重之后原表格数据被覆盖 例5.1学生信息数据“学生数据.xlsx”中有四列信息,即姓名,性别,出生日期,学号,其数据显示如图52所示。检查是否存在学生信息重复录入的情况。若存在,则将重复信息删除。 图52学生数据显示 1:from pandas import read_excel 2:df=read_excel(r'C:/case/学生数据.xlsx') 3:print('数据集是否存在重复观测: ', df.duplicated()) 4:newdf=df.drop_duplicates() 5:print(newdf) 【例题解析】 上述代码是对重复值进行识别与删除。 第1行从pandas库中导入read_excel()函数。第2行通过read_excel()函数将学生信息数据读入数据框df。第3行使用duplicated()函数检测数据中是否存在重复值,默认keep=‘First’。因此,数据在第一次出现时(即第0~5行)显示为False,但是在第6行和第7行再次出现时,被标记为了True,即重复行。第4行使用drop_duplicates()函数将出现重复值的行删除并赋值给新的数据框对象newdf。第5行打印并显示newdf中数据。 【运行结果】 第3行的输出结果: 数据集是否存在重复观测: 0False 1False 2False 3False 4False 5False 6True 7True dtype: bool 第5行的输出结果: namegenderbirthnumber 0张三女1993.4.1220161601 1李四男1992.2.15 20161602 2 李明男1994.3.21 20161603 3王梅女1994.5.24 20161604 4张强男1996.3.23 20161605 5周星星男1998.3.24 20161606 5.2.2缺失值检测与处理 除了重复值之外,真实世界中的数据也存在普遍的数据缺失现象。数据具有缺失值,并不意味着数据有错误。数据缺失的原因有很多,例如,由于工作人员的疏忽,造成无意的数据缺失; 或者由于数据采集器故障等原因造成的缺失; 本身数据不存在造成的数据缺失,比如一个未婚者的配偶名字、孩子的收入状况等。明确了缺失值来源,才能对症下药。 缺失值的检测可以使用isnull()判定。isnull()函数无参,返回一个布尔值,若该处值缺失,返回True,否则返回False。 常见的缺失值处理方法有直接删除,数据填补以及不进行任何处理。 1. 删除含有缺失值的行或列 dropna()函数可去除数据中值为空的数据行或列,其语法格式为: dropna(axis=0, how='any', thresh=None, subset=None, inplace=False) 其中主要参数描述如表54所示。 表54dropna()函数主要参数描述 参数 描述 axis axis=0(默认值): 删除包含缺失值(NaN)的行。 axis=1: 删除包含缺失值(NaN)的列 how how=‘any’(默认值): 有缺失值(NaN)即删除。 how=‘all’: 所有的值都缺失(NaN)才删除 thresh 如果非缺失值(NaN)的数量大于thresh则保留 subset 定义要在哪些列中查找缺失值 inplace 是否直接在原DataFrame上修改 例5.2如图53的学生成绩记录数据包含九列,分别为: 学年,学期,考试科目,考试性质,学分,成绩,班号,学号,备注。其中“备注”是为记录学生异常状态的预留列,如“休学”、“退学”等。请检查数据集中是否存在缺失,若列中缺失值大于100,直接将此列删除; 若存在空行也进行相应删除。 图53学生成绩记录.xlsx部分文件显示 1:from openpyxl import load_workbook 2:import pandas as pd 3:records=load_workbook(r'C:/case/学生成绩记录.xlsx') 4:ws = records.active 5:all_value = [] 6:for row in ws.values: 7:all_value.append(row) 8:records_1=pd.DataFrame(all_value) 9:records_2= records_1.dropna(axis=1,thresh=100) 10:print(records_2) 11:records_3=records_1.dropna(how="all") 12:print(records_3) 13:records_3.to_excel(r'C:/case/学生成绩记录(去缺失值).xlsx',header=False, index=False) 【例题解析】 第1~7行使用例4.12中的load_workbook ()函数读取数据。第8行将数据存入DataFrame,并赋值给records_1。第9行利用dropna()函数检测在列的方向上若出现100个以上NaN则将此列删除,并将删除后的数据赋值给records_2。其中,axis=1表示检查列值,thresh=100定义将缺失值超过100则删除。第10行输出records_2。因此,在运行结果中,第10行输出结果删除了“备注”列。 第11行dropna()函数删除行全空的值,即在行的方向上所有值均缺失(NaN)则将此行删除。删除出现空值的行并将其结果赋值给records_3。第12行输出此操作结果。因此,运行结果中在records_2的基础上删除空行剩余7923行。第13行将修改后的数据存入学生成绩记录(去缺失值).xlsx中。 【运行结果】 第10行输出结果(截取一部分显示): 0 1234 5 6 7 0 学年学期考试科目考试性质学分成绩班级学号 1 2015 秋 体育Ⅰ正常考试 185 B1520151803 2 2015 秋 体育Ⅰ正常考试 170 B1520151795 3 2015 秋 体育Ⅰ正常考试 180 B1520151819 4 2015 秋 体育Ⅰ正常考试 170 B1520151809 ... ... ... ... ... ... ... ... 79212023 春毕业实习正常考试 6 100 B1920192365 79222023 春毕业设计正常考试1179.9 B1920192365 7923NoneNoneNoneNoneNoneNoneNoneNone 7924NoneNoneNoneNoneNoneNoneNoneNone 7925NoneNoneNoneNoneNoneNoneNoneNone [7926 rows x 8 columns] 第12行输出结果(截取一部分显示): 0 1 2 3 4 56 7 0 学年学期考试科目考试性质学分成绩 班级学号 1 2015 秋 体育Ⅰ正常考试 185B1520151803 2 2015 秋 体育Ⅰ正常考试 170B1520151795 3 2015 秋 体育Ⅰ正常考试 180B1520151819 4 2015 秋 体育Ⅰ正常考试 170B1520151809 ...... ... ...... ...... ... 79182023 春毕业实习正常考试 689.9B1920192341 79192023 春毕业设计正常考试1179.9B1920192371 79202023 春毕业实习正常考试 679.9B1920192371 79212023 春毕业实习正常考试 6 100B1920192365 79222023 春毕业设计正常考试1179.9B1920192365 [7923 rows x 8 columns] 将包含缺失值的记录直接删除的方法简单,在数据量非常大且缺失值不多的情况下有效。然而,这种通过减少历史数据换取完整信息的方式,可能造成很多隐藏的重要信息丢失; 当缺失数据比例较大,特别是缺失数据非随机分布时,直接删除可能会导致数据分布特征的偏离。特别地,当样本量本身不大且缺失很多时,不建议使用直接删除。 2. 数据填补 常用的缺失值填补方法包括四种。 第一种,人工填写。一般来说,这种方法非常费时。当数据集很大、缺少值很多时,该方法可能行不通。 第二种,使用一个全局常量填充。将空缺的属性值用一个常数替换,尽管该方法简单,但是容易让分析过程误以为形成了一个有趣的概念和模式,因此并不推荐使用。 第三种,使用数据列的中心度量(如均值、中位数或众数)填充。对于非数值数据,使用众数(mode)或中位数填补; 对于数值型数据,使用平均数(mean)或中位数(median)填补。一般地,如果特征分布为正态分布时,使用平均值效果比较好,而当分布由于异常值存在而不是正态分布的情况下,使用中位数效果比较好。 第四种,使用最优可能的值填充。可以用回归、贝叶斯形式化方法等基于推理的工具或决策树归纳确定。 pandas库提供了fillna()函数实现数据的填充。其语法格式为: df.fillna(value=None,method=None,axis=None,inplace=False,limit=None, **kwargs) 其中的常用参数描述如表55所示。 表55fillna()函数参数描述 参数 描述 value 用于填充缺失值,或者指定为每个索引或列使用Serise/DataFrame的值 inplace inplace=True: 直接修改原对象。 inplace=False: 创建一个副本,修改副本,原对象不变(默认) method method=pad/ffill: 用前一个非缺失值去填充该缺失值。 method=backfill/bfill: 用后一个非缺失值填充该缺失值。 Method=None: 指定一个值去替换缺失值(默认为这种方式) limit 限制填充个数 axis 修改填充方向,0代表行,1代表列 例5.3接例5.2中名为“学生成绩记录(去缺失值).xlsx”的数据。提取考试科目为“体育Ⅱ”的数据,将其成绩使用此类课程成绩的平均数填充缺失值。 1:import pandas as pd 2:records= pd.read_excel(r'C:/case/学生成绩记录(去缺失值).xlsx') 3:records_2=records[records['考试科目']=="体育Ⅱ"] 4:records_2['成绩']= records_2['成绩'].astype(float).fillna(records_2['成绩'].mean()) 5:print(records_2) 6:records_2.to_excel(r'C:/case/体育成绩记录(填缺失值).xlsx',index=False) 【例题解析】 第1行引入pandas库。第2行使用read_excel()函数读取学生成绩记录数据,括号内为文件路径。第3行提取考试科目为“体育Ⅱ”的数据,命名为records_2。第4行先将数据成绩一列的数据类型转换为float型,然后将成绩列中空值使用此列的平均数进行填充。第5行打印填充后的数据值。第6行将修改后的数据存入体育成绩记录(填缺失值).xlsx中。 【运行结果】 学年 学期 考试科目考试性质 学分成绩 班级学号 467 2016春体育Ⅱ正常考试1.096.0B1520151819 469 2016春体育Ⅱ正常考试1.091.0B1520151825 470 2016春体育Ⅱ正常考试1.093.0B1520151823 473 2016春体育Ⅱ正常考试1.080.0B1520151837 476 2016春体育Ⅱ正常考试1.090.0B1520151845 ... ...... ...... ...... ... 63562020春体育Ⅱ正常考试1.069.0B1920192371 63662020春体育Ⅱ正常考试1.071.0B1920192391 63762020春体育Ⅱ正常考试1.067.0B1920192389 63912020春体育Ⅱ正常考试1.083.0B1920192341 64262020春体育Ⅱ正常考试1.073.0B1920192365 [111 rows x 8 columns] 3. 不处理 空值填补是用估计值填补未知值,不一定完全符合客观事实。在对不完备信息进行补齐处理的同时,或多或少地将改变原始信息。对空值不正确的填充也可能引入新的噪声,为分析带来错误的结果。因此,在某些情况下,希望在保持原始信息不发生变化的前提下对数据进行处理。 5.2.3噪声检测与处理 噪声(Noise)是数据集中的干扰数据(对场景描述不准确的数据),即被测量变量的随机误差或方差。噪声数据中存在着错误或异常,这将对数据分析造成干扰。一般而言,观测值是数据真实值与噪声的叠加,因此噪声在数据集中很常见。噪声在数据分析(包括离群点分析)中不是令人感兴趣的,需要在数据预处理中剔除,减少对后续模型预估的影响。 常用的数据平滑去噪的技术有分箱(Binning)、回归(Regression)和离群点分析(Outlier analysis)。 1. 分箱 分箱方法通过考察数据的近邻(即周围的值)来光滑有序数据值。这些有序的值被分布到一些桶或箱中。由于分箱方法考察近邻的值,因此是数据的局部光滑。常用方法有3种,即按箱均值平滑、按中值平滑和按边界值平滑,如图54表示。 按箱均值平滑是用箱中的均值替换箱中每一个值。在图54的例子中,有9个成绩值,首先将成绩按大小排序,然后被划分到大小为3的等频箱中(即每个箱包含3个)。箱中的值被此箱中的均值替代。类似地,按中值平滑即箱中的每一个值都被替换为该箱的中位数。对于按边界值平滑,给定箱中的最大值和最小值同样被视为箱边界,而箱中的每一个值都被替换为最近的边界值。例如,在图54中的箱边界光滑,以箱1为例,边界值为60、77,而61距离60更近,则使用60代替。一般而言,宽度越大,光滑效果越明显。分箱也可以作为一种离散化技术使用。 图54数据光滑的分箱方法 例5.4接例5.2中的“体育成绩记录(填缺失值).xlsx”的数据。将成绩分为10个箱,并对成绩进行箱均值光滑和箱边界光滑处理。 1:import pandas as pd 2:import numpy as np 3:def binning(filename,box_num): 4:my_list1=[] 5:noise_data = pd.read_excel(filename) 6:my_list1=sorted(noise_data['成绩']) 7:box_list=[] 8: len_box=int(np.ceil(len(my_list1)/float(box_num))) 9: for i in range(0,10): 10: each_box=my_list1[i*len_box:(i+1)*len_box] 11: box_list.append(each_box) 12: return box_list 13:def box_mean_smooth(box_list): 14: for i in range(0,len(box_list)): 15: box_avg=int(np.average(box_list[i])) 16: for j in range(0,len(box_list[i])): 17: box_list[i][j]=box_avg 18:return box_list 19:def box_boundary_smooth(box_list): 20: for i in range(0,len(box_list)): 21: left_bdy=box_list[i][0] 22: right_bdy=box_list[i][-1] 23: for j in range(0,len(box_list[i])): 24: if abs(box_list[i][j]-left_bdy) threshold2): 13:outlier.append(df.成绩[i]) 14:else: 15:continue 16:print(outlier) 【例题解析】 第1~4行引入pandas库、numpy库和read_excel函数。 第5~9行计算获得(μ-3σ,μ+3σ)的区间。其中,第5行读入数据。第6行求出成绩的平均值。第7行求出成绩数据值的标准差,第8行和第9行分别计算μ-3σ和μ+3σ。 第10~16行是将异常值保存在outlier中,其中,第10行定义列表意在将异常值放入。第11~15行检测成绩列中数值在(μ-3σ,μ+3σ)之外的成绩值,放入outlier。第16行将异常值打印。 从结果发现,异常值包括两种。一种是如1000这样的超高值,成绩最高为100,显然1000超过了成绩的范围,另一种是偏低的数值,即29以下的成绩,此类学生偏离平均水平较远,建议采取取消补考资格直接重修。 【运行结果】 [22.0,1108.0,19.0,13.0,20.0,0.0,0.0,0.0,11.0,12.0,11.0,0.0,0.0,6.0,…,9.0,12.0,21.0,22.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] 5.3数据集成 在很多应用场合,分析需要合并来自多个不同来源的数据。由于不同的数据源定义表名和列名时命名规则不同,存入的数据格式、取值方式、单位都会有不同。因此,小心集成有助于减少结果数据集的冗余和不一致,提高分析准确性。数据集成的本质是整合数据源,因此多个数据源中字段的语义差异、结构差异、字段间的关联关系,以及数据的冗余重复,都是数据集成面临的问题。 5.3.1实体识别问题 数据集成将多个数据源中的数据合并,存放在一个一致的数据存储中。这些数据源可能包括多个数据库或一般文件。在数据集成时,有许多问题需要考虑,如模式集成和对象匹配。来自多个信息源的现实世界的等价实体如何才能“匹配”?这涉及实体识别问题。例如,数据分析者要确定一个数据库中的student_id与另一数据库中的stud_number是同一个属性、指同一个实体。 集成时需要注意每个数据列的元数据包括名字、含义、数据类型和属性的允许取值范围,以及处理空白、零或空值规则等。在整合数据源的过程中,可能出现:  同列名但不同语义的情况,如两个数据源中都有一个列名字叫“成绩”,但其实一个数据源中记录的是未加平时成绩的考试成绩,另一个数据源中是加平时成绩、课堂表现等后的综合成绩。  不同列名但同语义的情况,如两个数据源都有数据列记录加平均成绩后的成绩,但是一个数据源中列名为score,另一个数据源中列名为grade。  同列名同语义但不同字段结构的情况,同样存储学生成绩字段,一个数据源存为int,另一个数据源中存为char。  字段取值范围不同,如学生成绩字段,一个数据源中是百分制,另一个数据源中是十分制等。 为了解决上述问题,需要在数据集成前,进行业务调研,确认每个字段的实际意义,不被误导。另外,在集成期间,当一个数据集的数据列与另一个数据集的数据列匹配时,必须特别注意源系统中的函数依赖和参照约束与目标系统中的匹配。 5.3.2数据列冗余问题 冗余是数据集成的另一个重要问题。一个数据列(例如,年收入)如果能由另一个或另一组数据列“导出”,则这个数据列可能是冗余的。有些冗余可以被相关分析检测到。 给定两个数据列,根据可用数据度量一个列能在多大程度上蕴含另一个。对于类别数据,可以使用χ2(卡方)检验。对于数值型数据列,使用相关系数(Correlation Goefficient)和协方差(Govariance)。 1. 类别数据的χ2(卡方)检验 对于类别数据,两个数据列A和B之间的相关联系可以通过卡方检验发现。假设A有c个不同值a1,a2,…,ac,B有r个不同值b1,b2,…,br。用A和B描述的数据可以用一个相依表显示,其中A的c个值构成列,B的r个值构成行。另(Ai, Bj)表示列A取值ai、列B取值bj的联合事件,即(A=ai, B=bj)。每个可能的(Ai, Bj)联合事件都在表中有自己的单元。χ2值(又称Pearson χ2统计量),可以用公式(51)计算: χ2=∑ci=1∑rj=1(oij-eij)2eij (51) 其中,oij是联合事件(Ai, Bj)的观测频度(即实际计数),而eij是(Ai,Bj)的期望频度,可以用公式(52)计算: eij=count(A=ai)×count(B=bj)n (52) 其中,n是数据集大小,count(A=ai)是A上具有值ai的个数,而count(B=bj)是B上具有值bj的个数。式(51)中的和在所有r×c个单元上计算。注意,对χ2值贡献最大的单元是其实际计数与期望计数很不相同的单元。 χ2统计检验假设A和B是独立的。检验基于显著水平,具有自由度(r-1)×(c-1)。如果可以拒绝该假设,则说明A和B是统计相关的。 Python的Scipy库中包含众多进行科学计算、统计分析的函数。Scipy是世界上著名的Python开源科学计算库,建立在NumPy之上。可通过Scipy库中的chi2_contingency()函数进行卡方检验,其语法格式为: chi2_contingency(observed, correction=True, lambda_=None) 其中常用参数描述如表56所示,返回值如表57所示。 表56chi2_contingency()函数常用参数描述 参数 描述 observed 列联表,表包含每个类别中观察到的频率(即发生次数)。在二维情况下,表通常被描述为“R×C表” correction 如果为True,并且自由度为1,则应用Yates校正以保持连续性。校正的效果是将每个观察值向相应的期望值调整0.5 lambda_ float或str,可选。默认情况下,此测试中计算的统计量是Pearson的卡方统计量 表57chi2_contingency()函数返回值描述 参数 描述 chi2 float,卡方值 p float,p值 dof int,自由度 expected ndarray,预期频率,基于表的边际总和 例5.6假设存在如表58所示男女所读书目的类型统计。检验性别与阅读类别是否有关; 设H0: 性别与阅读类别无关,H1: 性别与阅读类别有关。 表58男女阅读种类数据 男 女男 女 小说 250 200 非小说 50 1000 1:from scipy.stats import chi2_contingency 2:import numpy as np 3:kf_data = np.array([[250,200],[50,1000]]) 4:kf = chi2_contingency(kf_data) 5:print('chisq-statistic=%.4f, p-value=%.4f, df=%i expected_frep=%s'%kf) 【例题解析】 第1行引入scipy.stats库的chi2_contingency()函数。第2行引入NumPy库。 第3行以数组的形式写入数据,其中,表58中的数据以列(或行,无影响)为单位存入两个列表中。第4行使用chi2_contingency()函数对数组数据进行卡方检验。第5行输出卡方值,p值,自由度以及上述数组顺序的期望值。 因为其pvalue近似于0,因此拒绝两者独立的假设,即性别与阅读类别显著相关。 【运行结果】 chisqstatistic=504.7669, pvalue=0.0000, df=1 expected_frep=[[ 90. 360.] [210. 840.]] 2. 数值数据的相关系数 对于数值数据,可以通过计算数据列A和B的相关系数(又称Pearson积矩系数,Pearson’s Product Moment Coefficient),估计这两个数据列的相关度rA,B, rA,B=∑ni=1(ai-)(bi-)nσAσB=∑ni=1(aibi)-nnσAσB (53) 其中,n是数据集大小,ai和bi分别是第i行数据在列A和列B上的值,和分别是A和B的均值,σA和σB分别是A和B的标准差,∑ni=1(aibi)是AB叉积和(即列A每一个值乘以列B对应位置的值)。严格地说,Pearson的相关性要求每个数据集正态分布。与其他相关系数一样,此系数在 -1 和 +1 之间变化(-1≤rA,B≤+1),0表示没有相关性。 如果rA,B大于0,则A和B正相关,这意味着A值随着B值的增加而增加。该值越大,相关性越强(即每个属性蕴含另一个可能性越大)。因此,一个较高的rA,B值表明A(或B)可以作为冗余列。如果该结果值等于0,则A和B是独立的,并且它们之间不存在相关性。如果该结果值小于0,则A和B是负相关的,一个值随着另一个减少而增加。这意味着每一个属性列都阻止另一个出现。散点图也可以用来观察属性之间的相关性。 Stats模块是Scipy的统计模块,其中包含很多用于统计检验的函数。使用Stats模块的pearsonr()函数可计算皮尔逊相关系数和测试非相关性的p值,其语法格式为: scipy.stats.pearsonr(x,y) 其中,x、y为输入变量的数组,返回皮尔逊相关系数和测试非相关性的p值。 例5.7图55中记录了两个公司(ALLElect和Hightech)不同时刻的每支股票信息的单价信息,判断两支股票的相关性。 图55股票.xlsx部分数据显示 1:import pandas as pd 2:import scipy.stats as stats 3:df=pd.read_excel(r'C:/case/股票.xlsx') 4:print(stats.pearsonr(df["ALLElect"],df["Hightech"])) 【例题解析】 第1~2行引入pandas库与Scipy库的Stats模块。第3行读取数据。第4行打印数据中两列的相关系数以及p值。 因为结果中ALLElect 与Hightech两列的相关系数为0.867,p=0.057。因此,在90%的置信水平上拒绝原假设,即两个公司股票呈现显著性相关。 【运行结果】 (0.8674427949190671, 0.05676876648986295) 3. 数值数据的协方差 在概率论和统计学中,协方差和方差是两个类似的度量,评估两个数据列如何一起变化。考虑两个数值列A、B和n次观测的集合{(a1,b1),…,(an,bn)}。A和B的均值又分别称为A和B的期望,即 E(A)==∑ni=1ain 且 E(B)==∑ni=1biin A和B的协方差(covariance)定义为 con(A,B)=E((A-)(B-))=∑ni=1(ai-)(bi-)n(54) 如果把公式(53)和公式(54)相比较,则可以看到 rA,B=cov(A,B)σAσB(55) 其中,σA和σB分别是A和B的标准差。可以证明 cov(A,B)=E(A·B)-(56) 对于两个趋向于一起改变的属性列A和B,如果A大于,则B很可能大于。因此,A和B的协方差为正。另外,如果当一个属性小于它的期望值时,另一个属性趋向于大于它的期望值,则A和B的协方差为负。 如果A和B是独立的(即它们不具有相关性),则E(A·B)=E(A)·E(B)。因此,协方差cov(A,B)=E(A·B)-=E(A)·E(B)-=0。然而,其逆不成立。某些随机变量对(属性对)可能具有协方差0,但不是独立的。仅在某种附加的假设下(如数据遵守多元正态分布),协方差0蕴含独立性。 Python中使用DataFrame.cov()函数计算协方差,其语法格式为 DataFrame.cov([min_periods]) 该函数计算列的成对协方差,不包括NA/null值,其中,参数min_periods表示样本最少的数据量,返回值为表示协方差的DataFrame对象。 5.3.3数据值冲突问题 数据集成还涉及数据值冲突的检测与处理。例如,对于现实世界的同一对象,来自不同数据源的值可能不同。这可能是因为表示、尺度或编码不同。例如,同样是学生缴纳的学费列,数据类型均为数值型,但是一个数据源中使用逗号分隔,另一个数据源中用科学记数法。重量属性可能在一个系统中以公制单位存放,而在另一个系统中以英制单位存放。不同学校交换信息时,每个学校可能都有自己的课程计划和评分方案。一所大学可能采取学季制,开设3门数据库系统课程,用A+~F评分; 而另一所大学可能采取学期制,开设两门数据库课程,用1~10评分。很难在这两所大学之间指定准确的课程成绩变化规则,这使得信息交换非常困难。 列也可能在不同的抽象层,其中列在一个系统中记录的抽象层可能比另一个系统中“相同的”属性低。例如,“籍贯”在一个数据库中可能填写的是城市,而另一个数据库中相同名字的列可能表示的是县或者省份等。 对待这种问题,需要对实际业务知识有一定的理解。同时,对数据进行调研,尽量明确造成冲突的原因,如果数据的冲突实在无法避免,就要考虑冲突数据是否都要保留,是否要进行取舍,如何取舍等问题。 5.4数据规约 5.4.1策略概述 用于数据分析的数据集可能非常大。但是,在海量数据集上进行复杂的数据分析可能需要很长的时间。数据规约产生更小但保持原数据完整性的数据集。在规约后的数据集上进行分析和挖掘将更有效率。 数据规约策略包括维规约、数量规约和数据压缩。 维规约(Dimensionality Reduction)减少所考虑的随机变量或属性的个数。维规约方法包括小波变换和主成分分析法,它们把原数据变换或投影到较小的空间。属性子集选择是一种维规约方法,其中不相关、弱相关或冗余的属性或维被检测和删除。 数量规约(Numerosity Reduction)用替代的、较小的数据表示形式替换原数据。这些技术可以是参数的或非参数的。对于参数方法而言,使用模型估计数据,使得一般只需要存放模型参数,而不是实际数据(离群点可能也要存放)。回归和对数线性模型就是例子。存放数据规约表示的非参数方法包括直方图、聚类、抽样和数据立方体聚集。 数据压缩(Data Compression)使用变换,以便得到原数据的规约或“压缩”表示。如果原数据能够从压缩后的数据重构,而不损失信息,则该数据规约称为无损的。如果只能近似重构原数据,则该数据规约称为有损的。对于串压缩,有一些无损压缩算法。然而,它们一般只允许有限的数据操作。维规约和数量规约也可以视为某种形式的数据压缩。 5.4.2属性子集选择 属性子集选择属于维规约方法中的一种。用于分析的数据集可能包含数以百计的属性,其中大部分属性可能与分析任务不相关,或者是冗余的。例如,如果分析任务是“学生选择‘Python数据分析’这门课程的影响因素分析”,与“专业”和“选修课”不同,诸如学生的电话号码等属性多半是不相关的。尽管领域专家可以挑选出有用的属性,但这可能是一项困难而费时的任务,特别是当数据的行为不是十分清楚的时候更是如此。遗漏相关属性或留下不相关属性都可能是有害的,会导致所用的分析和挖掘方法无所适从。这可能导致发现质量很差的模式。此外,不相关或冗余的属性增加了数据量,可能会减慢分析进程。 属性子集选择通过删除不相关或冗余的属性(或维)减少数据量。属性子集选择的目标是找出属性最小属性集,使得数据内的概率分布尽可能地接近使用所有属性得到的原分布。在缩小的属性集上分析和挖掘还有其他的优点: 它减少了出现在发现模式上的属性数目,使得模式更容易理解。 如何找出原属性的一个“好的”子集?对于n个属性,有2n个可能的子集。穷举搜索找出属性的最佳子集可能是不现实的,特别是当n和数据集的数目增加时。因此,属性子集选择通常使用压缩搜索空间的启发式算法。通常,这些方法是典型的贪心算法,在搜索属性空间时,总是做看上去最佳的选择。他们的策略是做局部最优选择,期望由此导致全局最优解。在实践中,这种贪心方法是有效的,并可以逼近最优解。 “最好的”(和“最差的”)属性通常使用统计显著性检验来确定。这种检验假定属性是相互独立的。也可以使用一些其他属性评估度量,如建立分类决策树使用的信息增益度量。属性子集选择的基本启发式方法包括以下技术,如图56所示。 图56属性子集选择的贪心(启发式)方法 (1) 逐步向前选择: 该过程由空属性集作为归约集开始,确定原属性集中最好的属性,并将它添加到归约集中。在其后的每一次迭代,将剩下的原属性集中的最好属性添加到该集合中。 (2) 逐步向后删除: 该过程由整个属性集开始。在每一步中,删除尚在属性集中最差的属性。 (3) 逐步向前选择和逐步向后删除的组合: 可以将逐步向前选择和逐步向后删除方法结合在一起,每一步选择一个最好的属性,并在剩余属性中删除一个最差的属性。 (4) 决策树归纳: 决策树算法(例如ID3、C4.5和CART)最初是用于分类的。决策树归纳构造一个类似于流程图的结构,其中每个内部(非树叶)结点表示一个属性上的测试,每个分枝对应于测试的一个结果; 每个外部(树叶)结点表示一个类预测。在每个结点上,算法选择“最好”的属性,将数据划分成类。 当决策树归纳用于属性子集选择时,由给定的数据构造决策树。不出现在树中的所有属性假定是不相关的。出现在树中的属性形成归约后的属性子集。 这些方法的结束条件可以不同。该过程可以使用一个度量阈值来决定何时停止属性选择过程。 5.4.3抽样 抽样可以作为一种数据归约技术使用,因为它允许用比数据小得多的随机样本(子集)表示大型数据集。假定大型数据集D包含N个元组。可以用于数据归约的、最常用的对D的抽样方法包括: s个样本的无放回简单随机抽样: 从D中抽取s个样本,而且每次抽取一个样本,不放回数据集D中。 s个样本的有放回简单随机抽样: 该方法类似于无放回简单随机抽样,不同之处在于当一个样本从D中抽取后,记录它,然后放回原处。也就是说,一个样本被抽取后,它又被放回D,以便它可以被再次抽取。 簇抽样: 如果D中的样本被分组,放入M个互不相交的“簇”,则可以得到s个簇的简单随机抽样(SRS),其中s