特征工程练手(四):特征选择

news2024/12/28 4:42:13

本文为和鲸python 特征工程入门与实践·闯关训练营资料整理而来,加入了自己的理解(by GPT4o)

原活动链接

原作者:云中君,大厂后端研发工程师

目录

  • 0、关卡总结
  • 1、前言
  • 2、基础知识讲解
    • 2.1get_best_model_and_accuracy
    • 2.2创建基准机器学习流水线
    • 2.3特征选择的类型
      • 基于统计的特征选择
      • 使用假设检验
      • P值
    • 2.4基于模型的特征选择
      • 自然语言处理
      • 特征选择指标——基于树的模型
      • 线性模型和正则化
  • 3、闯关题
    • STEP1:根据要求完成题目

0、关卡总结

在本节,我们深入探讨了选择特征的多种方法,包括使用相关系数、机器学习模型等方式来优化特征集。以下是本节的主要总结:

相关系数:通过计算特征之间的相关系数,可以了解它们之间的线性关系。相关系数越高,表示两个特征之间的线性关系越强,可以用于判断特征之间的相关性。
理解 p 值:p 值是统计检验中的一个重要指标,表示观察到的结果在零假设成立时发生的概率。通常,当 p 值小于显著性水平(通常设为 0.05)时,我们会拒绝零假设。
用机器学习测量熵和信息增益:在决策树等模型中,可以使用信息熵和信息增益来评估特征的重要性。信息增益表示特征对模型的贡献程度。

下一步展望:
学习如何在数学矩阵中使用转换,减少特征的处理。
探讨降维技术,如主成分分析(PCA),以保留数据集的主要信息并减少维度。

通过本节的学习,你已经了解了在特征选择方面的多种策略,为在实际项目中优化特征集提供了有力的工具。在进入下一关之前,确保对这些方法有清晰的理解,并准备好学习如何在数学矩阵中进行转换,以及如何处理高维数据。祝你在学习的过程中取得更大的成功!

1、前言

特征选择是从原始数据中选择对于模型预测效果最好的特征的过程。特征选择方法分为两大类:基于统计的特征选择,以及基于模型的特征选择。

2、基础知识讲解

分类任务:

  • 真阳性率和假阳性率
  • 灵敏度(真阳性率)和特异性
  • 假阴性率和假阳性率

回归任务:

  • 平均绝对误差
  • R2

元指标是指不直接与模型预测性能相关的指标,它们试图衡量周遭的性能

  • 模型拟合/训练所需的时间
  • 拟合后的模型预测新实例的时间
  • 需要持久化(永久保存)的数据大小

2.1get_best_model_and_accuracy

定义get_best_model_and_accuracy函数:

  • 搜索所有给定的参数,优化机器学习流水线
  • 输出有助于评估流水线质量的指标。
# 导入网格搜索模块
from sklearn.model_selection import GridSearchCV


def get_best_model_and_accuracy(model, params, X, y):
    grid = GridSearchCV(model, # 要搜索的模型
                        params, # 要尝试的参数
                        error_score=0.) # 如果报错,结果是0
    grid.fit(X, y) # 拟合模型和参数
    # 经典的性能指标
    print("Best Accuracy: {}".format(grid.best_score_))
    # 得到最佳准确率的最佳参数
    print("Best Parameters: {}".format(grid.best_params_))
    # 拟合的平均时间(秒)
    print("Average Time to Fit (s): {}".format(round(grid.cv_results_['mean_fit_time'].mean(), 3)))
    # 预测的平均时间(秒)
    # 从该指标可以看出模型在真实世界的性能
    print("Average Time to Score (s): {}".format(round(grid.cv_results_['mean_score_time'].mean(), 3)))

对信用卡逾期数据集进行探索性数据分析

import pandas as pd
import numpy as np

# 用随机数种子保证随机数永远一致
np.random.seed(123)

path = './data/credit_card_default.csv'
# 导入数据集
credit_card_default = pd.read_csv(path)
# 描述性统计
# 调用.T方法进行转置,以便更好地观察
credit_card_default.describe().T
countmeanstdmin25%50%75%max
LIMIT_BAL30000.0167484.322667129747.66156710000.050000.00140000.0240000.001000000.0
SEX30000.01.6037330.4891291.01.002.02.002.0
EDUCATION30000.01.8531330.7903490.01.002.02.006.0
MARRIAGE30000.01.5518670.5219700.01.002.02.003.0
AGE30000.035.4855009.21790421.028.0034.041.0079.0
PAY_030000.0-0.0167001.123802-2.0-1.000.00.008.0
PAY_230000.0-0.1337671.197186-2.0-1.000.00.008.0
PAY_330000.0-0.1662001.196868-2.0-1.000.00.008.0
PAY_430000.0-0.2206671.169139-2.0-1.000.00.008.0
PAY_530000.0-0.2662001.133187-2.0-1.000.00.008.0
PAY_630000.0-0.2911001.149988-2.0-1.000.00.008.0
BILL_AMT130000.051223.33090073635.860576-165580.03558.7522381.567091.00964511.0
BILL_AMT230000.049179.07516771173.768783-69777.02984.7521200.064006.25983931.0
BILL_AMT330000.047013.15480069349.387427-157264.02666.2520088.560164.751664089.0
BILL_AMT430000.043262.94896764332.856134-170000.02326.7519052.054506.00891586.0
BILL_AMT530000.040311.40096760797.155770-81334.01763.0018104.550190.50927171.0
BILL_AMT630000.038871.76040059554.107537-339603.01256.0017071.049198.25961664.0
PAY_AMT130000.05663.58050016563.2803540.01000.002100.05006.00873552.0
PAY_AMT230000.05921.16350023040.8704020.0833.002009.05000.001684259.0
PAY_AMT330000.05225.68150017606.9614700.0390.001800.04505.00896040.0
PAY_AMT430000.04826.07686715666.1597440.0296.001500.04013.25621000.0
PAY_AMT530000.04799.38763315278.3056790.0252.501500.04031.50426529.0
PAY_AMT630000.05215.50256717777.4657750.0117.751500.04000.00528666.0
default payment next month30000.00.2212000.4150620.00.000.00.001.0
# 检查缺失值,发现无缺失
credit_card_default.isnull().sum()
LIMIT_BAL                     0
SEX                           0
EDUCATION                     0
MARRIAGE                      0
AGE                           0
PAY_0                         0
PAY_2                         0
PAY_3                         0
PAY_4                         0
PAY_5                         0
PAY_6                         0
BILL_AMT1                     0
BILL_AMT2                     0
BILL_AMT3                     0
BILL_AMT4                     0
BILL_AMT5                     0
BILL_AMT6                     0
PAY_AMT1                      0
PAY_AMT2                      0
PAY_AMT3                      0
PAY_AMT4                      0
PAY_AMT5                      0
PAY_AMT6                      0
default payment next month    0
dtype: int64
# 30 000行,24列
credit_card_default.shape
(30000, 24)
credit_card_default.head()
LIMIT_BALSEXEDUCATIONMARRIAGEAGEPAY_0PAY_2PAY_3PAY_4PAY_5...BILL_AMT4BILL_AMT5BILL_AMT6PAY_AMT1PAY_AMT2PAY_AMT3PAY_AMT4PAY_AMT5PAY_AMT6default payment next month
0200002212422-1-1-2...000068900001
112000022226-12000...3272345532610100010001000020001
2900002223400000...1433114948155491518150010001000100050000
3500002213700000...2831428959295472000201912001100106910000
45000012157-10-100...2094019146191312000366811000090006896790

