随机森林特征重要性(Variable importance)评估方法

news2025/2/3 15:14:41

Random Forest Variable importance

  • 算法介绍
  • 实现
    • 算法流程
    • 分类
    • 回归
  • 实验
    • 实验1:waveform数据集(分类)
    • 实验2:superconductivity数据集(回归)
    • 实验3:power-consumption数据集(回归)
    • 实验4:mnist数据集(可视化实验)
    • 实验5:与sklearn的验证
  • 附录

项目主页:randomforest
C++ implementation of random forests classification, regression, proximity and variable importance.
推荐阅读:Random Forests C++实现:细节,使用与实验

算法介绍

  特征重要性评估(Variable importance measure, or Feature importance evaluation)用来计算样本特征的重要性,定量地描述特征对分类或者回归的贡献程度。随机森林可以用来对特征重要性进行评估,从另一个角度来说,特征重要新评估是随机森林的一种自带工具,主要分为两种方法:(1)Mean Decrease Impurity(MDI),通过统计节点分裂时不纯度的下降数值来衡量某个节点的重要性;(2)Mean Decrease Accuracy(MDA), 方法是随机交换袋外数据集 (permuted oob data) 中某个特征的值,然后重新进行预测,通过衡量分类/回归的准确度下降的程度来计算该特征的重要性。这两种方法的思想都是Leo Breiman提出的。
  MDI使用的是训练集数据,在RF训练完成后就能直接获取MDI特征重要性评估值;MDA使用的是oob数据,需要在RF训练完成后独立运行特征重要性评估的程序。由于MDI完全使用训练数据,它可能对评估的准确性造成影响,而且更加倾向于加大high cardinality features的权重。相比之下,MDA的结果更加准确。MDI与MDA的对比可以查看sklearn给出的实验——Permutation Importance vs Random Forest Feature Importance (MDI)。

Mean Decrease Accuracy:
The MDA of X(j) is obtained by averaging the difference in out-of-bag error estimation before and after the permutation over all trees.
—— Biau, G., Scornet, E. A random forest guided tour. TEST 25, 197–227 (2016).

本文讨论和实现MDA方法,特征 j j j(样本第 j j j维)的重要性计算方法如下:
(1)分类:

M D A c ( j ) = 1 T ∑ t T [ 1 ∣ D t ∣ ( ∑ X i ∈ D t I ( P ( X i ) = = y i ) − ∑ X i j ∈ D t j I ( P ( X i j ) = = y i ) ) ] MDA_c(j)=\frac {1}{T} \sum_t^T\left [ \frac{1}{|D_t|} \left( \sum_{X_i\in D_t} I(P(X_i)==y_i)- \sum_{X_i^j\in D_t^j} I(P(X_i^j)==y_i) \right) \right] MDAc(j)=T1tT Dt1 XiDtI(P(Xi)==yi)XijDtjI(P(Xij)==yi)

(2)回归:

M D A r ( j ) = 1 T ∑ t T [ 1 ∣ D t ∣ ( ∑ X i j ∈ D t j ∑ k ( R k ( X i j ) − y i k ) 2 − ∑ X i ∈ D t ∑ k ( R k ( X i ) − y i k ) 2 ) ] MDA_r(j)=\frac {1}{T} \sum_t^T\left [ \frac{1}{|D_t|} \left( \sum_{X_i^j\in D_t^j} \sum _k \left(R_k(X_i^j)-y_i^k\right)^2- \sum_{X_i\in D_t} \sum_k \left(R_k(X_i)-y_i^k\right)^2 \right) \right] MDAr(j)=T1tT Dt1 XijDtjk(Rk(Xij)yik)2XiDtk(Rk(Xi)yik)2

