-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 79.6 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 79.6 KB
1
{"pages":[],"posts":[{"title":"动手学数据分析(1)——数据加载与探索性分析(EDA)","text":"本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。 Datawhale-动手学数据分析 数据来源Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率 Ch1 数据加载与探索性分析数据分析包含数据加载、探索性数据分析、数据清洗、特征处理、数据建模、模型评估等多个步骤。在进行数据分析之前,需要载入我们获取的数据集,并通过探索性分析初步了解数据的结构、组成和特征。 Ch1-1 数据载入与初步观察数据载入载入函数对于常见的数据文件类型(.csv/.xlsx/.tsv/…),通常使用第三方库pandas载入数据。 pandas中,常见的载入数据文件函数有以下几种: read_table():可以读取常见的分隔符定界文件,sep=''参数用于选择分隔符,若该参数为空,则不予分隔(所有数据集中在一列中),支持正则表达式。 read_csv():读取逗号分隔符文件(.csv),同样有sep=''参数,默认为逗号(,) read_excel():读取Excel文件(.xlsx),相比前两种函数,对于含有多个工作簿的excel文件,一般需要输入参数sheetname='',不输入则默认为第一张工作簿 以上函数默认返回DataFrame对象,对于excel,当读入多个工作簿(如:[1,2])时,返回一个dict对象,每个元素的值都是一个DataFrame对象。 除此之外,pandas还支持读取很多其他的文件格式(如:JSON、pickle、SQL以及常见统计软件STATA/SPSS的输出格式),具体参考pandas官方文档-IO 相对路径与绝对路径查看当前所在路径的命令:os.getcwd() 使用前要导入python自带的输入输出库os 相对路径表述: 符号 含义 / 根目录 ./ 当前目录 ../ 上一级目录 ../../ 上两级目录,多级目录以此类推 逐块读取当数据文件过大,包含数据量过多时,为了防止一次性读入所有数据服务器内存占用过大,难以处理,pandas的载入函数提供了chunksize参数以实现数据的逐块读取,该参数使得函数通过分多次将文件数据读入内存,降低了内存占用。 123456# 逐块读取chunks = pd.read_csv('../../Titanic-kaggle/data/train.csv', chunksize = 1000)print(type(chunks))for piece in chunks: print(type(piece)) print(len(piece)) 当使用chunksize参数时,载入函数将返回TextFileReader对象,该对象可以使用for语句遍历,其中每个元素都是一个包含指定行数的DataFrame对象,此时就可以在循环中实现对各块数据的批处理。 设定列名和索引 可以在读取数据文件时利用参数names=[]、header=0、index='index_col_name'设定。 也可以在后期通过方法df.set_index()、df.rename(colomns={})或借助属性df.columns、df.index暴力修改 Tips: 当使用read_csv()的names参数修改列名时,其实质是在原表基础上加上给定的列名,此时header会取Null;若不使用names参数,header默认取0。因此,设定列名时,应当使用header=0来表明原数据有列名,且位于第一行,这样才能实现列名的替换。 df.set_index()中包含drop参数,该参数应设定为True,表示删除现有索引列,否则当前索引行将变为普通列加入现有DataFrame 123456789# 方法1:读取时设定names = ['乘客ID','是否幸存','乘客等级(1/2/3等舱位)','乘客姓名','性别','年龄','堂兄弟/妹个数','父母与小孩个数','船票信息','票价','客舱','登船港口']# header属性用于设置列名取自哪一行,指定names时应当设为0,否则会多出原标题行df = pd.read_csv('../../Titanic-kaggle/data/train.csv',names= names,index_col='乘客ID',header=0)# 方法2:更改表头和索引column_names = {'PassengerId':'乘客ID','Survived':'是否幸存','Pclass':'乘客等级(1/2/3等舱位)','Name':'乘客姓名','Sex':'性别','Age':'年龄','SibSp':'堂兄弟/妹个数','Parch':'父母与小孩个数','Ticket':'船票信息','Fare':'票价','Cabin':'客舱','Embarked':'登船港口'}df.rename(columns = column_names, inplace = True)df.set_index('乘客ID', drop = True, inplace = True) 初步观察概览数据基本信息查看数据的基本信息主要用到以下一些方法和属性: 方法/属性 用途 df.shape 以元组形式返回dataframe对象的行列数 df.size 以整数形式返回dataframe对象的元素数(不包含索引/表头) df.colomns 输出所有的列名 df.head(n)/df.tail(n) 查看dataframe前n行/后n行 df.info() 输出关于数据的基本描述,包含行数、各列的列名、数据类型、非空值数以及占用内存。其中verbose参数可以用于选择长/短两种描述 df.describe() 输出各列的描述性统计信息,可以迅速查看数据的统计特征(Series也有该方法) df.value_counts() 返回一个包含不同列各值数的Series(Series也有该方法) 判断缺失值用到两个方法: df.isnull():返回一个判断是否为空的DataFrame,若为空则为True,反之为False df.isnotnull():返回一个判断是否为非空的DataFrame,若为非空则为True,反之为False 数据输出与读取的几个函数类似,语法基本一致,区别在于数据输出使用的是对象的方法,而非函数: df.to_csv() df.to_excel() Notes: 查阅API文档,发现没有to_table()方法,很神奇的是pandas自带了to_latex()和to_markdown()方法 一般会设置编码方式参数encoding='utf-8',当元素含有中文时,若出现乱码,可以尝试使用utf_8_sig或gbk格式 Ch1-2 pandas基础数据类型pandas最基本的两种数据类型:DataFrame和Series。此外还需要了解numpy中的基本数据类型——ndarray。 ndarray:numpy中最基础的数据类型,多维数组对象,本质上就是一个n维矩阵。只能存放同类型数据。 Series:主要由两部分组成,index 和 values。index可以是任意类型,不一定非是数字类型,values是存放的内容,是一个1维的ndarray DataFrame: 可以看作由多列Series组成,也可以看作由多行Series组成。或者可以看作columns, index, values这三部分组成。 columns:列名,默认也是数字升序,可以是任意类型 index:行名,默认数字升序,可以是任意类型 values:存放的内容,是一个2维的ndarray 简单来说,Series实质是一维数组,DataFrame则是多个Series组成的二维数组,当然二者相比ndarray要多出一些如索引、列名等的属性,可以看作ndarray的包装。 官方文档里写到二者的关系: DataFrame can be thought of as a dict-like container for Series objects.(DataFrame可以看作一个类似dict的用于盛放Series的容器) 构造方法Series List + index_list Dict ndarray + index_list 使用 List 进行创建,自动添加0开始的行标签(索引) Series 创建时若不注明 name 参数,相当于此列没有列名 1234567891011121314## Series的创建# 01 使用listsdata = [2000,500,1000,4000]example_1 = pd.Series(sdata)# 02 使用dictsdata = {'thu':1,'zju':3,'hust':8,'whu':9,'sysu':10}example_1 = pd.Series(sdata)example_1# 03 使用np.ndarraysdata = np.random.rand(10)*20example_1 = pd.Series(sdata,index=['a','b','c','d','e','f','g','h','i','j'],name='random') 无论是字典,列表还是元组,都可以构建Series。只不过,dict自带index,而list,tuple要专门定义index(也就是每一行的行名)。系统默认的index为0,1,2,3… DataFrame DataFrame的创建方法有很多种,这里只列了其中几种,具体可以参考创建DataFrame的7种方法 DataFrame(Dict):字典内可以为列表/字典/Series DataFrame.from_dict(Dict) DataFrame(np.ndarray) 可以添加index和columns参数,不附带index参数则索引默认为自然数序列 1234567891011121314151617## DataFrame的创建# 01 使用dict,其中key为list(亦可字典套字典)ddata = {'country':['US','Brazil','India','Europe','South Africa'],'confirmed':[5481795,3407354,2702742,930276,592744],'death':[171799,109888,51797,15836,12264]}example_2 = pd.DataFrame(ddata)print(example_2)print('--------------')# 02 from_dict()静态方法example_2 = pd.DataFrame.from_dict(ddata)print(example_2)print('--------------')# 03 二维数组ddata = np.array([4,1,4,5,5,2,3,5,6,3,3,4,6,1,9]).reshape(5,3)example_2 = pd.DataFrame(ddata, index=list('abcde'), columns=['four', 'one', 'three'])print(example_2) 有些时候我们只需要选择dict中部分的键当做DataFrame的列,那么我们可以使用columns参数,例如我们只选择country和death列: 1pd.DataFrame(data = ddata,columns=['country','death']) 数据操作列选取 作为属性选取:df.Cabin 使用[]运算符,其实现方式是实现类的__getitem__魔术方法:df['Cabin'] 很多时候会截取所有数据的一部分进行后续操作和分析,这个时候很可能需要用到reset_index(drop=True)方法来重新生成数字索引 行/列删除 df.drop():axis参数默认为0,即删除行,要改成列应改为1。或者忽略axis直接使用columns参数 del df[‘’]:del操作符,只能用于列 很多操作方法都有inplace参数,inplace为True表示直接在原对象上进行改动,默认为False,返回一个新对象 数据筛选Pandas有自带的访问器操作,loc 与 iloc iloc基于位置(数字索引)选择,通过其在 DataFrame 中的数字排位进行访问。 loc 则通过自定义标签进行提取,该方法聚焦于数据的标签(索引)而不是位置(数字索引)。 12345678910111213## iloc# 取出第一行的内容df.iloc[0]# 取出第一列的内容df.iloc[:, 0]# 也可以采用内嵌列表的方式df.iloc[[0, 1, 2], 0]## loc# 选中第一行的Sex列对应的单元格df.loc[0, 'Sex']# 选中Pclass, Name, Sex这三列的数据df.loc[:, ['Pclass', 'Name', 'Sex']] 可以使用负数来进行选择: 12# 选择倒数五行的内容df.iloc[-5:] Notes: 二者的使用方法都是使用中括号[]而不是小括号() loc 与 iloc 均采用了先选行后选列的语法,这与传统的 python 语法相反二者的区别 iloc 的区间满足前闭后开,而 loc则满足前闭后闭。因而当我们遇到 String 类型索引,需要按照索引进行选取内容时,我们往往是希望取出区间内所有的元素,此时更好的方法是使用 loc 特定条件筛选loc 访问器操作和[]运算符可以根据输入的逻辑值Series来筛选显示的行,将自动从中选取逻辑值为True的行。 一些常见的逻辑相关符号和方法: 用于连接多个条件的符号:使用 & 表示逻辑和,使用 | 表示逻辑或 df.isin([]):用于判断是否包含在XXX内,相当于SQL语言中的 WHERE … IN… df.isnull():用于判断是否为空值 df.notnull():用于判断是否非空 1234# []运算符df[(df['Age']>10) & (df['Age']<50)]# loc访问器操作df.loc[df['Cabin'].isin(['C123','C85'])] Ch1-3 探索性数据分析数据排序可以按值排序,也可以按索引排序。 String类型自动按字母顺序排序。 sort_values():按值排序,by=[<列1>,<列2>]参数用于选择排序依据列,可以按多列进行综合排序 sort_index():按索引排序,axis用于选择按行索引/列索引排序,默认为0(列索引) 二者都有控制升降序的参数ascending=True,True表示按升序,False表示按降序 一些探索性发现通过使用describe() 、info()、corr()、value_counts()等函数对数据进行探索性分析,有以下一些发现: 船舱信息中存在大量缺失值,年龄信息也有一部分缺失,需要对这些缺失值做一定的处理。 性别数据需处理为0/1变量。 得到的一些信息: 大多数乘客的家庭成员都很少。 乘客姓名第一个单词相同者拥有相同的船票信息、票价、登船港口、客舱、家庭成员人数,这些人应该属于同一个家族。大家族成员的存活率普遍偏低,因此,可以将家庭人数指标纳入后续的模型中。 乘客整体的平均年龄在29.7岁。相比整体数据,幸存人群大约只占所有人的1/3。观察其中几个方差较小的指标,其中,幸存人数的乘客等级整体偏高。表明乘客等级确实与幸存率有着一定的关系。 票价整体偏低,按照乘客舱位等级和年龄降序排序,发现前20中只有一人存活,这可能暗含着舱位较低的死亡风险更高的信息,猜测可能是舱位低安全措施越不足,安全风险更高的原因。在相关系数的分析中,票价与乘客等级负相关性较强,符合常识,可以考虑将二者结合为一个新的综合指标,进一步分析该指标和是否存活的关系。 尽管船上的男性多于女性,女性的存活率却明显高于男性,女性存活率约为74.2%,相比之下男性只有18.9%的存活率。因此,性别可能也可以作为预测模型的考虑因素之一。","link":"/2020/08/20/Data-Analysis-ch1-note/"},{"title":"动手学数据分析(2)——数据清洗及特征处理","text":"本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。 Datawhale-动手学数据分析 数据来源Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率 Ch2-1 数据清洗及特征处理缺失值缺失值表现在数据集中,有以下几种形式 NaN(Not A Number):普通数据的NA值 NaT(Not A Time):时间戳数据的NA值 None:Python中空值,没有数值 误输入无意义值 对于使用IO方法读入的数据中的空值,pandas默认会将其转化为NaN,NaN属于float的子类,与None不同,None属于Object类型。同时,pandas和numpy提供的处理NaN的方法更多,因此,一般不能使用None来判断空值。 None和NaN的详细比较可以参考: [1] Python 中 NaN 和 None 的详细比较 [2] 数据分析之Pandas缺失数据处理 缺失值检测查看存在空值的列: isnull():可以检测所有NA类数据(包含Null和NaN) info():输出所有列的空值信息 1234567## 查看存在空值的列,并统计空值数# 01 info()方法train.info()# 02 isnull()方法train.isnull().sum() 查看存在空值的行会用到以下两个处理逻辑值的方法: any():判断某一行/列是否含有True all():判断某一行/列是否都为True 踩的坑:NaN != NaN 因此,对于NaN的检测,不能使用 == np.nan,只能使用isnan(),NaT同理 12345# 显示某列为空值的行train[train['Age'].isnull()] # 正确train[np.isnan(train['Age'])] # 正确train[train['Age']== None] # 错误train[train['Age'] == np.nan] # 错误 缺失值处理处理缺失值的方法可以分为两大类: 删除:以dropna为代表,一般适用于数据较多的情况,删除数据不会使得数据过少。 填补:以fillna为代表,当数据量不够大时,一般使用这种方法,填充的方法有很多,如:均值填充、众数填充、使用模型预测缺失值填充等。由于填补的数据并不是真实的数据,可能会使数据失真,一般只能用于客观数据。 主要使用的函数/方法: DataFrame.dropna():删除含有缺失值的行/列 DataFrame.fillna():填充行/列的缺失值 DataFrame.interpolate():对缺失数据进行插值 前两种方法都包含以下参数: subset:用于选择判断依据行/列 how:’any’/‘all’,存在or任意 axis:维度 后两种方法都包含method参数,用于选择填补/插值的具体方法,可选方法参考fillna_methods与interpolate_methods 123# fillna()的几种使用方式train['Age'] = train['Age'].fillna(int(train['Age'].mean())) # 填充均值,因为Cabin列是字符串,不能使用这种方法,只以Age列为例train.fillna(method='ffill') 对于该任务中缺失值的处理:由于Cabin列缺失的数据很多,使用填补方式很容易失真,删除则会让数据集很小。因此,考虑后续建模不使用该特征(因此不去处理该列的缺失),而是使用该特征构建新的可用于预测的特征。对于Age列缺失值,由于不存在顺序关系,且年龄不属于定距变量,选择使用众数进行填补。 1train['Age'] = train['Age'].fillna(train['Age'].mode()) 数据去重重复值的处理主要用到两个方法:duplicated()与drop_duplicates() 一般需要处理的只有所有特征完全相同的样本,具体情况需要观察后得知。 123456# duplicated函数train.duplicated() # 判断整行重复train.duplicated(['Name','Sex','Age'])# drop_duplicates()函数,也可以选择哪几列重复为依据,以及去重保留哪一行(keep=)train.drop_duplicates(keep='first',inplace =True) 特征处理数据分箱数据预处理技术,用于减少次要观察误差的影响,是一种将多个连续值分组为较少数量的「分箱」方法。一般在建立分类模型时,需要对连续变量离散化,特征离散化后,模型会更稳定,降低了模型过拟合的风险。 分箱可以基于自定义划分的区间,也可以基于分位点。 数据分箱一般使用等距或者n等分,按分位数不等分极少。 在Python中,主要用到两种静态方法:pd.cut()和pd.qcut() cut():按值切割,即根据数据值的大小范围等分成n组,落入对应范围的进入到对应的组。 qcut():等频切割,即基本保证每个组里的元素个数是相等的。(也用于分位点分箱) 12345678910## cut# pandas.cut(x, bins, right=True, labels=None, retbins=False)# bins为int时,自动分箱;bins为list时,根据list划定各区间Age_category = pd.cut(train['Age'],5,labels = ['1','2','3','4','5'])Age_category = pd.cut(train['Age'],[0,5,15,30,50,80],labels=['1','2','3','4','5'])## qcut# pandas.qcut(x, q, labels=None, retbins=False)# q除了可以为分位数,支持传入分位点listAge_category = pd.qcut(train['Age'],[0.0,0.1,0.3,0.5,0.7,0.9],labels=['1','2','3','4','5']) 二者都含有label参数,用于产生分类后的类别标签。 两个方法均返回Categorical对象,其中包含了一个Series,即每个元素的分组标记。retbin参数控制是否返回bins,即整体的分组情况(分组区间)。 输入的bins和q为list时,区间分位点要按顺序排列,不能出现交叉,否则会报错。 特征编码数据分析前,除了将连续数据离散化,往往还需将类别特征转换为数值特征以方便后续的建模分析,因此需要对其进行编码。 常见的特征编码形式包括:标签编码(Label Encoding)与独热编码(One Hot Encoding) 在编码之前需要提取类别特征的所有可能值,因此会用到以下方法: unique():返回所有可能取值 nunique():返回可能取值的数量 value_counts():返回所有可能取值及其对应的数量 unique包含NaN值,但nunique和value_counts忽略了NaN值 此外,也可以使用其他方法来获取类别特征的可能取值,如groupby等: 12# groupby输出所有可能取值list(train.groupby('Sex').groups.keys()) 标签编码(Label Encoding)数字化编码即给特征的不同值赋予不同的数字标签(对类别变量中每一类别赋一数值),一般从0或1开始编码。 如: 原文本特征值 标签编码 S 0 C 1 Q 2 这种编码方式往往适用于类别间具有排序逻辑关系的数据(如:高、中、低),这种编码方式就保留了其中的大小关系。 而对于没有大小关系的特征,这样的编码无形之中给该特征添加了大小关系(如:S<C<Q)。例如(网上查到的例子):将[dog,cat,dog,mouse,cat]转换为[1,2,1,3,2]。对于不同机器学习模型来说,这里无形之间附加了新的信息——dog和mouse的平均值是cat。这会干扰模型的学习,影响模型的预测。 具体代码实现可以借助: replace(): map() sklearn库中的LabelEncoder类 1234567891011121314151617## Label Encoding# 01 replacetrain['Sex_num'] = train['Sex'].replace({'male':1,'female':2})# 也可使用list# train['Sex'].replace(['male','female'],[1,2])# 02 mapcabins = dict(zip(train['Cabin'].unique(),range(1,train['Cabin'].nunique()))) # 建立一个映射字典train['Cabin_num'] = train['Cabin'].map(cabins)# 03 LabelEncoderfrom sklearn import preprocessinglbe = preprocessing.LabelEncoder()#lbe.fit_transform(train['Embarked']) # 错误,embarked列中含有非string数据# 默认0开始编码,fit_transform可以拆成fit和transform,sklearn中都要先fittrain['Embarked_num']=lbe.fit_transform(train['Embarked'].astype(str)) sklearn库中的preprocessing提供了数据预处理需要用到的很多工具。该库往往需要先建立一个算法的对象,拟合fit()后,再进行预测predict()或转换transform()。 独热编码(One Hot Encoding) 独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。 这种编码方式为每个整数值都创建了一个二值数组,即0/1数组。对于每个特征,如果它有m个可能值,那么经过独热编码后,就变成了m个二元特征(如登船甲板这个特征有S、C、Q变成one-hot就是100, 010, 001)。并且,这些特征互斥,每次只有一个激活值(只含有一个1)。因此,数据会变成稀疏的。 原文本特征值 标签编码 S 100 C 010 Q 001 优点: 在回归、分类、聚类等机器学习算法中,往往需要计算特征之间的距离或相似度,这种计算往往基于欧式空间。One-hot编码将离散特征的取值扩展到了欧式空间,离散特征的某个取值对应欧式空间的某个点,这使得特征之间的距离计算更加合理。 扩充了特征空间 解决了标签编码附加大小关系的问题 缺点: 当类别很多时,特征空间会变得非常大,增加了计算量,在这种情况下,一般可以用PCA来减少维度。 独热编码可能会产生完全共线性问题。共线性问题可以在后续的相关性分析中解决(对相关系数过大特征予以处理)。 具体代码实现可以借助: pandas自带的get_dummies静态方法:可以设置prefix参数,即生成编码DataFrame中列名(特征名)前缀 sklearn库中的OneHotEncoder类: 1234567## One Hot Encoding# 01 OneHotEncoderohe = preprocessing.OneHotEncoder()embark_oh = ohe.fit_transform(train['Embarked'].astype(str).values.reshape(-1,1)).toarray() # .values.reshape(-1,1)是为了fit_transform函数输入为ndarray# 02 get_dummies()cabin_oh = pd.get_dummies(train['Cabin'],prefix='Cabin') fit_transform()方法相当于同时进行拟合fit()和转化/预测transform(),其要求输入的变量为String组成的ndarray,否则会报错。 reshape(-1,1)的作用是将Series转化为ndarray。 编码完毕后可能会使用到concat()方法,合并多个DataFrame。 文本提取在数据集中,文本类数据往往并非所有都是有效信息,需要通过类似爬虫用到的文本处理方法来提取其中的有效信息。 在pandas中,可以使用Series.str.extract()方法结合正则表达式提取字符串类型数据中的有效信息。 正则表达式的书写可以参考Learn Regex The Easy Way,可以配合正则表达式测试工具使用。","link":"/2020/08/21/Data-Analysis-ch2-note/"},{"title":"动手学数据分析(3)——数据重构","text":"本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。 Datawhale-动手学数据分析 数据来源Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率 Ch2-2 数据重构数据重构部分主要介绍了数据的合并、变形与分组。 数据合并与拼接数据的合并与拼接一般涉及四个方法/函数: 函数/方法 使用场景 concat() 可以在两个维度上拼接(外连接) append() DataFrame/Series的纵向拼接 merge() 两个DataFrame的横向拼接(基于任意共有列) join() 两个DataFrame的横向拼接(基于索引) 其中,concat和append方法倾向于简单的连接,merge和join则类似数据库中的连接。 连接方式这里回顾一下数据库的几种连接方式: 连接方式 含义 内连接 两个表的交集,左表和右表都有的才显示出来 外连接(全连接) 两个表的并集,有的都显示,连接后没有的值默认为空值 左连接(左外连接) 以左表为基准,左表有的右表也有就显示,左表有的右表没有填空值 右连接(右外连接) 以右表为基准,右表有的左表也有就显示,右表有的左表没有填空值 交叉连接(笛卡尔积) 左表每行依此连接右表各行,左表有m行,右表有n行,则连接后有m×n行 concatpd.concat以列表(List)形式传入需要连接的DataFrame,并可以设置连接的方向(axis),支持DataFrame/Series的组合,拼接方法默认为外连接(并集)。 123# keys参数可以方便辨别索引相同的行来源frames = [df1, df2, df3]result = pd.concat(frames, keys=['x', 'y', 'z']) append即纵向拼接,补充部分样本。支持列数不同的表拼接,没有数据将默认为NaN。ignore_index参数可以用于重置拼接后的数字索引。 merge/join一般用于表的横向连接,二者都支持上面提到的几种连接方式how='left'/'right'/'outer'/'inner'。主要差异在于join是靠索引进行连接的,而merge可以使用on指定连接的键(基准列)。 merge也使用index作为键进行连接的方式:将left_index与right_index均置为True。 此外,二者默认的连接方式不同,merge默认为内连接,join默认为左连接。当二者遇到基准列重复项时会使用笛卡尔积(交叉连接)。 merge函数有两种,一种是DataFrame的方法,一种是pandas自带的函数,二者的效果是一致的。 尽管join也有on参数,但只是更改了传入表中要连接的准则列。 关于几个问题的思考问题1:任务四和五中需要用到表的纵向连接,为什么都要求使用DataFrame的append方法,如果只要求使用merge或者join可不可以完成任务四和任务五呢? 思考:merge和join很少用于表的纵向连接,一般认为它无法用于纵向连接,但经过探索,在实际操作中,可以借助外连接实现表的纵向连接,只需传入的on参数为所有列,即基准列为所有列。 1234567891011# 尝试1result_try = pd.merge(result_up,result_down,on='PassengerId',how = 'outer')result_try# 失败# 因此,除了连接基准列其余列会直接横向合并,不管列名是否一致,同名自动加上_x/_y# 尝试2columns = list(result.columns)result_try = pd.merge(result_up,result_down,on=columns,how = 'outer')result_try# 成功 这样的操作是有限制的,倘若出现重复的行,就会出现只保留一行的情况,并不建议使用。 问题2:如何实现交叉连接?(来自Datawhale成员的提问) 思路:用到此前提到的遇到基准列重复项时会使用笛卡尔积(交叉连接)的兴致,为两个表构造一个数值相同的列作为key。 123df1['key']=1df2['key']=1pd.merge(df1,df2,on='key') 数据变形主要用到stack()和unstack()方法。 stack:最基础的变形函数,把Dataframe堆叠为二级索引的Series,可以看作将横向的索引放到纵向,参数level可指定变化的列索引是哪一层(或哪几层,需要列表) unstack:stack的逆函数,可以把Series解回为DataFrame 二者都具有将表转置(横索引 ↔ 纵索引)的作用,更高级的方法参见pivot() 数据分组GroupBy运行机制pandas中的数据分组基于GroupBy,既可用于Series也可用于DataFrame,groupby()方法返回一个对应的没有经过计算的DataFrameGroupBy对象或SeriesGroupBy对象,该对象本身不会返回任何东西,只有当相应的方法被调用才会起作用。 基本使用方式:在通过groupby()方法得到GroupBy对象后,可以使用其自带的各种统计量计算方法,将返回各组(列)的相应统计量(DataFrame/Series形式) 其运行机制如下: 即:根据输入的分组依据,运行groupby进行分组,后续使用统计函数时,同时对各个组应用该统计函数,并返回相应的统计量再聚合到一起以DataFrame形式返回。 对分组对象使用head函数,返回的是每个组的前几行,而不是数据集前几行。 groupby支持依据多列进行分组,分组后使用统计函数时会出现多级索引,可以使用多次[]操作器进行提取 基于列的聚合操作——aggagg()提供基于列的聚合操作。而groupby()可以看做是基于行,或者说index的聚合操作。 可以传入列表 可以直接输入完整的函数名:[np.sum, np.count] 可以只输入统计函数名(字符串):[‘mean’, ‘sum’] 可以传入dict——{列1:统计函数1,列2:统计函数2} dict的values还可以为多个统计函数的列表 结合rename使用,方便识别统计量进行后续分析 12345678910# 第一次尝试,可以使用numpy的函数df.groupby('Sex').agg([np.mean,np.sum])['Survived'].rename(columns={'mean':'mean_sex','sum':'survived_sex'})# 参考资料后第二次尝试df.groupby('Pclass').agg(['mean','sum'])['Survived'].rename(columns={'mean':'mean_pclass','sum':'survived_pclass'})# 这里调用的方法应该是?# 第三次尝试,参考课程答案,提示了agg可以传入dict以选择对其他每个特征的统计函数df.groupby('Sex').agg({'Survived':'sum','Fare':'mean'})# 再试试字典的value为func列表时df.groupby('Sex').agg({'Survived':['sum','count'],'Fare':'mean'}) 图片来源PANDAS 数据合并与重塑 Datawhale 动手学数据分析答疑群","link":"/2020/08/23/Data-Analysis-ch3-note/"},{"title":"动手学数据分析(4)——数据可视化","text":"本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。 Datawhale-动手学数据分析 数据来源Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率 Ch2-3 数据可视化数据可视化既可以用于探索性分析(如识别离群点、辅助数据变形),也可用于建模结果的交互式展示。以图形呈现数据的分布和趋势,以视觉为辅助,能够加深我们对数据的理解。 最终呈现的图形既可以是静态的,也可以是动态的/可交互的。 常用的第三方可视化库包含: matplotlib:最常用,包含了常用的所有图形,功能很全 seaborn:基于matplotlib的API高级封装,相对前者语法更简单,使用更方便 pyecharts:基于百度的可视化JS工具Echarts,可以实现很多JS ggplot/plotnine:语言结构继承自R的ggplot2,目前ggplot已被淘汰,一般选择plotnine plotly/bokeh等前端交互可视化库 具体的可视化库对比可以参见可视化工具不知道怎么选?深度评测5大Python数据可视化工具 matplotlib/seaborn课程主要介绍了关于matplotlib及其衍生库seaborn的基本使用方法。matplotlib的使用可以总结为三步: 创建画布 选择图表类型,绘制图象 配置图例,完善信息 创建画布画布(figure)和绘图对象(axes)画布(figure),相当于绘图工具中的画板。所有图形(Axes)绘制在画布上,图形的大小需要依据画布的大小。 一个figure可以包含多个axes,此时每个axes可以理解为一个子图,也可以只包含一个axes。 换句话说,figure是axes的父容器,而axes是figure的内部元素,而我们常用的各种图表、图例、坐标轴等则又是axes的内部元素。 可以借助官方文档中的解构图进行理解: 创建画布的方法创建画布,即创建figure和axes对象,Python for Data Analysis一书中主要介绍了2种方法: plt.figure+fig.add_subplot():创建一个画布(figure),此时默认只有一个axes,按需要再往画布里添加axes。 plt.subplots():接收3个数字或1个3位数(自动解析成3个数字)作为子图的行数、列数和当前子图索引。该方法将直接创建一个包含多个axes的figure,返回一个figure对象和包含一组axes对象的ndarray。 其中,plt.subplots()包含参数sharex、sharey,可以使所有子图的使用的x/y坐标系保持一致。 12345678910# fig即为返回的Figure对象,axes为以ndarray形式返回的一组AxesSubplot对象fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)# 可以直接调用AxesSubplot对象的方法绘图for i in range(2): for j in range(2): axes[i, j].hist(np.random.randn(500), bins=50, color='k', alpha=0.5)# 用于调整子图的间距plt.subplots_adjust(wspace=0, hspace=0) 得到以下无内部间距的图形: 上图中标签被遮挡住了,因此,在matplotlib中为了保证标签完整,须手动进行调整。 绘制图象用Matplotlib绘制可视化图表,有3种形式: plt:如常用的plt.plot(),不面向特定的实例对象,相当于在默认的画布上创建了一个虚拟的axes,在该axes上绘制图表。 面向对象:指面向Figure和Axes两类对象创建图表,即通过调用Figure或Axes两类实例的方法完成绘图的过程。这种方法将plt中的图形赋值给一个Figure或Axes实例,方便后续调用操作。 面向数据:直接调用DataFrame/Series/ndarray的方法绘图,该方法连同了pandas/numpy和matplotlib,直接基于数据绘制图象,方便快速可视化当前的数据 12345678data = np.random.randn(50)# plt绘图plt.plot(data)# 面向对象绘图ax = plt.subplots(222)ax.plot(data)# 面向数据绘图data.plot(ax=ax) plt和面向数据方法适用于简单的单图绘制,使用足够方便。面向对象方法则更适用于复杂的多图绘制,拥有更多的自定义设置,自由度更高。 图表的选择常用图表形式包括: plot:折线图/点图 scatter:散点图,常用于表述两组数据间的分布关系,也可由特殊形式下的plot实现 bar/barh:条形图或柱状图,常用于表达一组离散数据的大小关系,默认竖直条形图,可选barh绘制水平条形图 hist:直方图,用于统计一组连续数据的分区间分布情况 pie:饼图,主要用于表达构成或比例关系 选择图表的思想可以总结为一张图: 图线的调整与美化可以在之前提到的两种方法中添加图线的粗细(linewidth)、颜色(color)、标记(marker)、线型(linestyle)以及柱状图的宽度(width)等参数来进行图线的调整。不同图表含有的参数不同,需要查看相应绘图方法的参数介绍,这里不再详细叙述。 配置图例,完善图形在绘制好图象后,还需要进一步添加各种元素,例如设置标题、坐标轴、文字说明等,常用元素如下: title:设置图表标题 axis/xlim/ylim:设置相应坐标轴范围。axis是对xlim和ylim的集成,接受4个参数分别作为x和y轴的范围参数 grid:添加网格线 legend:添加label图例参数后,通过legend进行显示 xlabel/ylabel:用于设置x、y轴标题 xticks/yticks:用于自定义坐标轴刻度显示 text/arrow/annotation:在图例指定位置添加文字、箭头和标记 借用官方教程的一张图来介绍图象的各部分元素: 添加这些元素的方法也分为plt方法和面向对象方法,具体使用参考官方文档: plt.plot()——axes.plot() plt.legend()——axes.legend() plt.axes()——fig.add_axes() plt.subplot()——fig.add_subplot() plt.xlabel()——axes.set_xlabel() plt.ylabel()——axes.set_ylabel() plt.xlim()——axes.set_xlim() plt.ylim()——axes.set_ylim() plt.title()——axes.set_title() 此外,书中还介绍了一种类似json的元素设置方法,利用了axes对象的set(**props)方法,可以避免繁琐书写函数/方法的过程: 12345props = { 'title': 'My first matplotlib plot', 'xlabel': 'Stages'}ax.set(**props) SeabornSeaborn是对Matplotlib的高级封装,具有更美观的图形样式/颜色配置、更方便的语法结构。Seaborn有以下特点: API封装使得接口调用方便,少量参数就可以做出很不错的图表 图表类别更细,相比matplotlib更有指向性,具有统计学含义 配色、线型等,看起来更为优雅端庄 针对不同的展示环境(notebook/paper/poster/talk)提供了不同的绘图风格,方便使用 很有意思的点在于seaborn的缩写写作sns,并不是sbn。 Seaborn使用心得: 绝大多数绘图接口名——XXplot 绘图接口隐式参数常使用DataFrame,此时只需在x/y/hue三个参数中传入列名即可绘制图象;此外也支持numpy数组与list。 具体的使用参见官方API文档和gallery的examples,这里不作过多阐述。 举一个简单的例子: 1234567fig,axes = plt.subplots(1,2,figsize = (20,4))sns.lineplot(x=index,y=age_survived,ax=axes[0],label='Survived')sns.lineplot(x=index,y=age_unsurvived,ax=axes[1],label='Unsurvived')for ax in axes: ax.set_xlim([0,index.max()]) ax.set_title('The distribution of survival among different age')plt.show() pyecharts在matplotlib之外,稍微探索了一下pyecharts的使用,这里参考官方文档做一个简单介绍。 pyecharts的使用方式类似sklearn,完全面向对象。每个图表都是一个对象,创建图表首先需要实例化对应类的对象。 123456789101112131415161718192021from pyecharts.charts import Barfrom pyecharts import options as opts# 01 一般调用方法bar = Bar()bar.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])bar.add_yaxis("商家A", [5, 20, 36, 10, 75, 90])# render 会生成本地 HTML 文件,默认会在当前目录生成 render.html 文件# 也可以传入路径参数,如 bar.render("mycharts.html")# 02 链式调用方法,pyecharts 所有方法均支持链式调用bar = ( Bar() .add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]) .add_yaxis("商家A", [5, 20, 36, 10, 75, 90]) .set_global_opts(title_opts=opts.TitleOpts(title="主标题", subtitle="副标题")) # 或者直接使用字典参数 # .set_global_opts(title_opts={"text": "主标题", "subtext": "副标题"}))bar.render() 传入的数据格式必须为Python的原生格式,pyecharts基于echarts可视化库,不支持pandas/numpy等数据格式,因此需要进行数据格式的转换: 12345678# for int[int(x) for x in your_numpy_array_or_something_else]# for float[float(x) for x in your_numpy_array_or_something_else]# for str[str(x) for x in your_numpy_array_or_something_else]# 对于Series可以直接使用tolist()方法Series.tolist() pyecharts默认生成html文件,需要通过渲染器渲染为静态图像 12345678910from pyecharts.charts import Barfrom pyecharts.render import make_snapshot# 使用 snapshot-selenium 渲染图片from snapshot_selenium import snapshotbar = ......# 生成png图象make_snapshot(snapshot, bar.render(), "bar.png") Jupyter中的渲染只需将最后的render()方法改为render_notebook() 参考资料[1] Python for Data Analysis, 2nd Edition [2] Matplotlib入门详细教程(附导图) [3] Matplotlib Official Tutorial [4] python数据科学系列:seaborn入门详细教程 [5] pyecharts官方文档","link":"/2020/08/26/Data-Analysis-ch4-note/"},{"title":"算法入门布满荆棘?这份小白学习心得你值得看看","text":"前言缓慢而乏味的学习过程,每个人都经历过。信誓旦旦要学好一门编程语言,于是在知乎上查了大神们推荐的学习路径,买了教程里推荐的教材,下好推荐的 IDE ,一切准备就绪,心想着刷级打怪之路就此开始,结果书还没看几页,困意先盖过了求知欲。即便是认真敲了几天代码,基本的语法都学了一遍,到实际应用的时候,却完全无法下手,让人不禁开始对自己的学习能力产生怀疑。 现实生活中,更是经常会有以下情形:老师布置的任务,完全是我没有接触过的领域,怎么办?如果直接开始实战,我所学过的知识怕是完全不够解决遇到的问题,效果怕是不太行,还是先把任务里用到知识都先学一遍,啃完入门秘籍,再开始做任务吧!结果任务还没开始,就已经像上面描述的那样跪倒在了漫长的自学之路上。 这些都是作为算法小白的我亲身经历过的问题。在日常的学习中,带着高中的思维,我们总想着在搭建完整个知识架构后再去解决我们遇到的问题,完成老师布置的任务,以确保自己能够有把握地搞定问题。但是在大学学习中,往往没有那么多时间给你搭建整个知识架构,每一个领域所涉及的知识太多了,你不可能在短短的时间里搞定整个知识体系,这就需要我们转换思路,采用任务式驱动的学习方式。恰巧最近我在做一个老师布置的任务,这里以我个人的学习过程为例,给大家谈谈关于任务式学习的心得。 什么是任务式学习?先来聊聊传统的学习和任务式学习有什么区别。 身为经历了「五年中考,三年高考」在大学继续求学之路的学生党,我们的脑子里遵循着传统的教学大纲式学习模式,也就是如下的学习路径: 学习新知识 → 练习巩固 → 知识架构日趋完善 → 检验学习成果(考试/尝试解决问题) 大学的学习,也往往是这样,只是更多的需要我们在课后自学后解决一些不存在固定解的问题。相应的,我们思考的深度也会更深,但更多的还是会倾向于先搭建知识体系,再去解决问题。 搭建知识体系本身,没有任何问题,我们都需要笃实的基础来为后面的运用做准备。但,学习知识框架的过程,往往缓慢而乏味,尤其是对于编程这种需要大量实践的领域,我们缺少一些更直接的动力来驱动自身的学习。在这一点上,任务式学习给出了很好的解答。它以任务的完成和问题的解决为导向,能够有效地提高我们的学习效率和学习效果。任务式学习推进的逻辑如下: 尝试解决问题 → 遇到不懂的、无法解决的地方 → 学习相关概念与方法 → 知识架构日趋完善 → 检验学习成果(解决问题) 为什么说这样的逻辑,能够提高我们的学习效果呢? 首先,它给出了学习具体的目标。很明显,我们的学习目标是解决给定的具体的问题,而不是为了解决那些还未到来的不确定的问题。管理学中的 SMART 法则中提到了 Specific(具体性),人们往往不愿为那些不确定的目标而迅速行动起来,而任务式学习给定了我们具体的目标,为了达到这个目标,我们会更有动力去进行学习。 再者,以任务为导向,我们更容易走出「自己已经学了很多」的舒适圈,进行更深层次的思考。平常的自学虽然也需要进行深入的思考和探究,但由于大脑自身对困难的厌恶,我们更容易选择去进行浅层次的学习,而不愿意问自己为什么是这样并费上一番精力去思考其背后的原因和机理。而在任务式学习中,我们在尝试解决问题的过程中遇到的难点都需要我们自己对其背后的原理有足够的了解,因而解决问题本身,为我们进一步思考提供了动力,在问题的推动下,我们更容易走出沉溺舒适的怪圈,建立起基于自我思考的深层次的知识网络。 学习过程中,我们可以怎么做?在实际的任务式学习中,由于是从尝试解决问题开始,我们可能会遇到很多的不懂的地方,遇到这些不懂的地方,我们可以通过以下方式进行解决: 借助强大的搜索引擎和海量资料进行学习相比于高中,互联网的使用为大学学习提供了更易获得的、更完备的资料,前有 CSDN、博客园等网站提供的学习教程,后有 GitHub 提供的大量开源源码,再结合知乎上的一些学习经验和心得分享,可以说尝试解决问题过程中遇到的任何不懂之处,我们都可以优先诉诸搜索引擎寻找解答。 在搜索的时候也有一些小窍门:在搜索的时候尽量不要把我们遇到的难点写成问题的形式直接搜索,这样得到的搜索结果会相对较少。我们可以对于问题提取几个重要的关键词,用空格符隔开进行搜索,相对得到的结果就会更多。 此外不要忘记了编程中最有效的工具:帮助文档(API Documentation)。官方的帮助文档,可以解决你关于不认识的类、对象、方法的一切问题,只要搜索 「语言+api」 就可以找到对应的官方文档。很多的编译器内都附有自动查询鼠标悬停的代码的帮助文档的功能,以 IDEA 为例,在 Settings 内的 Editor 里就可以开启这个功能。 倘若在博客文章内还找寻不到答案,就可以借助相关的论文和教材针对性地搞清其背后的原理和逻辑,重点突破这些陌生的、不理解的障碍,这样也有助于加深我们的记忆。 同学、学长学姐是最好的求助对象上面提到我们可以借助搜索引擎和海量资料进行学习,那么,倘若我们依旧无法通过个人能力解决遇到的难点,应该怎么办呢? 「向有经验的人求助。」 有经验的人是指有在相关领域内领域知识更丰富的人,可以是一起学习的同学,通过思维碰撞可能可以得到答案;更好的选择是你求助所在的团队里的学长学姐们,他们可能是代码的提供者,也可能是该领域内的高手。 不要怕去和学长学姐交流,身为过来人,他们理解你们的苦衷,更愿意细心解答作为小白的你的问题。此外,通过问问题,你既结识了厉害的学长学姐,也可以打听到一些学习生活上的经验,何乐而不为呢? 当然,如果代码不是由你所在团队提供的,你也可以寻找代码作者的联系方式。作为这一切的「始作俑者」,他们能够提供的帮助自然是巨大的。 此外,一定不要憋着问题选择自己逞强解决,这样既拖慢了团队整体的进度,也容易使自己陷入漫长的焦虑之中。尝试求助于他人,可以让你绕开很多的坑,更快地到达目的地。 解决问题之外的一些体会在任务式学习的过程中,除了以上解决问题的几种有效途径,还有一些其他令我受益的思考: 遇到任务不要怕,犯错不可怕我们在接到任务的时候,往往会对老师给的任务产生畏难情绪:「我能够在规定时间内完成这个任务吗?」「我的基础很差怎么办?」「老师会不会因此而质疑我的能力?」。 一旦陷入了以上的思维,就很容易产生拖延和焦虑,并迈入之前提到的传统学习思路,越学越畏难,举步维艰。 首先,我们要知道,老师布置的任务,往往是基于他对你能力的认知和预期确定的。因而,很少会存在无法克服的困难,如果确实有很大的挑战,你可以和老师交流来调整任务的配置。 其次,不要怕犯错。仍处在学习阶段还未迈入工作阶段的我们有着最低的试错成本,你不会因为做错一件事就被「逐出师门」,做的慢不可怕,可怕的是不敢做、不去做。 最近读到感触很深的一句话,「要允许自己写/做出垃圾,不然你连垃圾都写/做不出来」。既然我们尚且还在学习,犯错就不该是我们的耻辱柱,而应该是我们的勋章墙,无论如何都是在前进。重要的是,要好好做你手上的事。积累了足够多的确定,之后的不确定,才会一点点减少。 要敢于抓住机会,主动寻找机遇在大一的时候,我是一个相当恐惧走出舒适圈的人,比如演讲答辩,比如和教授互动。一旦触碰到我的禁区,我就会选择沉默和退缩。但也是从那个时候开始,我开始尝试在圈边缘尝试迈出一小步,又一小步,一方面是出于压力,一方面是出于老师和自己的期许。 教授们有着最为丰富的资源,他们给我们提供任务,是在期许我们能够从中学习到更多得到成长,从而合作输出更多的成果。不要惧怕和教授的交流,他们不是你可望不可即、束之高阁的 boss 。倘若你在任务进行中遇到问题,可以和教授多交流,而不是畏畏缩缩,在担心影响他们对你印象的漩涡中自怨自艾。生活中的教授大多都是很可爱的人,他们会侃天侃地,也愿意和你分享一些有益的经验。 况且,能够得到教授的信任和支持,拿到接到任务的机会,本身就是对于你自身价值的一种肯定。勇敢地抓住机会,说不定就能为你赢得一个更好的未来。 结语以上就是我个人关于任务式学习的一些思考,当然以上的这些经验心得并不仅仅限于学习编程,任何需要解决问题的学习过程,都可以采取以上的方法。也就是说,想要学习新的知识,不妨先为自己寻找一个具体的问题和任务,而后再以解决具体问题为导向,利用我们对未知的好奇心,驱动自身的主动学习。此外,接到老师的任务,不必害怕,更应将其视作一次实践任务式学习的契机,从中获得更多的成长。","link":"/2020/03/30/algorithm-learning-experiences/"},{"title":"动手学数据分析(5)——数据建模与评估","text":"本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。 Datawhale-动手学数据分析 数据来源Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率 Ch3 数据建模与评估数据建模部分主要涉及模型的选择和评估方法,实际上这部分的内容已经超出了数据分析入门课程的范畴,数据建模涉及另外一套完整的理论体系,仅仅两天学习是完全不够的,需要后续的补充完善。 由于机器学习算法个人尚在入门阶段,数据建模和评估部分的细节有待深挖,只列举了少数的一些概念和相关的sklearn模块。笔记主要参考了南大周志华教授的《机器学习》(西瓜书)。 数据建模在对数据进行预处理和探索性分析后,我们已经对数据集的组成、特征分布有了相应的了解,此时可以根据建模需要以及数据集特点选定自变量和因变量,划分数据集后,选定相应的学习算法或模型对数据进行建模。 数据集的划分为了验证模型的泛化能力,需要将数据集拆分为训练集与测试集,在训练集上训练模型,测试集上测试模型的效果。常见的数据集划分方法包括: 留出法(Hold out) 交叉验证法(Cross Validation) 自助法(Bootstrapping) 其中,交叉验证法还衍生出了留一法(Leave-One-Out,简称 LOO)。 留出法直接将数据集$D$划分为两个互斥的部分,其中一部分作为训练集$S$,另一部分用作测试集$T$。训练集和测试集的比例一般为7:3。 尽可能保持数据分布的一致性,避免因数据划分过程引入的额外偏差而对最终结果产生影响。在分类中,保留类别比例的采样方法称为分层采样(Stratified Sampling)。 一般要采用若干次随机划分、重复进行实验评估后取平均值作为留出法的评估结果。如果只做一次分割,模型对训练集、验证集和测试集的样本数比例,还有分割后数据的分布是否和原始数据集的分布相同等因素会比较敏感。 交叉验证法将数据集$D$划分为$k$个大小近似的互斥子集,每个子集都尽可能保证数据分布的一致性(分层采样)。每次将$k-1$个子集的并集作为训练集,剩下的1个子集作为测试集。交叉验证重复$k$次,每个子样本验证一次,平均$k$次的测试结果,最终得到一个单一估测。这种划分数据集的测试方法称作 $k$ 折交叉验证(k-fold cross validation) 采用交叉验证方法时需要将学习数据样本分为两部分:训练数据样本和验证数据样本。并且为了得到更好的学习效果,无论训练样本还是验证样本都要尽可能参与学习。一般选取10重交叉验证即可达到好的学习效果。 与留出法相似,将数据集$D$划分为$K$个子集同样存在多种划分方式。划分方式引入的差别使得 $k$ 折交叉验证通常要随机使用不同的划分重复$p$次,最终的评估结果是这 $p$ 次 $k$ 折交叉验证结果的均值,常见的有10 次10折交叉验证。 使用交叉验证方法的目的主要有3个: 从有限的学习数据中获取尽可能多的有效信息 交叉验证从多个方向开始学习样本的,可以有效避免陷入局部最小值 可以在一定程度上避免过拟合问题 留一法(Leave-One-Out) 即当交叉验证的子集容量均为1的情况,相当于对于每个样本进行一次预测。这种方法的评估效果往往比较准确,但对于数据集很大的情况,计算开销过于庞大,且其估计效果不一定好于其他方法。 自助法(Bootstrapping)用于解决留出法、交叉验证法由于训练集与原数据集$D$规模不一致带来的偏差,以及留一法计算开销过大的问题。 基本思路:即在含有$m$个样本的数据集$D$中,进行$m$次有放回地随机抽样,组成的新数据集$D’$作为训练集,$D\\backslash D’$ 作为测试集。 这样得到的训练集规模与原始数据集一致,且$m$趋向于无穷时,初始数据集约有$1/e=36.8%$的样本不会出现在训练集中(作为测试集)。这种方法适合数据集较小无法划分训练测试集的情况,并且能从初始数据集中产生多个不同的数据集,但该方法产生的数据集分布会与原数据集产生偏差。 初始数据量足够时,更多地采用前两种方法。数据集较小难以划分时,可以采用自助法。 代码实现涉及sklearn的以下函数: sklearn.model_selection.train_test_split() sklearn.model_selection.cross_val_score() 函数方法的使用不做过多说明,使用的方法和用到的基本参数见代码示例,具体包含的参数可以查阅sklearn对应的API文档。 12345## 留出法from sklearn.model_selection import train_test_split# 划分数据集X_train, X_test, y_train, y_test = train_test_split(X,y,stratify = y,test_size=0.3,random_state = 0) random_state默认为False,当其设定为整数,则划分的数据集总是一致的,一般用于复现结果。stratify参数则代表分层抽样的分层依据,此处即:按照y的构成分层抽样。 123456789101112## 交叉验证法# 该模块中,有用于划分几折的方法,也有交叉验证输出k折准确率的方法from sklearn.model_selection import cross_val_scorefrom sklearn.linear_model import LogisticRegression# 训练模型,计算交叉验证各次的分类预测精度lr = LogisticRegression(C=100,max_iter=1000)scores = cross_val_score(lr, X_train, y_train, cv=10)# 求均值输出print('交叉验证精度平均值为:',scores.mean()) 学习算法的选择首先需要确定问题的类型,是监督学习还是无监督学习?如果是监督学习,是分类问题还是回归问题?如果是分类问题,是二分类问题还是多分类问题?确定问题的类型可以帮助我们找到适合该类问题的相应算法。 在划定问题类型后,已经可以排除掉很多的算法。接着需要了解数据的类型和形态。有些算法可以利用较小的样本集合工作,而另一些算法则需要海量的样本。特定的算法对特定类型的数据起作用。在之前的预处理和探索性分析中,我们已经对数据有了一定的了解,可以根据数据集的大小和特征稀疏度来进行选择。(需要建立在对模型原理和特点充分了解的基础上)此外,还需要考虑模型的复杂度是否符合实际的约束条件,如模型训练速度、预测速度与数据容量。 模型的复杂度是一个影响算法选择的重要标准。复杂的模型具备下列特征: 依赖于更多的特征进行学习和预测(例如,使用十个而不是两个特征来预测目标) 依赖于更复杂的特征工程(例如,使用多项式特征、交互特征或主成分) 更大的计算开销(例如,需要一个由 100 棵决策树组成的随机森林,而不是一棵单独的决策树) 除此之外,同样的算法可以基于参数的个数和超参数的选择而变得更加复杂。例如: 回归模型可以拥有更多的特征,或者多项式项和交互项。 决策树可以拥有更大或更小的深度。 将相同的算法变得更加复杂增加了发生过拟合的几率。 这里需要对大部分的机器学习算法有所了解,学习相应模型的原理,不然只能成为一个调包侠。 【思考】 为什么线性模型可以进行分类任务,背后是什么样的数学关系? 线性模型可以拟合出类别之间的分界线,模型根据该分界线来进行分类。 对于多分类问题,线性模型是怎么进行分类的? 对于多分类问题模型就可以训练得到多条分界线,将整个数据集划分为多个区域,每个区域对应一个类别 训练模型并输出预测结果sklearn涵盖了很大一部分的机器学习模型,但基本的调用方式都是一致的: 确定要使用的算法,定位其在sklearn中的位置 查找算法的初始化属性(超参数),创建一个相应算法的实例,如:lr= LogisticRegression(C=100,max_iter=500) 调用算法的fit方法,传入训练集以进行模型的训练 调用算法的predict()、predict_proba()(部分算法含有decision_function()方法)来获得训练好的模型输出的结果 输出准确率(对于分类问题,score()方法)或代价(对于回归问题),评估模型输出的结果 举一个例子: 123456789101112131415# 定位,导包from sklearn.ensemble import RandomForestClassifier# 创建对象,设定模型超参数rfc = RandomForestClassifier(max_depth = 6)# 训练模型rfc.fit(X_train, y_train)# 预测结果y_predict = rfc.predict(X_test)# 计算预测准确率# 方式1,自己写的accuracy = (y_predict == y_test).sum()/y_predict.sizeprint('测试集预测准确度为:',accuracy)# 方式2,自带的方法print('训练集集预测准确度为:',rfc.score(X_train, y_train))print('测试集预测准确度为:',rfc.score(X_test, y_test)) 模型超参数的选择,往往借助试错法(try error)。除了训练集与测试集,一般会从数据集中取出单独的一部分作为验证集(validation set)用于超参数的优化,超参数的优化也是机器学习与深度学习重要的组成部分。 对于某些应用场景,如违约概率预测,需要给出可能性/不确定度,分类器一般情况下有两种方法估计不确定度: 概率的形式,如predict_proba() 置信分,如decision_function() 前者返回模型输出的一组,测试样本属于各个类别的概率。基于每个样本的一组概率值,一种决策方式为将概率从大到小排布,取最高者作为模型最终决策结果;另一种决策方式则只认可概率超过阈值的类别为决策结果,对于不平衡类别数据而言,多类别数据会学到更多信息,所以也就可以适当增加其决策的难度,比如设置大于0.6才能分为1。 后者适用于二分类问题,默认返回一个置信分,置信分大于0,模型认为输入测试样本属于第一类,反之认为其属于第二类。 模型评估模型的评估也是建立在数据预测的具体需求上的,这里介绍的评估指标主要面向分类问题。 混淆矩阵根据二分类的分类结果可以将样例分为四类:真正例(TP)、假正例(FP)、真反例(TN)和假反例(FN),一图以概之: 基于样例的四种分类矩阵——混淆矩阵,衍生出了查准率、查全率等一系列模型的评价指标。 准确率(accuracy)一般输出的结果,即预测结果和原来样本有多少一致。 查准率(precision)针对预测结果而言的,表示的是预测为正的样本中有多少是真正的正样本。预测为正就有两种可能了,一种就是把正类预测为正类(TP),另一种就是把负类预测为正类(FP)。$$P=\\frac{TP}{TP+FP}$$ 查全率(recall)针对原来的样本而言的,表示的是样本中的正例有多少被预测正确了。也有两种可能,一种是把原来的正类预测成正类(TP),另一种就是把原来的正类预测为负类(FN)。$$R = \\frac{TP}{TP+FN}$$二者仅仅分母不同,结合混淆矩阵即可加深理解: PR曲线PR曲线以查准率为纵轴,查全率为横轴。 PR曲线直观地显示出学习器在样本总体上的查全率、 查准率,在进行比较时,有两种情况: 一个学习器的PR曲线被另一个学习器的曲线完全包住:后者的性能优于前者(双高) 两个学习器的PR曲线相交:使用曲线下面积进行比较(一定程度上表征了学习器在查准率和查全 率上取得相对双高的比例) 由于面积不易计算,更多地使用到下方的几种度量指标。 F1度量F1,即查准率与查全率的调和平均(倒数的平均),可以作为综合衡量查准率与查全率的指标:$$F1=\\frac{2\\times P\\times R}{P+R}=\\frac{1}{2}(\\frac{1}{P}+\\frac{1}{R})$$对于不同的使用场景,人们对学习器的偏好指标是不同的,例如商品推荐中,平台更希望推荐的内容的确是用户感兴趣的(查准率),而其他场景下,例如缺陷零件检验中,人们更偏好查出更多可能存在缺陷的零件(查全率)。因此诞生了$F1$的一般形式$F_{\\beta}$,$\\beta$代表查全率对查准率的相对重要性:$$F_\\beta=\\frac{(1+\\beta^2)\\times P\\times R}{(\\beta^2\\times P)+R}=\\frac{1}{1+\\beta^2}(\\frac{1}{P}+\\frac{\\beta^2}{R})$$ 代码实现主要使用到sklearn.metrics模块中以下的一些方法: confusion_matrix(y_test, y_test_predict) classification_report(y_test, y_test_predict) 该模块中还包含了很多其他的评价指标以供使用。 1234567891011121314151617# 导入对应模块from sklearn.metrics import confusion_matrixfrom sklearn.metrics import classification_report# 模型预测结果lr.fit(X_train, y_train)y_test_predict = lr.predict(X_test)# 输出混淆矩阵confusion_matrix(y_test, y_test_predict)# 也可以以热力图的形式来输出混淆矩阵#import seaborn as sns#plt.figure(figsize=(8,6))#sns.heatmap(confusion_matrix(y_train, rfc.predict(X_train)),annot=True,cmap='Blues')#plt.xlabel('Predicted labels')#plt.ylabel('True labels')#plt.show()# 精确率、召回率以及F1print(classification_report(y_test, y_test_predict)) 返回结果如下: ROC与AUCROC(受试者工作特征)曲线以真正例率为纵轴,假正例率为横轴绘制曲线。图中每个点则是不同截断点下计算的FPR和TPR,截断点相当于模型对于样本的概率输出,也可以说是打分Score。 ROC计算过程: 得到所有样本的概率输出(属于正样本的概率) 根据每个测试样本属于正样本的概率值从大到小排序 从高到低,依次将Score值作为阈值threshold,当测试样本属于正样本的概率大于等于这个threshold时,认为它为正样本,否则为负样本。 每次选取一个不同的threshold,就可以得到一组FPR和TPR,即ROC曲线上的一点。 将点依次连线,构成ROC ROC可以评判学习器得到实值排序质量的好坏,而排序质量又体现了综合考虑学习器在不同任务下的期望泛化性能,因此,ROC是研究学习器泛化性能的一项有力工具。 12345678910111213## ROC曲线的绘制# 导入绘制ROC曲线的模块from sklearn.metrics import roc_curve# 得到ROC曲线的系列值fpr, tpr, threshold = roc_curve(y_test, lr.decision_function(X_test))# 绘制ROC曲线sns.lineplot(x=fpr,y=tpr,label='ROC')close_zero = np.argmin(np.abs(threshold))plt.xlabel("FPR")plt.ylabel("TPR")plt.plot(fpr[close_zero], tpr[close_zero], 'o', markersize=10, label="threshold zero", fillstyle="none", c='steelblue', mew=2)plt.legend() AUC即ROC曲线下方的面积,AUC考虑的是样本预测的排序质量,因此它与排序误差有紧密联系,而排序误差实际就是ROC曲线上方的面积,具体数学解释可以参考西瓜书P35。 12# 计算AUCroc_auc_score(y_test, lr.decision_function(X_test)) 【思考】 对于多分类问题如何绘制ROC曲线? 两两一组计算TPR/FPR,再取平均,这种方法得到的ROC可以称作宏观ROC 两两一组计算TP/FP/TN/FN,取平均后计算TPR/FPR,称为微观ROC 从ROC曲线能得到什么信息?这些信息可以做什么? 模型的泛化性能,即学习器在不同任务下的期望泛化性能,可以评价不同模型的好坏","link":"/2020/08/28/Data-Analysis-ch5-note/"},{"title":"统计学习方法笔记(1)——统计学习及监督学习概论","text":"前言机器学习入门的战线可以说是拉的很长,想着要早早学完理论,实战数据科学赛事,为研究生打好基础,但到现在也依旧是门外小白一枚。 总是会在浩如烟海的学习资料里翻来覆去,西瓜书只翻了三四章,便看到网上说西瓜书前期学习作为参考资料更好,具体的公式推导不详细,不适合作为入门自学读物,自学搞懂原理中文教材还是选李航教授的《统计学习方法》比较好,便有些犹疑是否要先学完西瓜书。加之刚好看到Datawhale群里小伙伴开了个统计学习群,于是就给自己开了这个新坑,希望能完整系统学完这本《统计学习方法》。 笔记以《统计学习方法》第二版为主线,辅以学习过程中遇到的一些困惑点的进一步深挖。考虑到按自己传统的笔记方式很可能只是拎各种概念和方法,缺乏自己的深度思考,写完自己查阅也不方便,内容会很冗长,参考了群友Terr的笔记方式,决定尽可能精炼内容,以Q&A+思维导图的方式来辅助后续的学习,后续学习有新的思考也会逐渐补充至Q&A中。 1 统计学习及监督学习概论思维导图梳理因为内容很多,把整章划分成了四个部分。 完整思维导图 1.1 统计学习Q1 统计学习、统计学和机器学习到底是什么关系?每次看到「统计学习」四个字都会困惑,统计学就统计学,统计学习又是什么?《统计学习方法》一书为什么又是机器学习的入门书?加之专业课学习计量经济学后,发现机器学习方法有很多都是与统计学习方法一致的,愈发困惑。 书中没有对二者加以明确的区分,但貌似并没有将二者对等: 统计学习(statistical learning)是关于计算机基于数据构建概率统计模型,并运用模型对数据进行预测和分析的学科。统计学习也称统计机器学习(statistical machine learning)。 现在,当人们提及机器学习时,往往是指统计机器学习。所以可以认为本书介绍的是机器学习方法。 个人理解: 统计学习区别于统计学,但其根基在于概率论与统计学,是机器学习的先决条件。统计学的目标更多地在于假设检验和因果推断,侧重于寻找已有数据中各变量逻辑上的因果关系(二者之间有逻辑联系),关心推断或预测的置信度,实际处理中更关注统计量服从什么分布、假设检验是否显著、模型拟合是否合理等问题;统计学习则主要是利用建立的统计模型进行预测和分析,更侧重于数据之间的相关关系(即在数学意义上可以观察到二者存在联系,线性/非线性关系)。 统计注重inference(推论),而ML注重prediction(预测) 后续,查阅了维基百科,科学家们更多地认为统计学习是机器学习和统计学的交叉领域。且从机器学习发展里程来看,机器学习最早起源于符号学习,到20世纪七十年代在其基础上发展出了统计学习,而与此同时,连接主义派兴起,发展出了以神经网络为代表的基于连接的机器学习。因此,个人认为统计学习和机器学习二者不能等同。除统计学习之外,还应包括以ANN为代表的神经网络与深度学习。 具体关系参考了一下知乎ST ym给出的关系图(可能不一定正确): 1.2 统计学习的分类Q2 频率学派和贝叶斯学派的区别?二者隶属于统计学,是统计学的两大学派,代表两套不同的统计推断体系。 核心区别在于对统计参数的看法。统计参数是对未知总体分布的一种描述,统计推断即通过已有样本来对总体的分布进行推断,估计总体的参数。 频率派认为概率是客观的,事件本身就具有随机性,因此,参数是客观存在的,是一个确定的值,只要找到这个参数,就能够描绘随机事件(现象),了解到总体的分布。 贝叶斯学派则认为概率是主观的,事件本身不具有随机性,随机性来自于不同观察者对事件发生的主观判断,统计参数也是随机的,因为我们不能观察到整体,我们并不能知道参数的具体值,这和一个随机数没有区别,因此参数空间里的每个值都可能是真实的值,但是概率不同。 因此,频率派最常关心的是似然函数和置信区间,即有多少的把握圈出那个正确的参数;而贝叶斯派最常关心的是后验分布和先验分布,利用二者来寻找参数的分布。(对贝叶斯估计的理解可以看下一个问题) Q3 极大似然估计和贝叶斯估计的区别?大二上学的概率论与数理统计,印象不够深了,上课只讲了极大似然估计,没有提及贝叶斯估计方法,这里重新做一个整理。 似然与概率 似然(likelihood):已知结果,对参数是某个值的可能性预测 概率(probability):已知参数,对结果可能性的预测 In statistics, the likelihood function (often simply called the likelihood) measures the goodness of fit of a statistical model to a sample of data for given values of the unknown parameters. It is formed from the joint probability distribution of the sample, but viewed and used as a function of the parameters only, thus treating the random variables as fixed at the observed values. 似然函数针对给定的未知参数值来衡量统计模型与数据样本的拟合优度。 它是由样本的联合概率分布形成的,但是仅作为参数的函数进行查看和使用,它将随机变量固定为观察值。 似然与概率其实都是围绕$P(X|\\theta)$展开的。 当固定$\\theta$,则该函数为概率函数,表示随机变量$X$为不同值的概率。 当固定$x$,则该函数为似然函数,一般写作$L(\\theta|x)$或$L(x;\\theta)$,表示不同$\\theta$下,$X=x$的概率 极大似然估计(MLE)基于频率学派,既然要找到那个实际的参数,那么使得观测数据(样本)发生概率最大的参数就是最好的参数。 即:使得似然函数值在现有$x$的情况下最大的$\\theta$值。$$\\max_\\theta L(X;\\theta)$$由于样本之间满足独立同分布假设,似然函数可以通过样本概率函数的乘积得到:$$L(X;\\theta)=\\prod_{i=0}^nP(x_i|\\theta)$$为了求解方便,通常会将似然函数转成对数似然函数,然后再求解。对数函数并不影响函数的凹凸性。 最大后验估计(MAP)相比于极大似然估计,最大后验概率估计认为$\\theta$是一个随机变量,满足一定分布(先验分布),求解时除考虑似然函数$P(X|\\theta)$外,还要考虑$P(\\theta)$。 即:选择使$P(X|\\theta)P(\\theta)$最大的$\\theta$。$$\\arg\\min_{\\theta}P(X|\\theta)P(\\theta)$$与极大似然估计的思想是一致的,寻找使当前事件发生可能性最大的参数值,只不过多考虑了先验分布,吸收了贝叶斯学派的部分思想。这里的$P(\\theta)$需要进行描述(定义),由于最终参数的确定需依赖于先验分布,因此,受主观影响较大。 最大后验概率估计可以看作是正则化的最大似然估计,当然机器学习或深度学习中的正则项通常是加法,而在最大后验概率估计中采用的是乘法,$P(\\theta)$是正则项。在最大似然估计中,由于认为$\\theta$是固定的,因此$P(\\theta)=1$。 实际上,由于$P(X)$已知,最大化的其实是$\\theta$的后验概率$P(\\theta|X)$,这就是其名字的来源。$$\\arg\\min_{\\theta} P(\\theta|X)=\\arg\\min_{\\theta}\\frac{P(X|\\theta)P(\\theta)}{P(X)}\\propto\\arg\\min_{\\theta}P(X|\\theta)P(\\theta)$$ 贝叶斯估计最大后验估计的扩展,贝叶斯估计同样假定$\\theta$是一个随机变量,但贝叶斯估计并不是直接估计出$\\theta$的某个特定值,而是估计$\\theta$的分布。在已知$X$的情况下,描述$\\theta$的分布即描述后验概率$P(\\theta|X)$。 贝叶斯估计中,先验分布$P(X)$是不可忽略的,可以使用全概率公式求得:$$P(\\theta|X)=\\frac{P(X|\\theta)P(\\theta)}{P(X)}=\\frac{P(X|\\theta)P(\\theta)}{\\int_\\Theta P(X|\\theta)P(\\theta)d\\theta}$$贝叶斯估计的求解非常复杂,因此选择合适的先验分布就非常重要。一般来说,计算积分$\\int_\\Theta P(X|\\theta)P(\\theta)d\\theta$是不可能的。 如果后验分布的范围较窄,则估计值的准确度相对较高,反之,如果后验分布的范围较广,则估计值的准确度就较低。 小结一下 都是参数估计的方法,即预先知道/假设样本的分布形式,只是一些参数未知。 极大似然估计和最大后验估计都是点估计。即把参数看成未知常数,通过最大化似然和后验概率实现。 极大似然估计和最大后验估计都在寻找使当前事件发生可能性最大的参数值。最大似然最简单,只需找到使得样本似然函数最大的参数。而最大后验优化似然函数为似然 * 先验概率,考虑了参数的先验概率,多了一个先验概率项。 贝叶斯估计把参数看成一个随机变量,属于分布估计。贝叶斯是最大后验函数的扩展,和二者最大的不同在于,其最终得到的是一个参数分布(后验分布)。 因此,可以认为,极大似然估计是频率派的方法,贝叶斯估计是贝叶斯学派的方法。 尽管最大后验估计与贝叶斯统计共享先验分布的使用,通常并不认为它是一种贝叶斯方法,这是因为最大后验估计是点估计,然而贝叶斯方法的特点是使用这些分布来总结数据、得到推论。 Q4 怎么理解极大似然估计是经验风险最小化的一个例子,最大后验估计是结构风险最小化的一个例子?先不管证明,基于Q3里对三种参数估计方法的介绍,可以这样理解,极大似然寻找使当前已观察到样本可能性最大的参数。而经验风险最小化,其实就是寻找预测结果与真实结果差异可能性最小的参数。两者在思想上是一致的。结构风险最小化在经验风险最小化的基础上考虑了正则项(罚项),这和最大后验概率估计在最大似然估计多考虑了参数的先验分布类似,都额外考虑了参数自身的特征。 形式上,最大后验概率/极大似然取对数后,乘法就变成了加法,形式也基本一致了。 下面对其作数学证明(课后习题2): 对数损失函数:$$L(Y,P(Y|X))=-\\log P(Y|X)$$经验风险函数式:$$\\begin{align}R_\\text{erm}(P_\\theta)=&\\min_{P_\\theta\\in\\mathcal{F}}\\frac{1}{N}\\sum_{i=1}^NL(y_i,P_\\theta(y_i|x_i)) \\\\=&-\\min_{P_\\theta\\in\\mathcal{F}}\\frac{1}{N}\\log(\\prod_{i=1}^NP_\\theta(y_i|x_i)) \\\\=&\\frac{1}{N}\\max_{P_\\theta\\in\\mathcal{F}}\\log(\\prod_{i=1}^NP_\\theta(y_i|x_i))\\end{align}$$相当于极大似然估计(最大化对数似然函数):$$\\max_{\\theta\\in\\mathrm{R}^n}\\log(\\prod_{i=1}^NP_\\theta(y_i|x_i))$$得证。 同理,当模型复杂度由模型的先验条件表示时,结构风险表达式多添加了一个正则项$P(\\theta)$,由于对数函数将加法转化为乘法,最终形式也与最大后验概率函数相同。 1.3 统计学习三要素Q4 优化和机器学习为什么总是有交叉的内容?因为专业涉及优化运筹,会需要学习相关的优化算法,之前学习的时候发现很多算法是通用的,不太明白优化和机器学习的关系,这个问题,书里给了很好的解答。 通过确定统计学习三要素之一的策略(经验风险最小化和结构风险最小化),机器学习问题就转化为了最优化问题。即:当我们确定了选择最优模型的准则,就已经确定了最优化问题里的目标函数,后续要解决的就是如何求解得到这个最优模型的问题。(标准最优化) 因此就会用到非线性规划中的梯度下降法以及其他优化算法。 Wikipedia在机器学习词条中这样写道: The study of mathematical optimization(最优化) delivers methods, theory and application domains to the field of machine learning. 当然,最优化问题更倾向于得到一个精确解,机器学习问题则更关注模型最终的学习效果,即对模型在未知样本的代价上的最小化。换句话说,优化是机器学习的重要内容和过程,它能够使得其在给定的策略下学习到一个好的模型。然后我们再基于这个模型在新数据上进行预测,来得到我们需要的结果。 The difference between the two fields arises from the goal of generalization: while optimization algorithms can minimize the loss on a training set, machine learning is concerned with minimizing the loss on unseen samples. 1.7 生成模型与判别模型Q5 生成方法中的隐变量是什么意思?隐变量(latent variable),即那些不能被直接观察到,但可以通过其他可观测变量的特征间接推断出的变量。 举个栗子,经济学上的生活质量就可以视作隐变量,无法直接被衡量,但可以借助其他可观察变量来推断它, 衡量生活质量的可观测变量包括财富、就业、环境、身心健康、教育、娱乐和休闲时间以及社会归属感等等。 In statistics, latent variables are variables that are not directly observed but are rather inferred (through a mathematical model) from other variables that are observed (directly measured). Mathematical models that aim to explain observed variables in terms of latent variables are called latent variable models. 有一系列不同的模型和方法可以利用隐变量,或允许对隐变量进行推断,如隐马尔可夫模型、因子分析。推断隐变量的方法往往会涉及降维,通过对在高维空间上的特征进行降维,就可以得到隐含的隐变量。 Q6 标注问题与分类问题的关系or区别?二者的输出变量都只能取有限个离散值,区别在于输入输出的单位: 分类(classification):构建一对一的映射,以单个样本为单位进行输入输出。模型形式表示为为$P(Y|X)$或$Y=f(X)$ 标注(tagging):分类问题的推广,构建多对多的映射,以序列为单位进行输入输出。模型形式表示为条件概率分布$P(Y^{(1)},Y^{(2)},\\dots,Y^{(n)}|X^{(1)},X^{(2)},\\dots,X^{(n)})$。较多应用于信息抽取与自然语言处理。 参考资料[1] 维基百科相应词条 [2] 传统的统计和机器学习的区别和联系是什么? - PengyuCheng的回答 - 知乎 [3] 贝叶斯学派与频率学派有何不同? - Xiangyu Wang的回答 - 知乎 [4] *贝叶斯估计、最大似然估计、最大后验概率估计【写的非常清楚全面,梳理了ML中常用的概率统计知识】","link":"/2020/09/20/statistical-learning-method-ch1/"}],"tags":[{"name":"data_analysis","slug":"data-analysis","link":"/tags/data-analysis/"},{"name":"学习心得","slug":"学习心得","link":"/tags/%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/"},{"name":"machine_learning","slug":"machine-learning","link":"/tags/machine-learning/"},{"name":"statistical_learning_methods","slug":"statistical-learning-methods","link":"/tags/statistical-learning-methods/"}],"categories":[{"name":"机器学习","slug":"机器学习","link":"/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"}]}