5 rows × 24 columns

# 特征
X = credit_card_default.drop('default payment next month', axis=1)

# label
y = credit_card_default['default payment next month']
# 取空准确率
y.value_counts(normalize=True)
0    0.7788
1    0.2212
Name: default payment next month, dtype: float64

2.2创建基准机器学习流水线

# 导入4种模型
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

import warnings
warnings.filterwarnings("ignore")

# 为网格搜索设置变量
# 先设置机器学习模型的参数

# 逻辑回归
lr_params = {'C':[1e-1, 1e0, 1e1, 1e2], 'penalty':['l1', 'l2']}

# KNN
knn_params = {'n_neighbors': [1, 3, 5, 7]}

# 决策树
tree_params = {'max_depth':[None, 1, 3, 5, 7]}

# 随机森林
forest_params = {'n_estimators': [10, 50, 100], 
                 'max_depth': [None, 1, 3, 5, 7]}
                 
                 
# 实例化机器学习模型
lr = LogisticRegression()
knn = KNeighborsClassifier()
d_tree = DecisionTreeClassifier()
forest = RandomForestClassifier()          
get_best_model_and_accuracy(lr, lr_params, X, y)
Best Accuracy: 0.7788333333333334
Best Parameters: {'C': 0.1, 'penalty': 'l2'}
Average Time to Fit (s): 0.062
Average Time to Score (s): 0.001
get_best_model_and_accuracy(knn, knn_params, X, y)
Best Accuracy: 0.7615333333333333
Best Parameters: {'n_neighbors': 7}
Average Time to Fit (s): 0.007
Average Time to Score (s): 2.777

发现KNN的准确率不如空准确率0.7788。
KNN是基于距离的模型,使用空间的紧密度衡量,假定所有的特征尺度相同,但是数据可能并不是这样,因此对于KNN,我们需要更复杂的流水线,以更准确地评估基准性能。

# 导入所需的包
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# 为流水线设置KNN参数
knn_pipe_params = {'classifier__{}'.format(k): v for k, v in knn_params.items()}

# KNN 需要标准化的参数
knn_pipe = Pipeline([('scale', StandardScaler()), ('classifier', knn)])

# 拟合快,预测慢
get_best_model_and_accuracy(knn_pipe, knn_pipe_params, X, y)

print(knn_pipe_params)  # {'classifier__n_neighbors': [1, 3, 5, 7]} 
Best Accuracy: 0.0
Best Parameters: {'classifier__n_neighbors': 1}
Average Time to Fit (s): 0.02
Average Time to Score (s): 0.012
{'classifier__n_neighbors': [1, 3, 5, 7]}

关键代码说明:这段代码旨在通过网格搜索(GridSearchCV)来找到给定模型(在这里是KNN分类器)在不同参数组合下的最佳性能,并输出模型的准确率、最佳参数和时间消耗。以下是代码的详细解析:

1. 导入必要的库

from sklearn.model_selection import GridSearchCV  
from sklearn.pipeline import Pipeline  
from sklearn.preprocessing import StandardScaler  
  • GridSearchCV:用于进行网格搜索,通过穷举不同参数组合,找到在交叉验证中表现最好的模型。
  • Pipeline:用于创建机器学习流水线,可以将多个步骤(例如预处理、建模)串联起来,便于管理和优化。
  • StandardScaler:标准化工具,用于对特征进行标准化处理(均值为0,方差为1),对一些敏感于特征缩放的模型(如KNN)很有帮助。

2. get_best_model_and_accuracy 函数

def get_best_model_and_accuracy(model, params, X, y):  
    grid = GridSearchCV(model, # 要搜索的模型  
                        params, # 要尝试的参数  
                        error_score=0.) # 如果报错,结果是0  
    grid.fit(X, y) # 拟合模型和参数  
  • 函数输入

    • model:需要调优的模型或流水线。
    • params:模型参数的字典,这里将用于网格搜索。
    • Xy:训练数据的特征和标签。
  • GridSearchCV参数解释

    • model:需要进行参数搜索的模型或流水线。
    • params:需要优化的参数字典。
    • error_score=0.:如果某些参数组合导致错误,会返回0作为分数,以避免程序中断。

3. 输出网格搜索结果

    print("Best Accuracy: {}".format(grid.best_score_))  
    print("Best Parameters: {}".format(grid.best_params_))  
    print("Average Time to Fit (s): {}".format(round(grid.cv_results_['mean_fit_time'].mean(), 3)))  
    print("Average Time to Score (s): {}".format(round(grid.cv_results_['mean_score_time'].mean(), 3)))  
  • grid.best_score_:最佳参数组合下的准确率。
  • grid.best_params_:最佳的参数组合。
  • grid.cv_results_['mean_fit_time']:模型拟合的平均时间。
  • grid.cv_results_['mean_score_time']:模型预测的平均时间,反映了模型在实际使用中的性能。

4. 构建KNN流水线和参数

knn_pipe_params = {'classifier__{}'.format(k): v for k, v in knn_params.items()}  
knn_pipe = Pipeline([('scale', StandardScaler()), ('classifier', knn)])  
  • knn_pipe_params:通过字典推导式,将KNN模型的参数前面加上前缀 'classifier__',这是为了适应流水线中的参数命名方式。
  • knn_pipe:构建了一个包含两个步骤的流水线,第一步是标准化特征,第二步是应用KNN分类器。

5. 调用函数进行网格搜索

get_best_model_and_accuracy(knn_pipe, knn_pipe_params, X, y)  
  • 使用之前定义的函数对KNN流水线和参数进行网格搜索,输出最佳模型的相关信息。

6. 打印参数字典

print(knn_pipe_params)  # {'classifier__n_neighbors': [1, 3, 5, 7]}  
  • 打印出包含参数组合的字典(示例中是KNN的n_neighbors参数)。

**总结
这段代码的主要目的在于:

  1. 通过网格搜索优化KNN分类器的参数。
  2. 使用流水线进行数据预处理和模型训练,使整个流程更加清晰易管理。
  3. 输出最佳参数组合和一些性能指标,以帮助用户评估模型表现。

代码中的流程可以很方便地扩展到其他模型和参数上,只需修改模型和参数的定义即可。

用StandardScalar进行z分数标准化处理后,这个流水线的准确率至少比空准确率要高,但是这也严重影响了预测时间,因为多了一个预处理步骤。目前,逻辑回归依然领先:准确率更高,速度更快

# 决策树的准确率是第一,拟合速度比逻辑回归快,预测速度比KNN快
get_best_model_and_accuracy(d_tree, tree_params, X, y)
Best Accuracy: 0.8206333333333333
Best Parameters: {'max_depth': 3}
Average Time to Fit (s): 0.353
Average Time to Score (s): 0.002
get_best_model_and_accuracy(forest, forest_params, X, y)
Best Accuracy: 0.8203000000000001
Best Parameters: {'max_depth': 7, 'n_estimators': 50}
Average Time to Fit (s): 2.731
Average Time to Score (s): 0.027