以上公式参考自Gérard Biau的经典论文"A Random Forest Guided Tour",解释下公式中的符号:

  1. T T T: RF中随机树的数量
  2. ( X i , y i ) (X_i,y_i) (Xi,yi): 样本,对于分类 y y y为类别,对于回归为输出(可以是多维)
  3. X i j X_i^j Xij: X i X_i Xi的第 j j j维(特征)随机交换后的样本
  4. D t D_t Dt: 随机树 t t t的袋外(oob)样本集, D t j D_t^j Dtj: 第 j j j维交换后形成的样本集
  5. P ( X i ) P(X_i) P(Xi): 样本 X i X_i Xi的预测结果(类别); R ( X i ) R(X_i) R(Xi):样本 X i X_i Xi的预测输出(回归)
  6. I ( P X i = = y i ) I(P_{X_i}==y_i) I(PXi==yi): 指示函数,如果预测结果与真实类别相同,则返回1,否则返回0。
  7. y i k y_i^k yik: 考虑到多目标回归,表示第 k k k维输出

这个公式反映了,对样本第 j j j维(特征)上的数据进行随机交换后,模型预测(分类)准确度或者(回归)精度下降的程度,使用的是每棵树的袋外数据。如果在随机交换后,准确度下降较多,则认为这个特征比较重要。
上面公式中 M D A c ( j ) 、 M D A r ( j ) MDA_c(j)、MDA_r(j) MDAc(j)MDAr(j)被称为raw score。对于 M D A c ( j ) MDA_c(j) MDAc(j),在数值上它反映的是当特征 j j j 被随机交换后,袋外样本的平均分类正确率的下降值;对于 M D A r ( j ) MDA_r(j) MDAr(j),在数值上它反映的是当特征 j j j 被随机交换后,袋外样本均方误差(MSE)的上升数值。

In every tree grown in the forest, put down the oob cases and count the number of votes cast for the correct class. Now randomly permute the values of variable m in the oob cases and put these cases down the tree. Subtract the number of votes for the correct class in the variable-m-permuted oob data from the number of votes for the correct class in the untouched oob data. The average of this number over all trees in the forest is the raw importance score for variable m. by Leo Breiman

实现

算法流程

随机森林MDA算法的流程如下(以分类为例):

训练随机森林模型(树数量 T T T),训练过程记录每棵树的袋外数据集 D t D_t Dt
对于每棵树 t = 1...... T t=1......T t=1......T
|  计算 D t D_t Dt上的分类正确的样本数 c 0 c_0 c0
|     ∑ D t I ( P ( X i ) = = y i ) \sum_{D_t} I(P(X_i)==y_i) DtI(P(Xi)==yi)
|  对于每个特征 j = 1...... M j=1......M j=1......M
|  |  对 D t D_t Dt样本的特征 j j j进行随机交换得到 D t j D_t^j Dtj
|  |  计算 D t j D_t^j Dtj上的分类正确的样本数 c 1 c_1 c1:
|  |     ∑ D t j I ( P ( X i j ) = = y i ) \sum_{D_t^j} I(P(X_i^j)==y_i) DtjI(P(Xij)==yi)
|  |  计算: m d a ( t , j ) = 1 ∣ D t ∣ ( c 0 − c 1 ) mda(t,j)=\frac{1}{|D_t|}(c_0-c_1) mda(t,j)=Dt1(c0c1)
|  end of j j j
end of t t t
对于每个特征 j = 1...... M j=1......M j=1......M
|  计算特征 j j j的重要性raw socre:
|     M D A c ( j ) = 1 ∣ T ∣ ∑ t m d a ( t , j ) MDA_c(j)=\frac{1}{|T|}\sum_t mda(t,j) MDAc(j)=T1tmda(t,j)
end of j j j

算法的时间复杂度为 O ( M ∗ N ∗ T ) O(M*N*T) O(MNT) M , N , T M,N,T M,N,T分别是特征数、样本数、随机树数量。运行MDA特征重要性评估的耗时要远远大于随机森林训练的耗时。MDA特征重要性评估的C++实现如下:

