系列文章目录
监督学习:参数方法
【学习笔记】 陈强-机器学习-Python-Ch4 线性回归
【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归
【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv)
【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归
【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析
【学习笔记】 陈强-机器学习-Python-Ch8 朴素贝叶斯
【学习笔记】 陈强-机器学习-Python-Ch9 惩罚回归
【课后题练习】 陈强-机器学习-Python-Ch9 惩罚回归(student-mat.csv)
监督学习:非参数方法
【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch10 KNN法
前言
本学习笔记 仅为以防自己忘记了,顺便分享给一起学习的网友们参考。如有不同意见/建议,可以友好讨论。
本学习笔记 所有的代码和数据都可以从 陈强老师的个人主页 上下载
参考书目:陈强.机器学习及Python应用. 北京:高等教育出版社, 2021.
数学原理等 详见陈强老师的 PPT
参考了网友阡之尘埃的Python机器学习03——逻辑回归
陈强老师的逻辑回归的Python案例 使用的是泰坦尼克号乘客的存活数据 titanic.csv
一、描述性统计分析
1. 数据整体情况
#导入 库
import pandas as pd
import numpy as np
#读取CSV文件的路径:csv_path 是一个原始字符串,通过 r'' 标记来确保路径中的反斜杠 \ 不会被视为转义字符。
csv_path = r'D:\桌面文件\Python\【陈强-机器学习】MLPython-PPT-PDF\MLPython_Data\titanic.csv'
titanic = pd.read_csv(csv_path) #通过 Pandas 库中的 read_csv 函数来读取指定路径 csv_path 下的 CSV 文件,并将其加载到名为 titanic 的 Pandas 数据框(DataFrame)中。
titanic.shape
输出结果:(32, 5)
32个观测值与5个变量
#查看数据详情
titanic
输出结果:
以上(部分)结果可知,变量都为分类变量(categorical variables),如Age,Sex,Class.
2. 对数据框中最后一个变量Freq 进行处理
变量Freq为每个观测值在样本中出现的次数。
1)转换为NumPy数组
#使用to_numpy(): 将选定列的数据转换为NumPy数组
freq = titanic.Freq.to_numpy() #titanic['Freq']:从 titanic 数据框中选择名为 'Freq' 的列; .to_numpy(): 将选定列的数据转换为 NumPy 数组,存储在 freq 中。
freq #显示freq
输出结果:
array([ 0, 0, 35, 0, 0, 0, 17, 0, 118, 154, 387, 670, 4,
13, 89, 3, 5, 11, 13, 0, 1, 13, 14, 0, 57, 14,
75, 192, 140, 80, 76, 20], dtype=int64)
#dtype=int64 是指数据类型为64位整数(integer)
2)创建index数组
#使用np.repeat()创建一个数组(值重复出现)/也可以用np.tile()
index = np.repeat(np.arange(32), freq)
#np.arange(32): 创建一个长度为 32 的数组,其中包含从 0 到 31 的整数。
#np.arange([start, ]stop, [step, ]dtype=None)。 start: 可选参数,表示序列的起始值,默认为0。 stop: *必选参数*,表示序列的终止值(不包含在内)。 step: 可选参数,表示序列中的步长(默认为1)。 dtype: 可选参数,指定数组的数据类型,默认根据输入推断。
index #输出index数组
index.shape #输出index数组的形状,即其维度信息。
index[:10] #输出index数组的前10个数值
输出结果:
#输出index数组
array([ 2, 2, 2, ..., 31, 31, 31])
#输出index数组的形状
(2201,) #这个数组是一维的,并且有2201个元素。
#输出index数组的前10个数值
array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
3)构建索引:根据重复索引生存数据框
#使用 .iloc 进行选择和索引
titanic = titanic.iloc[index,:] #根据前面生成的index数组重新构建titanic数据框,按照index数组中的顺序重新排列数据框的行。
# dataframe.iloc[行索引, 列索引]
# 行索引:可以是单个整数、整数列表、切片对象或布尔数组。用于选择 DataFrame 中的行。
# 列索引:可以是单个整数、整数列表、切片对象或布尔数组。用于选择 DataFrame 中的列。
#
titanic = titanic.drop('Freq', axis=1) #删除名为 'Freq' 的列,axis=1 表示删除列(如果不指定 axis 参数,默认为行)。
titanic.info()
输出结果:
<class 'pandas.core.frame.DataFrame'>
Index: 2201 entries, 2 to 31
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Class 2201 non-null object
1 Sex 2201 non-null object
2 Age 2201 non-null object
3 Survived 2201 non-null object
dtypes: object(4)
memory usage: 86.0+ KB
结果解读:
- 数据类型:<class ‘pandas.core.frame.DataFrame’> 表示这是一个 Pandas 的 DataFrame对象。
- 索引信息:Index: 2201 entries, 2 to 31,说明索引是一个类型为 Index的对象,总共有2201个条目(行数),索引的范围从 ‘2’ 到 ‘31’。 列信息:共有4列,分别是 ‘Class’、‘Sex’、'Age’和 ‘Survived’。
- Non-Null Count: 非空值的数量,这里所有列都有2201个非空值,表明没有缺失值。
- Dtype: 数据类型,这里所有列的数据类型都是 object,即字符串类型。
- 内存使用:总内存使用约为 86.0 KB。
4)数据框的统计特征
titanic.describe()
输出结果:
3. 考察乘客存活率
据说,泰坦尼克号执行“女士和儿童优先”的政策。
1)不同性别乘客存活率
#使用crosstab函数生成一个交叉表
pd.crosstab(titanic.Sex, titanic.Survived)
# pd.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False)
# index: 用作行索引的数据或数据列表。
# columns: 用作列索引的数据或数据列表。
# values (可选): 用于填充交叉表的可选数组。如果未指定,将计算频数(即交叉表中的计数)。 rownames, colnames (可选): 分别指定行和列的名称。aggfunc (可选): 如果提供了 values,则用于聚合值的函数,默认是 np.sum。可以是任何接受数组,并返回一个聚合结果的函数。margins (可选): 添加行和列边际(总计)。 margins_name (可选): 边际列的名称(默认为 'All')。 dropna (可选): 默认情况下,忽略空值。如果设置为 False,则包括 NaN 值。 normalize (可选): 如果设置为 True,则将值归一化为行或列的百分比。
输出结果:
结果显示是的存活人数。
计算存活率
pd.crosstab(titanic.Sex,
titanic.Survived,
normalize='index') #normalize='index' 进行“行标准化”
输出结果:
2)不同年龄 乘客存活率
pd.crosstab(titanic.Age,
titanic.Survived,
normalize='index')
输出结果:
3)不同仓位 乘客存活率
pd.crosstab(titanic.Class,
titanic.Survived,
normalize='index')
输出结果:
二、进行逻辑回归(logistic regression)
1. 样本分为训练集 和 测试集
from sklearn.model_selection import train_test_split
train, test = train_test_split(titanic,test_size=0.3,
stratify=titanic.Survived,
random_state=0)
#stratify=titanic.Survived: 使用乘客的生存情况(Survived列)作为分层依据。
#使用分层抽样,可保证每个类别在训练集 和 测试集的比重相同。简答的随机抽样无法保证。→ 一般建议,根据响应变量的取值进行分层抽样。
2. 设置虚拟变量(用dmatrices)
from patsy import dmatrices #从pasty模块导入damatices(),design matrices,根据公式生成响应的数据矩阵。
y_train, X_train = dmatrices('Survived ~ Class + Sex + Age',
data = train,
return_type = 'dataframe')
# dmatrices() 函数的作用是根据公式生成响应的设计矩阵。在这里,它被用来生成因变量 y_train 和自变量 X_train。
#'Survived ~ Class + Sex + Age':指定了因变量 Survived 和自变量 Class、Sex、Age 。~ 符号在公式中分隔因变量和自变量。
# data=train:指定了数据源,即从 train 数据集中提取变量。
# return_type='dataframe':指定返回的数据类型为Pandas数据框(DataFrame)
pd.options.display.max_columns = 10 #设置了 Pandas 的显示选项 max_columns 的值为 10。
X_train.head() #默认情况下,head() 函数会显示 DataFrame 的前五行,如果不指定参数。
输出结果:
结果显示,已生成虚拟变量。
其中,“T.Male”中的“T”表示“Treatment”
“Intercept”为 增加的取值均值为1的截距项(排在第一列)。
生成测试集
#生成测试集
y_test, X_test = dmatrices('Survived ~ Class + Sex + Age',
data = test,
return_type = 'dataframe')
3. 处理响应变量(或因变量)
#查看 响应变量y
y_train.head()
结果显示,y_train包含两个虚拟变量:Survived[No] 和 Survived[Yes]。
由于只需要Survived[Yes],所以需要对y_train处理。
#只保留需要的“存活”:Survived[Yes]
y_train = y_train.iloc[:,1]
#第一个冒号表示选择所有的行(因为没有指定具体的行范围),第二个参数 1 表示选择第二列。
y_test = y_test.iloc[:, 1]
4. 进行拟合
import statsmodels.api as sm
model = sm.Logit(y_train, X_train)
results = model.fit()
输出结果:
Optimization terminated successfully. #模型的优化过程顺利完成,找到了收敛的解。
Current function value: 0.501935 #模型优化过程中达到的当前损失函数值(或者说似然函数的负对数值)
Iterations 6 #模型优化过程中进行的迭代次数为 6 次。
#模型经过了6次迭代优化后,成功找到了一个参数设置,使得损失函数(似然函数的负对数值)达到了较低的值,表明模型对训练数据的拟合效果良好。
1)查看回归系数
results.params
输出结果:
Intercept 2.023452
Class[T.2nd] -1.045877
Class[T.3rd] -1.869105
Class[T.Crew] -0.882650
Sex[T.Male] -2.355785
Age[T.Child] 1.126943
dtype: float64
2)查看回归系数的 odds ratio
np.exp(results.params)
输出结果:
Intercept 7.564391
Class[T.2nd] 0.351384
Class[T.3rd] 0.154262
Class[T.Crew] 0.413685 #当船舱等级为船员时,预测值的倍数为 0.41。即船员相较于基础等级(一等舱)的乘客预测值更低。
Sex[T.Male] 0.094819 #当性别为男性(Sex[T.Male]=1)时,预测值的倍数为 0.09。即男性相较于女性(Sex[T.Male]=0)的预测值更低。
Age[T.Child] 3.086208 #当年龄为儿童(Age[T.Child]=1)时,预测值的倍数为 3.09。即儿童相较于成人(Age[T.Child]=0)的预测值更高。
dtype: float64
3)查看 回归的汇总信息
results.summary()
输出结果:
- Pseudo R-squared: 拟合优度的伪R平方值为 0.2019,用于表示模型对数据的拟合程度。
- 协方差类型(Covariance Type)指定了用于计算模型参数(例如回归系数)标准误差的方法。
- 常见的协方差类型包括:
- Nonrobust: 非鲁棒估计。这种方法假设模型的误差项满足正态分布,但不考虑其他可能的分布特性或者数据中的异方差性质。
- Robust (Huber-White): 鲁棒估计(Huber-White估计)。这种方法通常用于处理数据中可能存在的异方差(heteroscedasticity)或者其他不满足标准假设的情况。它提供了对参数估计标准误差的更鲁棒的估计,减少了因数据违反假设而引起的偏差。
- 常见的协方差类型包括:
4)计算 变量的平均边际效应(AME)
margeff = results.get_margeff()
margeff.summary()
输出结果:
Method: dydx
表示边际效应的计算方法为对自变量的变化(dummy变量的变化或二分类变量的变化)求导数,即“对因变量的导数”。这种计算方法适用于虚拟变量(哑变量)的情况,它计算了当虚拟变量从0变为1时,因变量(生存概率)的变化量。这对于理解不同类别之间的差异以及各个因素对于结果变量的影响很有帮助。At: overall
指定了边际效应的计算条件,通常是整体平均值或者整体预测概率。- 具体解释每个变量的边际效应
- Class[T.2nd] 边际效应:-0.1708
解释:这是舱位的虚拟变量,表示第二等舱相较于第一等舱对生存率的影响。边际效应为负数(-0.1708),意味着相较于第一等舱的乘客,第二等舱的乘客生存的概率更低。 - Class[T.3rd] 边际效应:-0.3053
解释:这是舱位的虚拟变量,表示第三等舱相较于第一等舱对生存率的影响。边际效应为负数(-0.3053),意味着相较于第一等舱的乘客,第三等舱的乘客生存的概率更低。 - Class[T.Crew] 边际效应:-0.1442
解释:这是舱位的虚拟变量,表示船员相较于第一等舱对生存率的影响。边际效应为负数(-0.1442),意味着船员相对于第一等舱的乘客生存的概率更低。 - Sex[T.Male] 边际效应:-0.3848
解释:这是性别的虚拟变量,表示男性相较于女性对生存率的影响。边际效应为负数(-0.3848),意味着男性相对于女性的乘客生存的概率更低。 - Age[T.Child] 边际效应:0.1841
解释:这是年龄的虚拟变量,表示儿童相较于成人对生存率的影响。边际效应为正数(0.1841),意味着儿童相对于成人的乘客生存的概率更高。
- Class[T.2nd] 边际效应:-0.1708
三、进行 预测
1. 考虑 训练集 误差
1)训练集 混淆矩阵
#对训练集 直接pred_table() 获得混淆矩阵
table = results.pred_table()
table
输出结果:
array([[949., 94.],
[252., 245.]])
- 第一行:实际类别为负例(Negative Class)的情况。
- 第一列:预测为负例的数量(真负例,True Negative,TN)为 949。
- 第二列:预测为正例的数量(假正例,False Positive,FP)为 94。
- 第二行:实际类别为正例(Positive Class)的情况。
- 第一列:预测为负例的数量(假负例,False Negative,FN)为 252。
- 第二列:预测为正例的数量(真正例,True Positive,TP)为 245。
2) 计算 “准确率”、“错分率”、 “灵敏度”、“特异度”、“召回度”
'''书上的代码'''
#准确率 Accuracy :准确率计算公式为: Accuracy=(TN+TP)/Total
Accuracy = (table[0,0]+table[1,1])/np.sum(table)
# TN是真负例的数量(table[0,0])。
# TP是真正例的数量(table[1,1])。
# np.sum(table) 是混淆矩阵中所有元素的总和,即总样本数。
Accuracy
#错分率 Error rate:Error Rate=1−Accuracy(模型的准确率)
Error_rate = 1- Accuracy
Error_rate
#灵敏度 Sensitivity 或真正例率(True Positive Rate,TPR):Sensitivity=TP/(TP+FN)
Sensitivity = table[1,1]/(table[1,0]+table[1,1])
#FN 是假负例的数量(混淆矩阵中的 table[1,0])。
Sensitivity
#特异度(Specificity)或真负例率(True Negative Rate,TNR):Specificity=TN/(TN+FP)
Specificity = table[0,0]/(table[0,0]+table[0,1])
Specificity
#召回率(Recall)或真正例率:TP/(TP+FP)
Recall = table[1,1]/(table[0,1]+table[1,1])
Recall
输出结果:
Accuracy
0.7753246753246753 (这表示模型正确预测了约 77.53% 的样本。)
Error_rate
0.2246753246753247 (模型错误预测了约 22.47% 的样本。)
Sensitivity
0.49295774647887325
Specificity
0.909875359539789
Recall
0.7227138643067846
'''用sklearn.metrics中的函数'''
from sklearn.metrics import confusion_matrix
# 从混淆矩阵中提取各项指标
TN = cm[0, 0] # 真负例
FP = cm[0, 1] # 假正例
FN = cm[1, 0] # 假负例
TP = cm[1, 1] # 真正例
# 计算灵敏度(Recall)、特异度(Specificity)和召回率(Recall)
sensitivity = TP / (TP + FN)
specificity = TN / (TN + FP)
recall = TP / (FP + TP)
print(f"Sensitivity (Recall): {sensitivity}")
print(f"Specificity: {specificity}")
print(f"Recall: {recall}")
2. 用 测试集 进行 预测
1) 计算 【测试集】 的混淆矩阵
'''书上的代码'''
#用predict()生成预测概率
prob =results.predict(X_test)
#results是你训练集的模型,使用 X_test 数据集进行预测,
#以0.5作为门槛,预测 测试集个体是否存活
pred = (prob >0.5)
#根据设定的阈值(0.5),将每个样本的预测概率转换为二元分类预测结果(True 或 False),即样本是否被预测为生存。
#用crosstab()计算 【测试集】 的混淆矩阵
table = pd.crosstab(y_test, pred, colnames = ['Predicted'])
#其中 y_test 是真实的标签,pred 是模型预测的结果。colnames=['Predicted'] 用于设置预测结果的列名。
table
输出结果:
'''用sklearn.metrics中的函数生成测试集的混淆矩阵'''
from sklearn.metrics import confusion_matrix
prob =results.predict(X_test)
pred = (prob > 0.5)
# 使用 confusion_matrix 函数计算混淆矩阵
cm = confusion_matrix(y_test, pred)
cm
输出结果:
array([[415, 32],
[110, 104]], dtype=int64)
2) 计算 “准确率”、“错分率”、 “灵敏度”、“特异度”、“召回度”
'''书上的代码'''
# np.array() 转换数组
table = np.array(table )
Accuracy = (table[0,0]+table[1,1])/np.sum(table)
Accuracy
Error_rate = 1- Accuracy
Error_rate
Sensitivity = table[1,1]/(table[1,0]+table[1,1])
Sensitivity
Specificity = table[0,0]/(table[0,0]+table[0,1])
Specificity
Recall = table[1,1]/(table[0,1]+table[1,1])
Recall
输出结果:
Accuracy
0.7851739788199698
Error_rate
0.21482602118003025
Sensitivity
0.48598130841121495
Specificity
0.9284116331096197
Recall
0.7647058823529411
'''用sklearn.metrics中的函数'''
from sklearn.metrics import confusion_matrix
# 从混淆矩阵中提取各项指标
TN = table[0, 0]
FP = table[0, 1]
FN = table[1, 0]
TP = table[1, 1]
# 计算各项指标
accuracy = (TP + TN) / (TP + TN + FP + FN) # 准确率
Error_rate = (FP + FN) / (TP + TN + FP + FN) # 错分率
sensitivity = TP / (TP + FN) # 灵敏度
specificity = TN / (TN + FP) # 特异度
recall = TP / (FP + TP) #召回率
print(f"准确率 (Accuracy): {accuracy}")
print(f"错分率 (Error_rate): {Error_rate }")
print(f"灵敏度 (Sensitivity): {sensitivity}")
print(f"特异度 (Specificity): {sensitivity}")
print(f"召回度 (Recall): {recall}")
3. 画 ROC图 并计算AUC
【注意:sklearn1.5.0版本之后,plot_roc_curve 用不了】
可以参考网友特斯拉学生菜斯拉的plot_roc_curve引用不上的问题用 RocCurveDisplay
(参考scikit-learn 1.5.1 稳定版 使用手册 例子)
我用的是matplotlib.pyplot
from sklearn.metrics import roc_curve, roc_auc_score
#预测 测试集中的概率
y_pred_proba = results.predict(sm.add_constant(X_test))
# 计算ROC曲线的真正率(True Positive Rate, TPR/sensitivity)和假正率(False Positive Rate, FPR)
fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
# 计算AUC
roc_auc = auc(fpr, tpr)
print(f"AUC: {roc_auc:.4f}")
# 画出ROC
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='blue', lw=2, label=f'ROC Curve (AUC = {roc_auc:.4f})')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--') #绘制了一条对角线,用于表示随机猜测的基准线。[0, 1] 是 X 轴坐标,表示从 0 到 1。[0, 1] 是 Y 轴坐标,表示从 0 到 1。linestyle='--' 设置线条为虚线。
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate (FPR)')
plt.ylabel('True Positive Rate (TPR)')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.legend(loc="lower right")
plt.show()
输出结果:
4. 计算kappa
# 将概率转换为二进制预测(设阈值为0.5)
y_pred = (y_pred_proba > 0.5).astype(int)
#(y_pred_pr oba > 0.5): 通过比较每个预测概率是否大于 0.5 来生成一个布尔数组。如果预测概率大于 0.5,则认为预测结果为正类(通常表示为 1),否则为负类(表示为 0)。
# .astype(int): 将布尔值(True/False)转换为整数(1/0),以便用于后续的统计计算。
# 计算Kappa
from sklearn.metrics import cohen_kappa_score
kappa = cohen_kappa_score(y_test, y_pred)
#使用y_test(实际标签)和 y_pred(预测标签)计算 Cohen's Kappa 系数。这个系数量化了观察者之间的一致性,也可以解释为模型预测和实际标签之间的一致性。值范围从 -1 到 1,其中 1 表示完全一致,0 表示完全随机,负值表示一致性低于随机水平。
print(f"Cohen's Kappa: {kappa}")
输出结果:
Cohen’s Kappa: 0.4578900555613312 #中等一致性