2.3特征选择的类型

基于统计的特征选择很大程度上依赖于机器学习模型之外的统计测试,以便在流水线的训练阶段选择特征。
基于模型的特征选择则依赖于一个预处理步骤,需要训练一个辅助的机器学习模型,并利用其预测能力来选择特征。

基于统计的特征选择

  • 皮尔逊相关系数(Pearson correlations)
  • 假设检验。这两个方法都是单变量方法
    意思是,如果为了提高机器学习流水线性能而每次选择单一特征以创建更好的数据集,这种方法最简便。
# 相关系数
credit_card_default.corr()
LIMIT_BALSEXEDUCATIONMARRIAGEAGEPAY_0PAY_2PAY_3PAY_4PAY_5...BILL_AMT4BILL_AMT5BILL_AMT6PAY_AMT1PAY_AMT2PAY_AMT3PAY_AMT4PAY_AMT5PAY_AMT6default payment next month
LIMIT_BAL1.0000000.024755-0.219161-0.1081390.144713-0.271214-0.296382-0.286123-0.267460-0.249411...0.2939880.2955620.2903890.1952360.1784080.2101670.2032420.2172020.219595-0.153520
SEX0.0247551.0000000.014232-0.031389-0.090874-0.057643-0.070771-0.066096-0.060173-0.055064...-0.021880-0.017005-0.016733-0.000242-0.001391-0.008597-0.002229-0.001667-0.002766-0.039961
EDUCATION-0.2191610.0142321.000000-0.1434640.1750610.1053640.1215660.1140250.1087930.097520...-0.000451-0.007567-0.009099-0.037456-0.030038-0.039943-0.038218-0.040358-0.0372000.028006
MARRIAGE-0.108139-0.031389-0.1434641.000000-0.4141700.0199170.0241990.0326880.0331220.035629...-0.023344-0.025393-0.021207-0.005979-0.008093-0.003541-0.012659-0.001205-0.006641-0.024339
AGE0.144713-0.0908740.175061-0.4141701.000000-0.039447-0.050148-0.053048-0.049722-0.053826...0.0513530.0493450.0476130.0261470.0217850.0292470.0213790.0228500.0194780.013890
PAY_0-0.271214-0.0576430.1053640.019917-0.0394471.0000000.6721640.5742450.5388410.509426...0.1791250.1806350.176980-0.079269-0.070101-0.070561-0.064005-0.058190-0.0586730.324794
PAY_2-0.296382-0.0707710.1215660.024199-0.0501480.6721641.0000000.7665520.6620670.622780...0.2222370.2213480.219403-0.080701-0.058990-0.055901-0.046858-0.037093-0.0365000.263551
PAY_3-0.286123-0.0660960.1140250.032688-0.0530480.5742450.7665521.0000000.7773590.686775...0.2272020.2251450.2223270.001295-0.066793-0.053311-0.046067-0.035863-0.0358610.235253
PAY_4-0.267460-0.0601730.1087930.033122-0.0497220.5388410.6620670.7773591.0000000.819835...0.2459170.2429020.239154-0.009362-0.001944-0.069235-0.043461-0.033590-0.0265650.216614
PAY_5-0.249411-0.0550640.0975200.035629-0.0538260.5094260.6227800.6867750.8198351.000000...0.2719150.2697830.262509-0.006089-0.0031910.009062-0.058299-0.033337-0.0230270.204149
PAY_6-0.235195-0.0440080.0823160.034345-0.0487730.4745530.5755010.6326840.7164490.816900...0.2663560.2908940.285091-0.001496-0.0052230.0058340.019018-0.046434-0.0252990.186866
BILL_AMT10.285430-0.0336420.023581-0.0234720.0562390.1870680.2348870.2084730.2028120.206684...0.8602720.8297790.8026500.1402770.0993550.1568870.1583030.1670260.179341-0.019644
BILL_AMT20.278314-0.0311830.018749-0.0216020.0542830.1898590.2352570.2372950.2258160.226913...0.8924820.8597780.8315940.2803650.1008510.1507180.1473980.1579570.174256-0.014193
BILL_AMT30.283236-0.0245630.013002-0.0249090.0537100.1797850.2241460.2274940.2449830.243335...0.9239690.8839100.8533200.2443350.3169360.1300110.1434050.1797120.182326-0.014076
BILL_AMT40.293988-0.021880-0.000451-0.0233440.0513530.1791250.2222370.2272020.2459170.271915...1.0000000.9401340.9009410.2330120.2075640.3000230.1301910.1604330.177637-0.010156
BILL_AMT50.295562-0.017005-0.007567-0.0253930.0493450.1806350.2213480.2251450.2429020.269783...0.9401341.0000000.9461970.2170310.1812460.2523050.2931180.1415740.164184-0.006760
BILL_AMT60.290389-0.016733-0.009099-0.0212070.0476130.1769800.2194030.2223270.2391540.262509...0.9009410.9461971.0000000.1999650.1726630.2337700.2502370.3077290.115494-0.005372
PAY_AMT10.195236-0.000242-0.037456-0.0059790.026147-0.079269-0.0807010.001295-0.009362-0.006089...0.2330120.2170310.1999651.0000000.2855760.2521910.1995580.1484590.185735-0.072929
PAY_AMT20.178408-0.001391-0.030038-0.0080930.021785-0.070101-0.058990-0.066793-0.001944-0.003191...0.2075640.1812460.1726630.2855761.0000000.2447700.1801070.1809080.157634-0.058579
PAY_AMT30.210167-0.008597-0.039943-0.0035410.029247-0.070561-0.055901-0.053311-0.0692350.009062...0.3000230.2523050.2337700.2521910.2447701.0000000.2163250.1592140.162740-0.056250
PAY_AMT40.203242-0.002229-0.038218-0.0126590.021379-0.064005-0.046858-0.046067-0.043461-0.058299...0.1301910.2931180.2502370.1995580.1801070.2163251.0000000.1518300.157834-0.056827
PAY_AMT50.217202-0.001667-0.040358-0.0012050.022850-0.058190-0.037093-0.035863-0.033590-0.033337...0.1604330.1415740.3077290.1484590.1809080.1592140.1518301.0000000.154896-0.055124
PAY_AMT60.219595-0.002766-0.037200-0.0066410.019478-0.058673-0.036500-0.035861-0.026565-0.023027...0.1776370.1641840.1154940.1857350.1576340.1627400.1578340.1548961.000000-0.053183
default payment next month-0.153520-0.0399610.028006-0.0243390.0138900.3247940.2635510.2352530.2166140.204149...-0.010156-0.006760-0.005372-0.072929-0.058579-0.056250-0.056827-0.055124-0.0531831.000000

24 rows × 24 columns

# 用Seaborn生成热图
import seaborn as sns
import matplotlib.style as style
%matplotlib inline

# 选用一个干净的主题
style.use('fivethirtyeight')