分类

int RawVariableImportanceScore(float** data, int* label, LoquatCForest* loquatForest, int nType, float* varImportance, bool bNormalize, char* filename)
{
	int var, tr, i, j, oobnum, index;
	const int variables_num = loquatForest->RFinfo.datainfo.variables_num;
	const int Ntrees = loquatForest->RFinfo.ntrees;
	struct LoquatCTreeStruct* pTree = NULL;
	int* pIndex = NULL, predicted_class_index;
	float confidence;
	int correct_num = 0, correct_num_premute = 0;
	float* tmp_data = new float[variables_num];

	int** DeltMatrix = new int* [Ntrees];
	for (i = 0; i < Ntrees; i++)
	{
		DeltMatrix[i] = new int[variables_num];
		memset(DeltMatrix[i], 0, sizeof(int) * variables_num);
	}

	float* mean_var = new float[variables_num];
	memset(mean_var, 0, sizeof(float) * variables_num);
	float* std2_var = new float[variables_num];
	memset(std2_var, 0, sizeof(float) * variables_num);	
	int *oobOfTrees = new int[Ntrees];
	
	for (tr = 0; tr < Ntrees; tr++)
	{
		pTree = loquatForest->loquatTrees[tr];
		pIndex = pTree->outofbag_samples_index;
		oobnum = pTree->outofbag_samples_num;
		oobOfTrees[tr] = oobnum;
		int* permuted_order = new int[oobnum];
		for (correct_num = 0, i = 0; i < oobnum; i++)
		{
			index = pIndex[i];
			PredictAnTestSampleOnOneTree(data[index], variables_num, pTree, predicted_class_index, confidence);
			if (predicted_class_index == label[index])
				correct_num++;
		}

		for (var = 0; var < variables_num; var++)
		{
			// permuting (var)th variables
			// permute [0, oobnum-1]
			permute(oobnum, permuted_order);

			for (correct_num_premute = 0, i = 0; i < oobnum; i++)
			{
				index = pIndex[permuted_order[i]];
				memcpy(tmp_data, data[pIndex[i]], variables_num * sizeof(float));
				tmp_data[var] = data[index][var];
				PredictAnTestSampleOnOneTree(tmp_data, variables_num, pTree, predicted_class_index, confidence);
				if (predicted_class_index == label[pIndex[i]])
					correct_num_premute++;
			}

			DeltMatrix[tr][var] = correct_num - correct_num_premute;
			mean_var[var] += (correct_num - correct_num_premute)/(float)oobOfTrees[tr];
		}

		delete[]permuted_order;
	}

	for (i = 0; i < variables_num; i++)
		mean_var[i] = mean_var[i] / Ntrees;

	for (i = 0; i < variables_num; i++)
	{
		for (j = 0; j < Ntrees; j++)
			std2_var[i] += (DeltMatrix[j][i]/(float)oobOfTrees[j] - mean_var[i]) * (DeltMatrix[j][i]/(float)oobOfTrees[j] - mean_var[i]);
		std2_var[i] /= Ntrees;
	}

	float* raw_score = new float[variables_num];
	float* z_score = new float[variables_num];
	memset(raw_score, 0, sizeof(float) * variables_num);
	memset(z_score, 0, sizeof(float) * variables_num);

	float fsum = 0.f;
	// raw score
	for (i = 0; i < variables_num; i++)
	{
		raw_score[i] = mean_var[i];
		fsum += raw_score[i];
	}

	// Normalization
	if (bNormalize) {
		for (i = 0; i < variables_num; i++)
			raw_score[i] /= fsum;
	}

	// z-score
	fsum = 0.f;
	for (i = 0; i < variables_num; i++)
	{
		z_score[i] = mean_var[i] / (sqrtf(std2_var[i]) + FLT_EPSILON); //0530
		fsum += z_score[i];
	}

	// Normalization
	if (bNormalize) {
		for (i = 0; i < variables_num; i++)
			z_score[i] /= fsum;
	}

	if (nType == 0) // raw_score
		memcpy(varImportance, raw_score, sizeof(float) * variables_num);
	else  // z-score
		memcpy(varImportance, z_score, sizeof(float) * variables_num);

	if (filename != NULL)
	{
		fstream vieFile;
		vieFile.open(filename, ios_base::out);
		if (vieFile.is_open())
		{
			vieFile << "variable index" << "\t" << "raw score" << "\t\t" << "z-score" << endl;
			for (i = 0; i < variables_num; i++)
				vieFile << i << "\t\t" << raw_score[i] << "\t\t" << z_score[i] << endl;
			vieFile.close();
		}
	}

	delete[] tmp_data;

	for (i = 0; i < Ntrees; i++)
		delete[] DeltMatrix[i];
	delete[] DeltMatrix;
	delete[] mean_var;
	delete[] std2_var;
	delete[] oobOfTrees;
	delete[] raw_score;
	delete[] z_score;

	return 0;
}