sns.heatmap(credit_card_default.corr())
<AxesSubplot:>

在这里插入图片描述

用相关系数确定特征交互和冗余变量,发现并删除这些冗余变量是减少机器学习过拟合问题的一个关键方法

# 只有特征和label的相关性
credit_card_default.corr()['default payment next month'] 
LIMIT_BAL                    -0.153520
SEX                          -0.039961
EDUCATION                     0.028006
MARRIAGE                     -0.024339
AGE                           0.013890
PAY_0                         0.324794
PAY_2                         0.263551
PAY_3                         0.235253
PAY_4                         0.216614
PAY_5                         0.204149
PAY_6                         0.186866
BILL_AMT1                    -0.019644
BILL_AMT2                    -0.014193
BILL_AMT3                    -0.014076
BILL_AMT4                    -0.010156
BILL_AMT5                    -0.006760
BILL_AMT6                    -0.005372
PAY_AMT1                     -0.072929
PAY_AMT2                     -0.058579
PAY_AMT3                     -0.056250
PAY_AMT4                     -0.056827
PAY_AMT5                     -0.055124
PAY_AMT6                     -0.053183
default payment next month    1.000000
Name: default payment next month, dtype: float64
# 只留下相关系数超过正负0.2的特征
credit_card_default.corr()['default payment next month'].abs() > .2
LIMIT_BAL                     False
SEX                           False
EDUCATION                     False
MARRIAGE                      False
AGE                           False
PAY_0                          True
PAY_2                          True
PAY_3                          True
PAY_4                          True
PAY_5                          True
PAY_6                         False
BILL_AMT1                     False
BILL_AMT2                     False
BILL_AMT3                     False
BILL_AMT4                     False
BILL_AMT5                     False
BILL_AMT6                     False
PAY_AMT1                      False
PAY_AMT2                      False
PAY_AMT3                      False
PAY_AMT4                      False
PAY_AMT5                      False
PAY_AMT6                      False
default payment next month     True
Name: default payment next month, dtype: bool
# 存储特征
mask = credit_card_default.corr()['default payment next month'].abs() > .2
highly_correlated_features = credit_card_default.columns[mask]

highly_correlated_features
Index(['PAY_0', 'PAY_2', 'PAY_3', 'PAY_4', 'PAY_5',
       'default payment next month'],
      dtype='object')
# 删掉label
highly_correlated_features = highly_correlated_features.drop('default payment next month')

highly_correlated_features
Index(['PAY_0', 'PAY_2', 'PAY_3', 'PAY_4', 'PAY_5'], dtype='object')
# 只有5个高度关联的变量
X_subsetted = X[highly_correlated_features]

get_best_model_and_accuracy(d_tree, tree_params, X_subsetted, y) 
Best Accuracy: 0.8213333333333332
Best Parameters: {'max_depth': 3}
Average Time to Fit (s): 0.009
Average Time to Score (s): 0.002

准确率比要击败的准确率0.82026略差,但是拟合时间快了大概20倍。我们的模型只需要5个特征就可以学习整个数据集,而且速度快得多。

将相关性选择作为预处理阶段的一部分,封装成CustomCorrelationChooser,实现一个拟合逻辑和一个转换逻辑

  • 拟合逻辑:从特征矩阵中选择相关性高于阈值的列
  • 转换逻辑:对数据集取子集,只包含重要的列

因为 Scikit-Learn 是依赖鸭子类型的(而不是继承),转换器类需要有三个方法:fit()(返回self),transform(),和fit_transform()。通过添加TransformerMixin作为基类,可以很容易地得到最后一个。添加BaseEstimator作为基类(且构造器中避免使用args和kargs),可以得到两个额外的方法(get_params()和set_params()),二者可以方便地进行超参数自动微调

from sklearn.base import TransformerMixin, BaseEstimator

class CustomCorrelationChooser(TransformerMixin, BaseEstimator):
    def __init__(self, response, cols_to_keep=[], threshold=None):
        # 保存响应变量
        self.response = response
        # 保存阈值
        self.threshold = threshold
        # 初始化一个变量,存放要保留的特征名
        self.cols_to_keep = cols_to_keep
        
    def transform(self, X):
        # 转换会选择合适的列
        return X[self.cols_to_keep]
        
    def fit(self, X, *_):
        # 创建新的DataFrame,存放特征和响应
        df = pd.concat([X, self.response], axis=1)
        # 保存高于阈值的列的名称
        mask = df.corr()[df.columns[-1]].abs() > self.threshold
        self.cols_to_keep = df.columns[mask]
        # 只保留X的列,去掉响应变量
        self.cols_to_keep = [c for c in self.cols_to_keep if c in X.columns]
        return self

重要代码解析:

这段代码展示了如何通过自定义转换器 CustomCorrelationChooser 将相关性选择作为机器学习流水线中的预处理步骤。其设计目的是根据特定特征与目标变量(响应变量)之间的相关性,筛选出对模型预测最重要的特征。

首先,代码中提到的核心概念包括拟合逻辑和转换逻辑:

  • 拟合逻辑:根据给定的阈值选择与响应变量相关性高的特征。
  • 转换逻辑:只保留选出的重要特征,应用于新的数据集。

1. 继承TransformerMixin和BaseEstimator

from sklearn.base import TransformerMixin, BaseEstimator  

class CustomCorrelationChooser(TransformerMixin, BaseEstimator):  
  • TransformerMixin:通过继承该类,自动获得fit_transform()方法。
  • BaseEstimator:提供get_params()set_params()方法,便于超参数调优(例如在网格搜索中使用)。

2. 初始化方法__init__

def __init__(self, response, cols_to_keep=[], threshold=None):  
    self.response = response  
    self.threshold = threshold  
    self.cols_to_keep = cols_to_keep  
  • response:目标变量(响应变量),用于计算与每个特征的相关性。
  • threshold:用于选择特征的相关性阈值。相关性大于这个值的特征将被保留。
  • cols_to_keep:初始化时存放最终选中的特征列名称。

3. fit()方法

def fit(self, X, *_):  
    df = pd.concat([X, self.response], axis=1)  
    mask = df.corr()[df.columns[-1]].abs() > self.threshold  
    self.cols_to_keep = df.columns[mask]  
    self.cols_to_keep = [c for c in self.cols_to_keep if c in X.columns]  
    return self  
  • fit()方法实现了拟合逻辑:
    • 首先,将特征矩阵 X 和响应变量 response 合并成一个新的 DataFrame。
    • 计算各特征与响应变量之间的相关性,使用 .corr() 方法获得相关矩阵。
    • 使用给定的阈值 threshold 筛选出相关性高于阈值的特征列名称。
    • 将这些特征名称存储在 self.cols_to_keep 中,同时排除响应变量列。
    • 最终,返回 self,这是 Scikit-learn 兼容性所要求的行为。

4. transform()方法

def transform(self, X):  
    return X[self.cols_to_keep]  
  • transform()方法实现了转换逻辑:
    • 直接返回只包含筛选后重要特征的子集。
    • 这个方法在实际预测或模型训练时将被调用。

5. 代码关键点和优势

  • Scikit-learn 兼容性:通过继承 TransformerMixinBaseEstimator,这个转换器可以方便地集成到 Scikit-learn 的流水线中,并且可以用于超参数调优(如网格搜索)。
  • 灵活性:可以自定义相关性阈值,适应不同任务的需求。
  • 代码结构清晰:逻辑分离明确,fittransform 方法的职责清晰,符合 Scikit-learn 转换器的设计规范。

使用示例
假设你有一个特征矩阵 X 和响应变量 y,你可以这样使用这个自定义转换器:

chooser = CustomCorrelationChooser(response=y, threshold=0.5)  
X_transformed = chooser.fit_transform(X)  

这段代码会选择与 y 相关性高于 0.5 的特征,然后返回只包含这些特征的 X_transformed

总结
CustomCorrelationChooser 提供了一种有效的方法,在机器学习管道中自动筛选与目标变量相关的特征。通过简单配置,这个自定义转换器能够轻松集成到现有的 Scikit-learn 工作流中,增强模型的效果和易用性。

# 实例化特征选择器
ccc = CustomCorrelationChooser(threshold=.2, response=y)
ccc.fit(X)

ccc.cols_to_keep
['PAY_0', 'PAY_2', 'PAY_3', 'PAY_4', 'PAY_5']
ccc.transform(X).head()
PAY_0PAY_2PAY_3PAY_4PAY_5
022-1-1-2
1-12000
200000
300000
4-10-100
# 流水线
from copy import deepcopy

# 使用响应变量初始化特征选择器
ccc = CustomCorrelationChooser(response=y)

# 创建流水线,包括选择器
ccc_pipe = Pipeline([('correlation_select', ccc), 
                     ('classifier', d_tree)])

tree_pipe_params = {'classifier__max_depth': 
                    [None, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]}

# 复制决策树的参数
ccc_pipe_params = deepcopy(tree_pipe_params)

# 更新决策树的参数选择
ccc_pipe_params.update({'correlation_select__threshold':[0, .1, .2, .3]})

print(ccc_pipe_params)
{'classifier__max_depth': [None, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21], 'correlation_select__threshold': [0, 0.1, 0.2, 0.3]}

# 比原来好一点,而且很快
get_best_model_and_accuracy(ccc_pipe, ccc_pipe_params, X, y) 
Best Accuracy: 0.8207333333333333
Best Parameters: {'classifier__max_depth': 3, 'correlation_select__threshold': 0.2}
Average Time to Fit (s): 0.188
Average Time to Score (s): 0.003
# 阈值是0.1
ccc = CustomCorrelationChooser(threshold=0.1, response=y)
ccc.fit(X)

# 选择器保留了我们找到的5列,以及LIMIT_BAL和PAY_6
ccc.cols_to_keep
['LIMIT_BAL', 'PAY_0', 'PAY_2', 'PAY_3', 'PAY_4', 'PAY_5', 'PAY_6']

使用假设检验

假设检验是一种统计学方法,可以对单个特征进行复杂的统计检验。作为一种统计检验,假设检验用于在给定数据样本时确定可否在整个数据集上应用某种条件。假设检验的结果会告诉我们是否应该相信或拒绝假设(并选择另一个假设)。基于样本数据,假设检验会确定是否应拒绝零假设。我们通常会用p值(一个上限为1的非负小数,由显著性水平决定)得出结论

# SelectKBest在给定目标函数后选择k个最高分
from sklearn.feature_selection import SelectKBest

# ANOVA测试
from sklearn.feature_selection import f_classif

# f_classif 可以使用负数 但不是所有类都支持
# chi2(卡方)也很常用,但只支持正数
# 回归分析有自己的假设检验

P值

P值,也就是常见到的 P-value。P 值是一种概率,指的是在 H0 假设为真的前提下,样本结果出现的概率。如果 P-value 很小,则说明在原假设为真的前提下,样本结果出现的概率很小,甚至很极端,这就反过来说明了原假设很大概率是错误的

p值的一个常见阈值是0.05,意思是可以认为p值小于0.05的特征是显著的

# 只保留最佳的5个特征
k_best = SelectKBest(f_classif, k=5)
# 选择最佳特征后的矩阵
k_best.fit_transform(X, y)
array([[ 2,  2, -1, -1, -2],
       [-1,  2,  0,  0,  0],
       [ 0,  0,  0,  0,  0],
       ...,
       [ 4,  3,  2, -1,  0],
       [ 1, -1,  0,  0,  0],
       [ 0,  0,  0,  0,  0]], dtype=int64)
# 取列的p值
k_best.pvalues_
array([1.30224395e-157, 4.39524880e-012, 1.22503803e-006, 2.48536389e-005,
       1.61368459e-002, 0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
       1.89929659e-315, 1.12660795e-279, 7.29674048e-234, 6.67329549e-004,
       1.39573624e-002, 1.47699827e-002, 7.85556416e-002, 2.41634443e-001,
       3.52122521e-001, 1.14648761e-036, 3.16665676e-024, 1.84177029e-022,
       6.83094160e-023, 1.24134477e-021, 3.03358907e-020])
# 特征和p值组成DataFrame
# 按p值排列
p_values = pd.DataFrame({'column': X.columns, 'p_value': k_best.pvalues_}).sort_values('p_value')

# 前5个特征
p_values.head()
columnp_value
5PAY_00.000000e+00
6PAY_20.000000e+00
7PAY_30.000000e+00
8PAY_41.899297e-315
9PAY_51.126608e-279
# 低p值的特征
p_values[p_values['p_value'] < .05]
columnp_value
5PAY_00.000000e+00
6PAY_20.000000e+00
7PAY_30.000000e+00
8PAY_41.899297e-315
9PAY_51.126608e-279
10PAY_67.296740e-234
0LIMIT_BAL1.302244e-157
17PAY_AMT11.146488e-36
18PAY_AMT23.166657e-24
20PAY_AMT46.830942e-23
19PAY_AMT31.841770e-22
21PAY_AMT51.241345e-21
22PAY_AMT63.033589e-20
1SEX4.395249e-12
2EDUCATION1.225038e-06
3MARRIAGE2.485364e-05
11BILL_AMT16.673295e-04
12BILL_AMT21.395736e-02
13BILL_AMT31.476998e-02
4AGE1.613685e-02
# 高p值的特征
p_values[p_values['p_value'] >= .05]
columnp_value
14BILL_AMT40.078556
15BILL_AMT50.241634
16BILL_AMT60.352123
# 试试SelectKBest
from copy import deepcopy

k_best = SelectKBest(f_classif)

# 用SelectKBest建立流水线
select_k_pipe = Pipeline([('k_best', k_best), 
                         ('classifier', d_tree)])

select_k_best_pipe_params = deepcopy(tree_pipe_params)
# all没有作用
select_k_best_pipe_params.update({'k_best__k':list(range(1,23)) + ['all']})

print(select_k_best_pipe_params) # {'k_best__k': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 'all'], 'classifier__max_depth': [None, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]}