回归

回归森林中的实现比分类森林中复杂一些,主要是要兼顾多目标回归的情况。

int RawVariableImportanceScore(float** data, float* target, LoquatRForest* loquatForest, int nType, float* varImportance, bool bNormalize, char* filename)
{
	int var, tr, i, j, oobnum, index;
	const int variables_num = loquatForest->RFinfo.datainfo.variables_num_x;
	const int target_num = loquatForest->RFinfo.datainfo.variables_num_y;
	const int Ntrees = loquatForest->RFinfo.ntrees;
	int* pIndex = NULL;
	float confidence,  predicted=0.f;
	float *mse = new float[target_num];
	float *mse_premute = new float[target_num];
	float *target_predicted = NULL;
	float* tmp_data = new float[variables_num];

	float** DeltMatrix = new float * [Ntrees];
	for (i = 0; i < Ntrees; i++)
	{
		DeltMatrix[i] = new float[variables_num];
		memset(DeltMatrix[i], 0, sizeof(float) * variables_num);
	}

	float* mean_var = new float[variables_num];
	memset(mean_var, 0, sizeof(float) * variables_num);
	float* std2_var = new float[variables_num];
	memset(std2_var, 0, sizeof(float) * variables_num);	
	int *oobOfTrees = new int[Ntrees];
	
	for (tr = 0; tr < Ntrees; tr++)
	{
		pIndex = loquatForest->loquatTrees[tr]->outofbag_samples_index;
		oobnum = loquatForest->loquatTrees[tr]->outofbag_samples_num;
		oobOfTrees[tr] = oobnum;
		int* permuted_order = new int[oobnum];
		memset(mse, 0, sizeof(float)*target_num);
		for (i = 0; i < oobnum; i++)
		{
			index = pIndex[i];
			target_predicted = GetArrivedLeafNode(loquatForest, tr,  data[index])->pLeafNodeInfo->MeanOfArrived;
			
			for(int k=0; k<target_num; k++)
			{
				predicted = loquatForest->bTargetNormalize == false ? target_predicted[k] : (target_predicted[k] - loquatForest->offset[k])/ loquatForest->scale[k];
				mse[k] += (target[index*target_num+k]-predicted) * (target[index*target_num+k]-predicted);
			}	
		}

		for (var = 0; var < variables_num; var++)
		{
			// permuting (var)th variables
			// permute [0, oobnum-1]
			permute(oobnum, permuted_order);
			memset(mse_premute, 0, sizeof(float)*target_num);
			for (i = 0; i < oobnum; i++)
			{
				index = pIndex[permuted_order[i]];
				memcpy(tmp_data, data[pIndex[i]], variables_num * sizeof(float));
				tmp_data[var] = data[index][var];
				target_predicted = GetArrivedLeafNode(loquatForest, tr,  tmp_data)->pLeafNodeInfo->MeanOfArrived;

				for(int k=0; k<target_num; k++)
				{
					predicted = loquatForest->bTargetNormalize == false ? target_predicted[k]  : (target_predicted[k] -loquatForest->offset[k])/loquatForest->scale[k];
					mse_premute[k] +=(target[pIndex[i]*target_num+k] - predicted)*(target[pIndex[i]*target_num+k] - predicted);
				}
		
			}

			for (int k=0; k<target_num; k++)
				DeltMatrix[tr][var] += (mse_premute[k] - mse[k]);
			mean_var[var] += DeltMatrix[tr][var]/(float)oobOfTrees[tr];
		}

		delete[]permuted_order;
	}

	for (i = 0; i < variables_num; i++)
		mean_var[i] = mean_var[i] / Ntrees;

	for (i = 0; i < variables_num; i++)
	{
		for (j = 0; j < Ntrees; j++)
			std2_var[i] += (DeltMatrix[j][i]/(float)oobOfTrees[j] - mean_var[i]) * (DeltMatrix[j][i]/(float)oobOfTrees[j] - mean_var[i]);
		std2_var[i] /= Ntrees;
	}

	float* raw_score = new float[variables_num];
	float* z_score = new float[variables_num];
	memset(raw_score, 0, sizeof(float) * variables_num);
	memset(z_score, 0, sizeof(float) * variables_num);

	float fsum = 0.f;
	// raw score
	for (i = 0; i < variables_num; i++)
	{
		raw_score[i] = mean_var[i];
		fsum += raw_score[i];
	}

	// Normalization
	if (bNormalize) {
		for (i = 0; i < variables_num; i++)
			raw_score[i] /= fsum;
	}

	// z-score
	fsum = 0.f;
	for (i = 0; i < variables_num; i++)
	{
		z_score[i] = mean_var[i] / (sqrtf(std2_var[i]) + FLT_EPSILON); //0530
		fsum += z_score[i];
	}

	// Normalization
	if (bNormalize) {
		for (i = 0; i < variables_num; i++)
			z_score[i] /= fsum;
	}

	if (nType == 0) // raw_score
		memcpy(varImportance, raw_score, sizeof(float) * variables_num);
	else  // z-score
		memcpy(varImportance, z_score, sizeof(float) * variables_num);

	if (filename != NULL)
	{
		fstream vieFile;
		vieFile.open(filename, ios_base::out);
		if (vieFile.is_open())
		{
			vieFile << "variable index" << "\t" << "raw score" << "\t\t" << "z-score" << endl;
			for (i = 0; i < variables_num; i++)
				vieFile << i << "\t\t" << raw_score[i] << "\t\t" << z_score[i] << endl;
			vieFile.close();
		}
	}

	delete[] tmp_data;
	delete[] mse;
	delete[] mse_premute;
	for (i = 0; i < Ntrees; i++)
	{
		delete[] DeltMatrix[i];
		DeltMatrix[i] = NULL;
	}
	delete[] DeltMatrix;
	delete[] mean_var;
	delete[] std2_var;
	delete[] oobOfTrees;
	delete[] raw_score;
	delete[] z_score;

	return 1;
}

上述代码随机随机森林实现的一部分,建议到项目主页randomforest上查看。

实验

  本节通过实验展示了随机森林特征重要性评估的效果,选取了分类和回归中典型的数据集,特征数量为11、40、81、780、4096。并根据数据集的特点给出可视化结果,以此验证本项目实现的准确性。

实验1:waveform数据集(分类)

  使用waveform数据集,训练样本数5000,特征40维,类别数3。RF参数为随机树200棵,分类候选特征数 40 \sqrt{40} 40 。运行5次得到以下特征重要性的柱状图,显示每个特征重要性的平均值及标准差。这个数据是经典的用于特征重要性评估验证的数据集,它的后19维特征是随机噪声,计算结果符合上述情况。
在这里插入图片描述

实验2:superconductivity数据集(回归)

  数据集superconductivity的特征81维,训练样本数21263,采集和加工自超导体临界温度的实验。随机回归森林的参数为:随机树 T = 200 T=200 T=200,分类候选特征数 81 3 \frac{81}{3} 381,节点最小样本数5,最大树深度40,都是默认参数。运行5次得到以下特征重要性的柱状图,显示每个特征重要性的平均值及标准差。

在这里插入图片描述

实验3:power-consumption数据集(回归)

  使用Tetuan-City-power-consumption数据集来进行试验,原始数据集是通过时间、温度、湿度、风速等6个变量来预测城市3个配电网的能源消耗,即输入6维,输出3维。由于“时间”变量难以使用,所以分解为[minute,hour,day,month,weekday,weekofyear] 6个变量,加上原始的5个气象变量 (temperature, light, sound, CO2 and digital passive infrared (PIR)),形成新的11维输入。随机回归森林的参数为:随机树 T = 200 T=200 T=200,分类候选特征数 4 = 11 3 4=\frac{11}{3} 4=311,节点最小样本数5,最大树深度40,都是默认参数。可以看到hour,month,weekofyear,气温这四个特征(变量)的重要性排名前列,符合常理。