# 与相关特征选择器比较
get_best_model_and_accuracy(select_k_pipe, select_k_best_pipe_params, X, y)
{'classifier__max_depth': [None, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21], 'k_best__k': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 'all']}
Best Accuracy: 0.8213333333333332
Best Parameters: {'classifier__max_depth': 3, 'k_best__k': 5}
Average Time to Fit (s): 0.201
Average Time to Score (s): 0.002
k_best = SelectKBest(f_classif, k=7)
p_values.head(7)
columnp_value
5PAY_00.000000e+00
6PAY_20.000000e+00
7PAY_30.000000e+00
8PAY_41.899297e-315
9PAY_51.126608e-279
10PAY_67.296740e-234
0LIMIT_BAL1.302244e-157

特征选择的两种统计方法,每次选择的7个特征都一样。我们选择这7个特征之外的所有特征,看看效果

# 完整性测试
# 用最差的特征
the_worst_of_X = X[X.columns.drop(['LIMIT_BAL', 'PAY_0', 'PAY_2', 'PAY_3', 'PAY_4', 'PAY_5', 'PAY_6'])]

# 如果选择的特征特别差
# 性能也会受影响
get_best_model_and_accuracy(d_tree, tree_params, the_worst_of_X, y)
Best Accuracy: 0.7829333333333333
Best Parameters: {'max_depth': 3}
Average Time to Fit (s): 0.329
Average Time to Score (s): 0.002

2.4基于模型的特征选择

自然语言处理

# 推文数据集
tweets_path = './data/twitter_sentiment.csv'
tweets = pd.read_csv(tweets_path, encoding='latin1')

tweets.head()
ItemIDSentimentSentimentText
010is so sad for my APL frie...
120I missed the New Moon trail...
231omg its already 7:30 :O
340.. Omgaga. Im sooo im gunna CRy. I'...
450i think mi bf is cheating on me!!! ...
tweets_X, tweets_y = tweets['SentimentText'], tweets['Sentiment']
# 流水线
from sklearn.feature_extraction.text import CountVectorizer
# 导入朴素贝叶斯,加快处理 
from sklearn.naive_bayes import MultinomialNB

featurizer = CountVectorizer()

text_pipe = Pipeline([('featurizer', featurizer), 
                 ('classify', MultinomialNB())])

text_pipe_params = {'featurizer__ngram_range':[(1, 2)], 
               'featurizer__max_features': [5000, 10000],
               'featurizer__min_df': [0., .1, .2, .3], 
               'featurizer__max_df': [.7, .8, .9, 1.]}


get_best_model_and_accuracy(text_pipe, text_pipe_params, tweets_X, tweets_y)
Best Accuracy: 0.7558931564507154
Best Parameters: {'featurizer__max_df': 0.7, 'featurizer__max_features': 10000, 'featurizer__min_df': 0.0, 'featurizer__ngram_range': (1, 2)}
Average Time to Fit (s): 2.897
Average Time to Score (s): 0.258
# 更基础,用了SelectKBest的流水线
featurizer = CountVectorizer(ngram_range=(1, 2))

select_k_text_pipe = Pipeline([('featurizer', featurizer), 
                      ('select_k', SelectKBest()),
                      ('classify', MultinomialNB())])

select_k_text_pipe_params = {'select_k__k': [1000, 5000]}

get_best_model_and_accuracy(select_k_text_pipe, 
                            select_k_text_pipe_params, 
                            tweets_X, tweets_y)
Best Accuracy: 0.752712856923538
Best Parameters: {'select_k__k': 5000}
Average Time to Fit (s): 3.271
Average Time to Score (s): 0.43

看起来SelectKBest对于文本数据效果不好。如果没有FeatureUnion,我们不能达到之前的准确率。值得注意的是,无论使用何种方式,拟合和预测的时间都很长:这是因为统计单变量方法在大量特征(例如从文本向量化中获取的特征)上表现不佳。

特征选择指标——基于树的模型

在拟合决策树时,决策树从根节点开始,在每个节点处选择最优分割,优化节点纯净度指标。默认情况下,scikit-learn每步都会优化基尼指数(gini metric)。每次分割时,模型会记录每个分割对整体优化目标的帮助

# 创建新的决策树分类器
tree = DecisionTreeClassifier()

tree.fit(X, y)
DecisionTreeClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier()
# 注意:还有其他特征
importances = pd.DataFrame({'importance': tree.feature_importances_, 'feature':X.columns}).sort_values('importance', ascending=False)

importances.head()
importancefeature
50.162200PAY_0
110.070392BILL_AMT1
40.068424AGE
00.059474LIMIT_BAL
160.053507BILL_AMT6

拟合中最重要的特征是PAY_0,和之前统计模型的结果相匹配。第2、第3和第5个特征,这3个特征在进行统计测试前没有显示出重要性。这意味着,这种特征选择方法有可能带来一些新的结果。

SelectFromModel和SelectKBest相比最大的不同之处在于不使用k(需要保留的特征数):SelectFromModel使用阈值,代表重要性的最低限度

# 和SelectKBest相似,但使用机器学习模型的内部指标来评估特征的重要性,不使用统计测试的p值
from sklearn.feature_selection import SelectFromModel

# 实例化一个类,按照决策树分类器的内部指标排序重要性,选择特征
select_from_model = SelectFromModel(DecisionTreeClassifier(), 
                                    threshold=.05)

selected_X = select_from_model.fit_transform(X, y)
selected_X.shape         
(30000, 7)
# 为后面加速
tree_pipe_params = {'classifier__max_depth': [1, 3, 5, 7]}

from sklearn.pipeline import Pipeline
import warnings
warnings.filterwarnings('ignore')

# 创建基于DecisionTreeClassifier的SelectFromModel
select = SelectFromModel(DecisionTreeClassifier())

select_from_pipe = Pipeline([('select', select),
                             ('classifier', d_tree)])

select_from_pipe_params = deepcopy(tree_pipe_params)

select_from_pipe_params.update({
 'select__threshold': [.01, .05, .1, .2, .25, .3, .4, .5, .6, "mean", "median", "2.*mean"],
 'select__estimator__max_depth': [None, 1, 3, 5, 7]
 })

print(select_from_pipe_params)  # {'select__threshold': [0.01, 0.05, 0.1, 'mean', 'median', '2.*mean'], 'select__estimator__max_depth': [None, 1, 3, 5, 7], 'classifier__max_depth': [1, 3, 5, 7]}


get_best_model_and_accuracy(select_from_pipe, 
                            select_from_pipe_params, 
                            X, y)
{'classifier__max_depth': [1, 3, 5, 7], 'select__threshold': [0.01, 0.05, 0.1, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 'mean', 'median', '2.*mean'], 'select__estimator__max_depth': [None, 1, 3, 5, 7]}
Best Accuracy: 0.8206333333333333
Best Parameters: {'classifier__max_depth': 3, 'select__estimator__max_depth': 1, 'select__threshold': 'median'}
Average Time to Fit (s): 0.374
Average Time to Score (s): 0.002
# 设置流水线最佳参数
select_from_pipe.set_params(**{'select__threshold':0.01,
                           'select__estimator__max_depth':None,
                           'classifier__max_depth':3})

# 拟合数据
select_from_pipe.steps[0][1].fit(X,y)