在这里插入图片描述

实验4:mnist数据集(可视化实验)

  使用mnist手写字符识别数据集训练随机森林分类器,样本数60000,特征数780。RF参数为:随机树 T = 200 T=200 T=200,分类候选特征数 780 \sqrt{780} 780 ,节点最小样本数5,最大树深度40。将特征重要性的输出值转换为相同分辨率的图像(28x28,780+4,在最后增补了4个0),如下图。可以看到重要特征集中在图像中部,周边的特征重要性非常低,这也符合手写字符训练图像周边像素大多为0的情况。

在这里插入图片描述

实验5:与sklearn的验证

  Scikit-learn中实现了随机森林MDI方法,在训练结束后可以获取特征重要性的权重值。官网文章“Pixel importances with a parallel forest of trees”通过olivetti_faces人脸识别数据集对像素级特征的重要性进行了可视化。
  将本项目输出的olivetti_faces人脸像素特征重要性与sklearn结果进行对比,以验证本项目实现的随机森林在特征重要性评估中的有效性。使用olivetti_faces人脸识别数据集进行验证,选取样本数50(选取了前5类样本,与sklearn官网的实验保持一致),特征数4096(64x64图像),每个样本是一张人脸图像。RF参数为:随机树 T = 750 T=750 T=750,分类候选特征数 4096 \sqrt{4096} 4096 ,节点最小样本数1,最大树深度40。这个实验是为了与sklearn的的结果进行比较,实验样本完全相同,参数尽量接近。从可视化结果看,本项目的输出(上图)与sklearn的MDI类似,这验证了本项目实现的特征重要性算法的正确性。
本项目MDA

本项目MDA特征重要新

sklearn-MDI

sklearn的MDI特征重要性

附录

使用我实现的randomforest算法进行RF训练+特征重要性评估的代码如下(以分类森林为例):