# 列出选择的列
X.columns[select_from_pipe.steps[0][1].get_support()]
Index(['LIMIT_BAL', 'SEX', 'EDUCATION', 'MARRIAGE', 'AGE', 'PAY_0', 'PAY_2',
       'PAY_3', 'PAY_5', 'PAY_6', 'BILL_AMT1', 'BILL_AMT2', 'BILL_AMT3',
       'BILL_AMT4', 'BILL_AMT5', 'BILL_AMT6', 'PAY_AMT1', 'PAY_AMT2',
       'PAY_AMT3', 'PAY_AMT4', 'PAY_AMT5', 'PAY_AMT6'],
      dtype='object')
select_from_pipe.steps[0][1]
SelectFromModel(estimator=DecisionTreeClassifier(), threshold=0.01)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SelectFromModel(estimator=DecisionTreeClassifier(), threshold=0.01)
DecisionTreeClassifier()
DecisionTreeClassifier()

线性模型和正则化

SelectFromModel可以处理任何包括feature_importances_或coef_属性的机器学习模型。基于树的模型会暴露前者,线性模型则会暴露后者。

在线性模型中,正则化是一种对模型施加额外约束的方法,目的是防止过拟合,并改进数据泛化能力。正则化通过对需要优化的损失函数添加额外的条件来完成,意味着在拟合时,正则化的线性模型有可能严重减少甚至损坏特征。

# 用正则化后的逻辑回归进行选择
logistic_selector = SelectFromModel(LogisticRegression())

# 新流水线,用LogistisRegression的参数进行排列
regularization_pipe = Pipeline([('select', logistic_selector), 
 ('classifier', tree)])

regularization_pipe_params = deepcopy(tree_pipe_params)

# L1和L2正则化
regularization_pipe_params.update({
 'select__threshold': [.01, .05, .1, "mean", "median", "2.*mean"],
 'select__estimator__penalty': ['l1', 'l2'],
 })

print(regularization_pipe_params)  # {'select__threshold': [0.01, 0.05, 0.1, 'mean', 'median', '2.*mean'], 'classifier__max_depth': [1, 3, 5, 7], 'select__estimator__penalty': ['l1', 'l2']}


get_best_model_and_accuracy(regularization_pipe, 
                            regularization_pipe_params, 
                            X, y)
{'classifier__max_depth': [1, 3, 5, 7], 'select__threshold': [0.01, 0.05, 0.1, 'mean', 'median', '2.*mean'], 'select__estimator__penalty': ['l1', 'l2']}
Best Accuracy: 0.8207000000000001
Best Parameters: {'classifier__max_depth': 3, 'select__estimator__penalty': 'l2', 'select__threshold': 'median'}
Average Time to Fit (s): 0.072
Average Time to Score (s): 0.001
# 设置流水线最佳参数
regularization_pipe.set_params(**{'select__threshold': 0.01, 
 'classifier__max_depth': 5, 
 'select__estimator__penalty': 'l2'})

# 拟合数据
regularization_pipe.steps[0][1].fit(X, y)

# 列出选择的列
X.columns[regularization_pipe.steps[0][1].get_support()]
Index([], dtype='object')

目前看来,逻辑回归分类器和支持向量分类器(SVC)的最大区别在于,后者会最大优化二分类项目的准确性,而前者对属性的建模更好

# SVC是线性模型,用线性支持在欧几里得空间内分割数据
# 只能分割二分数据
from sklearn.svm import LinearSVC

# 用SVC取参数
svc_selector = SelectFromModel(LinearSVC())

svc_pipe = Pipeline([('select', svc_selector), 
 ('classifier', tree)])

svc_pipe_params = deepcopy(tree_pipe_params)

svc_pipe_params.update({
 'select__threshold': [.01, .05, .1, "mean", "median", "2.*mean"],
 'select__estimator__penalty': ['l1', 'l2'],
 'select__estimator__loss': ['squared_hinge', 'hinge'],
 'select__estimator__dual': [True, False]
 })

print(svc_pipe_params)  # 'select__estimator__loss': ['squared_hinge', 'hinge'], 'select__threshold': [0.01, 0.05, 0.1, 'mean', 'median', '2.*mean'], 'select__estimator__penalty': ['l1', 'l2'], 'classifier__max_depth': [1, 3, 5, 7], 'select__estimator__dual': [True, False]}

get_best_model_and_accuracy(svc_pipe, 
                            svc_pipe_params, 
                            X, y) 
{'classifier__max_depth': [1, 3, 5, 7], 'select__threshold': [0.01, 0.05, 0.1, 'mean', 'median', '2.*mean'], 'select__estimator__penalty': ['l1', 'l2'], 'select__estimator__loss': ['squared_hinge', 'hinge'], 'select__estimator__dual': [True, False]}
Best Accuracy: 0.8212666666666667
Best Parameters: {'classifier__max_depth': 3, 'select__estimator__dual': False, 'select__estimator__loss': 'squared_hinge', 'select__estimator__penalty': 'l1', 'select__threshold': 'mean'}
Average Time to Fit (s): 0.76
Average Time to Score (s): 0.001

SVC达到了最高的准确率。可以看见拟合时间受到了影响,但是如果能把最快的预测和最好的准确率结合,那么机器学习流水线就会很出色了:基于SVC,利用正则化为决策树分类器找到最佳特征。下面看看选择器选择了哪些特征来达到目前的最佳准确率:

# 设置流水线最佳参数
svc_pipe.set_params(**{'classifier__max_depth': 5, 
                                  'select__estimator__dual': False, 
                                  'select__estimator__loss': 'squared_hinge', 
                                  'select__estimator__penalty': 'l1', 
                                  'select__threshold': 0.01})

# 拟合数据
svc_pipe.steps[0][1].fit(X, y)

# 列出选择的列
X.columns[svc_pipe.steps[0][1].get_support()]
Index(['SEX', 'EDUCATION', 'MARRIAGE', 'PAY_0', 'PAY_2', 'PAY_3', 'PAY_5'], dtype='object')

与逻辑回归比,唯一的区别是PAY_4特征,可以看到,移除单个特征不会影响流水线的性能。

3、闯关题

答题说明:
请在题目下方的答题Code Cell中输入你的答案,并按照步骤说明完成你的提交。
请注意,一定要按照顺序依次运行下方的代码,否则会出现报错喔!

答案全部为大写字符串、无任何分隔符(如:a1 =‘A’ 或 a1 =‘AB’),判断题T为正确,F为错误。

STEP1:根据要求完成题目

Q1. (判断题)L1正则化可以用于特征选择,提高模型泛化能力?

Q2. (判断题)皮尔逊相关系数是用来衡量两个连续变量之间线性关系强度的统计量?

Q3. (判断题)p值的一个常见阈值是0.05,可以认为p值小于0.05的特征是显著的?

#填入你的答案并运行,注意大小写
a1 = 'T'  # 如 a1= 'T/F'
a2 = 'T'  # 如 a2= 'T/F'
a3 = 'T'  # 如 a3= 'T/F'

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2064049.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot集成kafka-生产者发送消息