int main()
{
	// read training samples if necessary
	char filename[500] = "/to/direction/dataset/train-data.txt" 
	float** data = NULL;
	int* label = NULL;
	Dataset_info_C datainfo;
	int rv = InitalClassificationDataMatrixFormFile2(filename, data/*OUT*/, label/*OUT*/, datainfo/*OUT*/);
	// check the return value
	// 	... ...

	// setting random forests parameters
	RandomCForests_info rfinfo;
	rfinfo.datainfo = datainfo;
	rfinfo.maxdepth = 40;
	rfinfo.ntrees = 200;
	rfinfo.mvariables = (int)sqrtf(datainfo.variables_num);
	rfinfo.minsamplessplit = 5;
	rfinfo.randomness = 1;
	// train forest
	LoquatCForest* loquatCForest = NULL;
	rv = TrainRandomForestClassifier(data, label, rfinfo, loquatCForest /*OUT*/, 20);
	// check the return value
	// 	... ...

	// variable importance measure
	float *vim = new float [datainfo.variables_num];
	RawVariableImportanceScore2(data, label, loquatCForest, 0/*raw score*/, vim, true, "file_to_store.txt");
	delete [] vim;
	
	// clear the memory allocated for the entire forest
	ReleaseClassificationForest(&loquatCForest);
	// release money: data, label
	for (int i = 0; i < datainfo.samples_num; i++)
		delete[] data[i];
	delete[] data;
	delete[] label;
	return 0;
}

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

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

相关文章

UNIX环境高级编程——进程间通信

15.1 引言 本章将说明进程之间相互通信技术——进程间通信&#xff08;InterProcess Communication&#xff0c;IPC&#xff09;。 IPC类型包括&#xff1a; 前10种IPC形式通常限于同一台主机的两个进程之间的IPC&#xff1b;最后2种是仅有的支持不同主机上两个进程之间的I…

5G NR SRS功率控制

3GPP TS 38.213 7.3.1节中&#xff0c;关于SRS的功率控制算法如下&#xff1a; 其中&#xff0c; : 表示在载波f服务小区c以及SRS的发送时隙i UE被配置的最大发射功率&#xff1b; : 表示在载波f&#xff0c;BWP b, 服务小区c&#xff0c;SRS资源集qs所配置的p0值&#xff1…

【GateWay快速入门】 —— 每天一点小知识

&#x1f4a7; Z o o K e e p e r 快速入门 \color{#FF1493}{ZooKeeper快速入门} ZooKeeper快速入门&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专…

子线程不显示Toast?

Handler不仅在ANR过程中有用到&#xff0c;Toast中也用到了Handler。 代码如下&#xff1a; public class MainActivity extends AppCompatActivity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.lay…

chatgpt赋能python:Python如何进行高效的查询?

Python如何进行高效的查询&#xff1f; 在Python中&#xff0c;查询是一项非常常见的操作&#xff0c;而高效的查询在数据量增大时可以显著提高程序的性能和效率。本文将介绍Python中的查询方式以及如何进行高效的查询操作。 Python中的查询方式 Python中查询的方式主要分为…

嵌入式容器源码解析

问题分析 不同于使用springmvc,在我们使用springboot时无需配置tomcat就可以直接使用&#xff0c;这就说明springboot已经在我们启动项目时将tomcat配置好了&#xff0c;接下来我们就来看看springboot底层是怎么实现的。 源码解析 ServletWebServerFactoryAutoConfiguratio…

基于Java乡镇自来水收费系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

chatgpt赋能python:如何使用Python查看当前路径

如何使用Python查看当前路径 作为一名有10年Python编程经验的工程师&#xff0c;我今天想和大家分享如何使用Python来查看当前路径。这在开发过程中非常有用&#xff0c;尤其是当需要导入文件或访问某个文件夹时。在本文中&#xff0c;我将介绍在Windows、Mac和Linux系统上如何…

深蓝学院C++基础与深度解析笔记 第 3 章 数组、 vector 与字符串

第 3 章 数组、 vector 与字符串 一、数组 将一到多个相同类型的对象串连到一起&#xff0c;所组成的类型 int a → int b[10] b的类型是int[10]; A、 数组的初始化方式&#xff1a; ● 缺省初始化 ● 聚合初始化&#xff08; aggregate initialization &#xff09; int b[3…

ansible的部署和命令模块和playbooks剧本

系列文章目录 文章目录 系列文章目录一、Ansible1、Ansible简介2、Ansible特点及优势3、Ansible核心程序4、Ansible工作原理及流程5.部署Ansible自动化运维工具6、Ansible常用模块 二、playbooks剧本1.playbooks2、playbooks组成部分3、playbooks启动及检测 总结 一、Ansible …