springboot集成kafka发送消息 1、kafkaTemplate.send()方法1.1、springboot集成kafka发送消息Message对象消息1.2、springboot集成kafka发送ProducerRecord对象消息1.3、springboot集成kafka发送指定分区消息 2、kafkaTemplate.sendDefault()方法3、kafkaTemplate.send(...)和k…

案例-异常

题目: (如果一开始不知道如何用异常的语法写,可先用如if语句代替try...catch,最后再把if优化为try...catch) 代码: javabean类: 测试类:

Java CompletableFuture:你真的了解它吗?

文章目录 1 什么是 CompletableFuture&#xff1f;2 如何正确使用 CompletableFuture 对象&#xff1f;3 如何结合回调函数处理异步任务结果&#xff1f;4 如何组合并处理多个 CompletableFuture&#xff1f; 1 什么是 CompletableFuture&#xff1f; CompletableFuture 是 Ja…

springboot静态资源访问问题归纳

以下内容基于springboot 2.3.4.RELEASE 1、默认配置的springboot项目&#xff0c;有四个静态资源文件夹&#xff0c;它们是有优先级的&#xff0c;如下&#xff1a; "classpath:/META-INF/resources/", &#xff08;优先级最高&#xff09; "classpath:/reso…

【精选】基于Spark的国漫推荐系统(精选设计产品)

目录&#xff1a; 系统开发技术 Python可视化技术 Django框架 Hadoop介绍 Scrapy介绍 IDEA介绍 B/S架构 MySQL数据库介绍 系统流程分析 操作流程 添加信息流程 删除信息流程 系统系统介绍&#xff1a; 可以查看我的B站&#xff1a; 系统测试 运行环境 软件平台 硬…

docker-compose安装NebulaGraph 3.8.0

文章目录 一. 安装NebulaGraph1.1 通过 Git 克隆nebula-docker-compose仓库的3.8.0分支到主机1.2 部署1.3 卸载1.4 查看 二. 安装NebulaGraph Studio2.1 下载 Studio 的部署配置文件2.2 创建nebula-graph-studio-3.10.0目录&#xff0c;并将安装包解压至目录中2.3 解压后进入 n…

shaushaushau1

CVE-2023-7130 靶标介绍&#xff1a; College Notes Gallery 2.0 允许通过“/notes/login.php”中的参数‘user’进行 SQL 注入。利用这个问题可能会使攻击者有机会破坏应用程序&#xff0c;访问或修改数据. 已经告诉你在哪里存在sql注入了&#xff0c;一般上来应该先目录扫…

【补充篇】AUTOSAR多核OS介绍(下)

文章目录 前文回顾1 AUTOSAR OS1.1 AUTSOAR OS元素1.1.1 操作系统对象1.1.2 操作系统应用程序1.1.3 AUTOSAR OS裁剪类型1.1.4 AUTOSAR OS软件分区1.2 AUTOSAR OS自旋锁1.3 AUTOSAR OS核间通信1.4 AUTOSAR OS多核调度前文回顾 在上篇文章【补充篇】AUTOSAR多核OS介绍(上)中,…

对于一个36岁的人来说,现在转行AI大模型还来得及吗?

前言 在职场生涯中&#xff0c;33岁似乎是一个尴尬的年龄。许多人在这个阶段已经定型&#xff0c;难以寻求新的突破。然而&#xff0c;随着科技行业的飞速发展&#xff0c;人工智能成为了新时代的宠儿。那么&#xff0c;对于一个33岁的人来说&#xff0c;现在转行AI大模型还来…

做SSH实验下载 paramiko库

今天做SSH实验下载paramiko库文件一直出问题&#xff0c;后面库文件下好了还是报错&#xff0c;这里记录了我的解决方案。 pycharm修改默认下载路径为国内镜像&#xff08;我这里用清华大学的镜像下载快一些&#xff09; Simple Index 到这里路径就改好了&#xff0c;接下来就…

从就业出发,深度剖析大数据行业的现状与前景

以一个经典案例引入——啤酒与纸尿裤的故事。 20世纪90年代&#xff0c;沃尔玛从购物的后台信息数据中&#xff0c;发现很多买了纸尿裤的男士会同时买啤酒。后来&#xff0c;调查发现&#xff0c;此类人多是被“轰出来”买纸尿裤&#xff0c;一想到养娃压力大&#xff0c;心情…

牛客竞赛数据结构专题班树状数组、线段树练习题

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ G 智乃酱的平方数列&#xff08;线段树&#xff0c;等差数列&#xff0c;多项式&#xff09; 题目描述 想必你一定会用线段树维护等差数列吧&#xff1f;让我们来看看它的升级版。 请你维护一个长度为510 ^5…

Mysql高级 [Linux版] 性能优化 数据库系统配置优化 和 MySQL的执行顺序 以及 Mysql执行引擎介绍

数据库系统配置优化 1、定义 数据库是基于操作系统的&#xff0c;目前大多数MySQL都是安装在linux系统之上&#xff0c;所以对于操作系统的一些参数配置也会影响到MySQL的性能&#xff0c;下面就列出一些常用的系统配置。 2、优化配置参数-操作系统 优化包括操作系统的优化及My…

集运系统:如何实现不同员工的不同操作权限?

在集运行业&#xff0c;员工的角色和职责各有不同&#xff0c;因此对系统的操作权限需求也不尽相同。为了确保数据的安全性和业务的顺利进行&#xff0c;易境通集运系统提供了灵活的权限管理功能&#xff0c;让企业可以根据员工的角色和职责&#xff0c;设置不同的操作权限。 易…

Redis (day 3)

一、通过jedis连接数据库 1.首先导入依赖 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.1.0</version></de…

mac 微信数据直接存储到移动硬盘

在apple设备上存储都是1500块/128gb的价格收取的&#xff0c;真的是寸土寸金。在手机已经占用了一遍存储空间之后&#xff0c;微信备份还要占用一遍。 iCloud备份微信聊天记录的稳定性真的非常差劲&#xff0c;比如我微信30g&#xff0c;经常恢复到20g左右就被打断&#xff0c;…

【C++ Primer Plus习题】2.6

问题: 解答: #include <iostream> using namespace std;#define LIGHT_TO_SKY 63240double lightToSky(double value) {return value * LIGHT_TO_SKY; }int main() {double light 0;cout << "请输入光年值:";cin >> light;cout << light &…

还在返回一大堆 null 字段给前端?

在许多情况下&#xff0c;返回的 JSON 数据可能包含许多 null 值的字段&#xff0c;这会导致数据冗余&#xff0c;增加网络传输的负担&#xff0c;并使得前端处理数据变得复杂。因此&#xff0c;使用 JsonInclude(JsonInclude.Include.NON_NULL) 可以帮助我们优化 JSON 的输出&…

看看人家写的,Controller太优雅了~【送源码】

今天咱们来聊聊如何写出优雅的Controller代码。 写程序想让作品成为经典&#xff0c;不只是简单地加个try-catch就完事了。有时候&#xff0c;一个不小心&#xff0c;Controller里写的业务逻辑都能让你血压飙升&#xff01;不过别慌&#xff0c;今天我就来带大家看看怎么把Cont…