简单的TCP网络程序·多进程、多线程(后端服务器)

前文链接 -- 简单的TCP网络程序单进程 上篇文章中&#xff0c;实现了TCP网络通信的的单进程版本&#xff0c;因为实现的是一个死循环的逻辑&#xff0c;是串行实运行的&#xff0c;显然这和实际中的TCP通信是不同的&#xff0c;为了解决这方面的问题&#xff0c;需要使用多进程…

B+树:MySQL数据库索引的实现

作为一个软件开发工程师&#xff0c;你对数据库肯定再熟悉不过了。作为主流的数据存储系统&#xff0c;它在我们的业务开发中&#xff0c;有着举足轻重的地位。在工作中&#xff0c;为了加速数据库中数据的查找速度&#xff0c;我们常用的处理思路是&#xff0c;对表中数据创建…

chatgpt赋能python:Python怎么横着输出?

Python怎么横着输出&#xff1f; 如果你是一名有10年Python编程经验的工程师&#xff0c;你一定已经经历过许多项目&#xff0c;很可能你曾经需要对代码进行一些横向格式化或输出。Python作为一种高级编程语言&#xff0c;有各种各样的技巧和技能。在本文中&#xff0c;我们将…

node安装后的全局环境变量配置

安装node时&#xff0c;位置最好不要装在c盘&#xff0c;这里&#xff0c;我在D盘下创建了文件夹"node"&#xff0c;安装地址选择在该文件夹下 一直next&#xff0c;直到安装结束&#xff0c;打开"node"文件夹&#xff0c;安装完后&#xff0c;里面的配置…

C++【STL】之vector的使用

文章目录&#xff1a; vector介绍vector使用1. 默认成员函数1.1 默认构造1.2 拷贝构造1.3 析构函数1.4 赋值重载 2. 迭代器2.1 正向迭代器2.2 反向迭代器 3. 容量操作3.1 获取空间数据3.2 空间扩容3.3 大小调整3.4 空间缩容 4. 数据访问4.1 下标随机访问4.2 获取首尾元素 5. 数…

chatgpt赋能python:Python怎么横向键盘输入?

Python怎么横向键盘输入&#xff1f; 如果你是一位使用Python进行编程的工程师&#xff0c;你肯定明白快速而准确地输入代码的重要性。现在&#xff0c;许多程序员都找到了一个方法来更快地输入代码-横向键盘输入。 什么是横向键盘输入&#xff1f; 横向键盘输入是一种方法&…

基于Java校园驿站管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

力扣题库刷题笔记5--最长回文子串

1、题目如下&#xff1a; 2、个人Python代码实现&#xff1a; 首先想到的是通过类似冒泡排序的方式进行切片&#xff0c;然后判断切片的子字符串是否为回文字符串&#xff0c;然后记录出最长的回文字符串&#xff0c;代码如下&#xff1a; 可以看到&#xff0c;通过切片的方式&…

合宙Air724UG Cat.1模块硬件设计指南--电源供电

电源供电 简介 在模块应用设计中&#xff0c;电源设计是很重要的一部分&#xff0c;供电部分的电路设计不当会造成模块出现工作异常、指标恶化等现象&#xff0c;而良好的电源设计方案能够给模块提供稳定的工作状态。 特性 模块主供电VBAT&#xff1a;3.3V~4.3V&#xff0c;推…

设计模式—模板方法模式

模板方法模式&#xff1a; 定义一个操作的流程框架&#xff0c;而将流程中一些步骤延迟到子类中实现。使得子类在不改变流程结构的情况下&#xff0c;重新定义流程中的特定步骤。 主要角色&#xff1a; 抽象类: 负责给出操作流程的轮廓或框架&#xff0c;由模板方法和若干基…