当前位置: 首页 > news >正文

Kaggle First Place Winner Solution Study——多变量回归问题

        本期分享一个Kaggle上playground系列多变量回归问题的第一名解决方案。试着分析、复现、学习一下金牌选手的数据分析思路。

赛题链接:

Prediction of Wild Blueberry Yield | Kagglehttps://www.kaggle.com/competitions/playground-series-s3e14第一名解决方案链接:

[PS S3E14, 2023] First place winning solution | Kagglehttps://www.kaggle.com/code/sergiosaharovskiy/ps-s3e14-2023-first-place-winning-solution


目录

1、数据查看

2、准备工作

3、数据预处理

第二部分是 mattop_post_process 函数的定义

第三部分是 DataProcessor 类的定义

第四部分是 xy_split 函数的定义

4、模型定义

——fit函数——

5、训练

6、第一次后处理:组合

7、第二次后处理


1、数据查看

         在得到数据的时候,首先简单的查看一下数据的分布情况,明确一下手里有什么数据以及最后需要得到什么数据。

        读取数据查看数据信息及分布:

path_train = 'data/train.csv'
path_test = 'data/test.csv'train_data_set = pd.read_csv(path_train)
print(train_data_set.describe())
print(train_data_set.info())

 结果如下:

                 id     clonesize  ...         seeds         yield
count  15289.000000  15289.000000  ...  15289.000000  15289.000000
mean    7644.000000     19.704690  ...     36.164950   6025.193999
std     4413.698468      6.595211  ...      4.031087   1337.056850
min        0.000000     10.000000  ...     22.079199   1945.530610
25%     3822.000000     12.500000  ...     33.232449   5128.163510
50%     7644.000000     25.000000  ...     36.040675   6117.475900
75%    11466.000000     25.000000  ...     39.158238   7019.694380
max    15288.000000     40.000000  ...     46.585105   8969.401840

 #   Column                Non-Null Count  Dtype
---  ------                --------------  -----
 0   id                    15289 non-null  int64
 1   clonesize             15289 non-null  float64
 2   honeybee              15289 non-null  float64
 3   bumbles               15289 non-null  float64
 ......
 15  fruitmass             15289 non-null  float64
 16  seeds                 15289 non-null  float64
 17  yield                 15289 non-null  float64

        由上可知,我们的 train.csv 中共有18列数据,出去id数据之外,共有17列有用数据,且数据中均为浮点数类型,无空值数据,数据总个数为15289个。依据题意,我们需要基于前16中特征数据来预测第17个‘yiedl'字段的数据,也就是建立一个 16->1 的一个预测模型。

        测试样本中test.csv中数据结构与之相同,只是缺少了’yield‘字段数据,其数据个数有10194个。

        接下来就来学习一下得分最高的solution。

2、准备工作

        该solution使用的编辑器环境为notebook,导入需要的第三方库。

!pip install sklego -qimport pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from tqdm.auto import tqdm
import osimport lightgbm as lgbm
from lightgbm import log_evaluation, early_stopping, record_evaluation
from sklearn import metrics
from sklearn import model_selection, utils
from sklego.linear_model import LADRegressionimport warnings
warnings.filterwarnings('ignore')tqdm.pandas()rc = {"axes.facecolor": "#F8F8F8","figure.facecolor": "#F8F8F8","axes.edgecolor": "#000000","grid.color": "#EBEBE7" + "30","font.family": "serif","axes.labelcolor": "#000000","xtick.color": "#000000","ytick.color": "#000000","grid.alpha": 0.4
}sns.set(rc=rc)
palette = ['#302c36', '#037d97', '#E4591E', '#C09741','#EC5B6D', '#90A6B1', '#6ca957', '#D8E3E2']from colorama import Style, Fore
blk = Style.BRIGHT + Fore.BLACK
red = Style.BRIGHT + Fore.RED
gld = Style.BRIGHT + Fore.YELLOW
blu = Style.BRIGHT + Fore.BLUE
res = Style.RESET_ALL

        上述代码中:pandas、numpy、matplotlib为常用的数据分析及可视化第三方库,不多做介绍。seaborn是在matplotlib的基础上开发的数据可视化库。tqdm主要是用于在屏幕打印循环进度。lightgbm是一个组合模型的实现第三方库。sklearn中封装了常用的机器学习的算法、模型、预处理、评价标准等等。sklego也是一个机器学习第三方库,其中封装着很多机器学习模型和方法。

        其余部分主要是对可视化的一些颜色进行提前定义。

3、数据预处理

PATH_ORIGIN = '/kaggle/input/wild-blueberry-yield-prediction-dataset/WildBlueberryPollinationSimulationData.csv'
PATH_TRAIN = '/kaggle/input/playground-series-s3e14/train.csv'
PATH_TEST = '/kaggle/input/playground-series-s3e14/test.csv'
PATH_OOFS = '/kaggle/input/s3e14-oofs/pubs'
PATH_SUB = '/kaggle/input/playground-series-s3e14/sample_submission.csv'def mattop_post_process(preds):return np.array([min(unique_targets, key = lambda x: abs(x - pred)) for pred in preds])class DataProcessor:def __init__(self,train_data=None,test_data=None,combined: bool = False,verbose: bool = False):self.origin_data = Noneself.train_data = train_dataself.test_data = test_dataself.combined = combinedself.verbose = verbosedef load_data(self):if self.combined:# self.origin_data = pd.read_csv(PATH_ORIGIN).drop(columns='Row#').drop([766])self.train_data = pd.read_csv(PATH_TRAIN).drop(columns='id')self.test_data = pd.read_csv(PATH_TEST).drop(columns='id')if self.verbose:print(f'{gld}[INFO] Shapes before feature engineering:'f'{gld}\n[+] train  -> {red}{self.train_data.shape}'f'{gld}\n[+] test   -> {red}{self.test_data.shape}\n')@staticmethoddef fe(df):return dfdef process_data(self):self.load_data()self.train_data = self.fe(self.train_data)self.test_data = self.fe(self.test_data)if self.combined:cols = self.train_data.columnsself.origin_data = self.fe(self.origin_data)self.train_data = pd.concat([self.train_data, self.origin_data])self.train_data = self.train_data.reset_index(drop=True)if self.verbose:print(f'{gld}[INFO] Shapes after feature engineering:'f'{gld}\n[+] train  -> {red}{self.train_data.shape}'f'{gld}\n[+] test   -> {red}{self.test_data.shape}\n')return self.train_data, self.test_datadef xy_split(tr_df, te_df, target, cols_to_drop=[]):"""Preprocess the train and test data by dropping theeliminated columns and separating the target column.Args:tr_df: Train dataframe.te_df: Test dataframe.target: list of str (target name).cols_to_drop: list of str (columns to be eliminated).Returns:X_tr: X_train dataframe.y_tr: y_train pd.Series.te_df: test dataframe."""if cols_to_drop:X_tr = tr_df.drop(columns=target + cols_to_drop)y_tr = tr_df[target[0]]te_df = te_df.drop(columns=cols_to_drop)else:X_tr = tr_df.drop(columns=target)y_tr = tr_df[target[0]]return X_tr, y_tr, te_df

对上述代码进行分析理解,

第一部分是定义文件路径,略过。

第二部分是 mattop_post_process 函数的定义。如下:

def mattop_post_process(preds):return np.array([min(unique_targets, key = lambda x: abs(x - pred)) for pred in preds])

关于对min函数中key参数的解释,key通常表示一个函数,如下算例可以辅助了解:

print(min([2,3,4,5,6],key = lambda x:(x-1)*2.5))
# 表达对[2,3,4,5,6]这组数据中每个元素的lambda结果后的最小值
# 也即 min[lambda(2),lambda(3),lambda(4),lambda(5),lambda(6)]
# 最小为 lambda(2) 所以使得这个匿名函数最小的元素是2
# 输出 2

在理解完min函数中key值的用法之后,式中的unique_targets在后续的代码中会被定义为一个数组,是一个全局已知变量,该函数可以简化为

def mattop_post_process(preds):return np.array([min(...) for pred in preds])

       所以,该函数的作用是返回preds每一个元素与unique_target中所有元素距离(差值绝对值)的那个元素,也即寻找对某一个preds相差最小的那个unique_target.。

第三部分是 DataProcessor 类的定义:

        整体代码类定义如下:

class DataProcessor:def __init__(self,train_data=None,test_data=None,combined: bool = False,verbose: bool = False):self.origin_data = Noneself.train_data = train_dataself.test_data = test_dataself.combined = combinedself.verbose = verbosedef load_data(self):if self.combined:# self.origin_data = pd.read_csv(PATH_ORIGIN).drop(columns='Row#').drop([766])self.train_data = pd.read_csv(PATH_TRAIN).drop(columns='id')self.test_data = pd.read_csv(PATH_TEST).drop(columns='id')if self.verbose:print(f'{gld}[INFO] Shapes before feature engineering:'f'{gld}\n[+] train  -> {red}{self.train_data.shape}'f'{gld}\n[+] test   -> {red}{self.test_data.shape}\n')@staticmethoddef fe(df):return dfdef process_data(self):self.load_data()self.train_data = self.fe(self.train_data)self.test_data = self.fe(self.test_data)if self.combined:cols = self.train_data.columnsself.origin_data = self.fe(self.origin_data)self.train_data = pd.concat([self.train_data, self.origin_data])self.train_data = self.train_data.reset_index(drop=True)if self.verbose:print(f'{gld}[INFO] Shapes after feature engineering:'f'{gld}\n[+] train  -> {red}{self.train_data.shape}'f'{gld}\n[+] test   -> {red}{self.test_data.shape}\n')return self.train_data, self.test_data

        该类主要分为以下模块:

1、 def __init__(self,train_data=None,test_data=None,combined: bool = False,verbose: bool = False):

类自带的初始化函数,即当这个类实例化一个对象时会执行的函数操作。

本类别中一共定义了五个变量用来创建数据。

self.origin_data 表示原始数据(Kaggle上的原始数据来源,WildBlueberryPollinationSimulationData)

self.train_data 表示训练数据集(主要是从train.csv数据中读取)

self.test_data 表示测试数据即(从test.csv数据中提取)

self.combined bool类型数据,表示是否将原始数据和train.csv数据融合

self.verbose bool类型数据,表示是否将数据集信息输出到屏幕

2、def load_data(self):

        此函数的作用即是读取数据文件,将读取的数据保存到类的变量中。如果给的读入原始数据的为真,则将原始数据也读入保存到其中。

3、def fe(df):

           类的静态方法(所谓静态方法即不需要对该类进行实例化就能调用的的方法)

           该函数返回本身

4、def process_data(self):

      处理数据。如果要读入原始数据,则最后将原始数据融入到训练数据中。如果不融入原始数据则就读取train.csv即可。

        函数最后返回 训练数据 测试数据。(此处的训练数据和测试数据不是训练模型的训练集和测试集)

        该类的使用也很简单:

A = DataProcessor(*param)    # 设置参数实例化类
train_data,test_data = A.process_data()    # 调用方法

第四部分是 xy_split 函数的定义

函数定义如下:

def xy_split(tr_df, te_df, target, cols_to_drop=[]):"""Preprocess the train and test data by dropping theeliminated columns and separating the target column.Args:tr_df: Train dataframe.te_df: Test dataframe.target: list of str (target name).cols_to_drop: list of str (columns to be eliminated).Returns:X_tr: X_train dataframe.y_tr: y_train pd.Series.te_df: test dataframe."""if cols_to_drop:X_tr = tr_df.drop(columns=target + cols_to_drop)y_tr = tr_df[target[0]]te_df = te_df.drop(columns=cols_to_drop)else:X_tr = tr_df.drop(columns=target)y_tr = tr_df[target[0]]return X_tr, y_tr, te_df

对于该函数来说,需让传入四个参数:训练数据集、测试数据集、目标列、需要删除的列。

其完成的功能即:对于传入的数据(训练数据集、测试数据集),在删除不需要的数据之后(需要删除的列作为函数的参数传入)。将训练数据集划分为X数据集(也即代码中的X_tr)和y数据集(代码中的y_tr)。

4、模型定义

 首先是对LightGBM模型的定义

class LGBMCVModel:def __init__(self, cv, **kwargs):self.cv = cvself.model_params = kwargsself.models_ = list()self.feature_importances_ = Noneself.eval_results_ = dict()self.oof = Noneself.metric = maeself.tuning_step = Falseself.mean_cv_score = Noneself.general_config = Noneself.predictions = Noneself.target_name = Nonedef fit(self, X, y=None, **kwargs):feature_names = X.columns if isinstance(X, pd.DataFrame) else list(range(X.shape[1]))self.feature_importances_ = pd.DataFrame(index=feature_names)self.oof = np.zeros(len(X))for fold, (fit_idx, val_idx) in enumerate(self.cv.split(X, y), start=1):# Split the dataset according to the fold indexes.X_fit = X.iloc[fit_idx]X_val = X.iloc[val_idx]y_fit = y.iloc[fit_idx]y_val = y.iloc[val_idx]# LGBM .train() requires lightgbm.Dataset.# https://lightgbm.readthedocs.io/en/latest/Python-API.html#lightgbm.Datasetfit_set = lgbm.Dataset(X_fit, y_fit)val_set = lgbm.Dataset(X_val, y_val)# Training.# https://lightgbm.readthedocs.io/en/latest/Python-API.html#lightgbm.trainself.eval_results_[fold] = {}model = lgbm.train(params=self.model_params,train_set=fit_set,valid_sets=[fit_set, val_set],valid_names=['fit', 'val'],callbacks=[log_evaluation(0),record_evaluation(self.eval_results_[fold]),early_stopping(self.model_params['early_stopping_rounds'],verbose=False, first_metric_only=True)],**kwargs)val_preds = model.predict(X_val)self.oof[val_idx] += val_preds / self.general_config['N_REPEATS']if not self.tuning_step:val_score = self.metric(y_val, val_preds)best_iter = model.best_iterationprint(f'Fold: {blu}{fold:>3}{res}| {self.metric.__name__}: {blu}{val_score:.5f}{res}'f' | Best iteration: {blu}{best_iter:>4}{res}')# Stores the modelself.models_.append(model)self.mean_cv_score = self.metric(y, self.oof)print(f'{"*" * 50}\n{red}Mean{res} {self.metric.__name__}: {red}{self.mean_cv_score:.5f}')return selfdef predict(self, X):utils.validation.check_is_fitted(self, ['models_'])y = np.zeros(len(X))for model in tqdm(self.models_):y += model.predict(X)return y / len(self.models_)

这个类主要由三个函数组成,__init__(),fit(),predict(),其分别代表实例化对象时初始化方法拟合模型方法预测方法

__init__():实例化对象时的初始化方法,该方法主要是提前定义一些模型的参数及变量。

其定义的参数和变量如下:

self.cv = cv                # sklearn中的交叉验证
self.model_params = kwargs        # 模型参数
self.models_ = list()        # 来保存每次的模型
self.feature_importances_ = None        # 
self.eval_results_ = dict()              # 
self.oof = None        # 折外交叉验证的预测,即对剩下那一折进行预测
self.metric = mae      # 定义预测评价标准为MAE准则
self.tuning_step = False        # 
self.mean_cv_score = None        # 平均的交叉验证分数
self.general_config = None        # 一些
self.predictions = None            # 
self.target_name = None        # 目标列名字

——fit函数——

fit函数是整个类的主题,着重分析和介绍。

首先分析前三行:

feature_names = X.columns if isinstance(X, pd.DataFrame) else list(range(X.shape[1]))

# isinstance(X,classinfo) 函数,判断对象X是不是某个类别,本语句即分析X是否为pd.DataFrame。是的话feature_names是X的列名。否的话基于列的维度产生对应数组。

self.feature_importances_ = pd.DataFrame(index=feature_names)

# 按上行语句的列名创建一个对应行数的空DataFrame。即假设上个语句的结果(X的列名)为['a','b','c'],则这个语句就创建一个行索引为 'a' 'b' 'c' 的三行零列的空DataFrame
self.oof = np.zeros(len(X))

# 创建一个和X的行维度一致的一维0数组。即假设X为1000行50列,则该语句创建一个1000长度的1维(0初始化)数组

 其次是根据交叉验证分割的数据集进行训练,

        for fold, (fit_idx, val_idx) in enumerate(self.cv.split(X, y), start=1):# Split the dataset according to the fold indexes.X_fit = X.iloc[fit_idx]X_val = X.iloc[val_idx]y_fit = y.iloc[fit_idx]y_val = y.iloc[val_idx]fit_set = lgbm.Dataset(X_fit, y_fit)val_set = lgbm.Dataset(X_val, y_val)

 对于其交叉验证而言,其最终返回的是对应数据集的索引,根据索引得到本次的训练集和交叉验证集。随后调用lgbm.Dataset方法来组件lgbm模型的数据集。

       本文使用的是N次重复的KFOLD交叉验证方法。具体源码可见:

sklearn.model_selection.RepeatedKFold-scikit-learn中文社区https://scikit-learn.org.cn/view/642.html        由上文可知设置参数为3次重复的10择交叉验证,由此可知循环共会进行30次。后续代码即设置参数对lgbm模型进行训练。

            self.eval_results_[fold] = {}model = lgbm.train(params=self.model_params,train_set=fit_set,valid_sets=[fit_set, val_set],valid_names=['fit', 'val'],callbacks=[log_evaluation(0),record_evaluation(self.eval_results_[fold]),early_stopping(self.model_params['early_stopping_rounds'],verbose=False, first_metric_only=True)],**kwargs)val_preds = model.predict(X_val)self.oof[val_idx] += val_preds / self.general_config['N_REPEATS']if not self.tuning_step:val_score = self.metric(y_val, val_preds)best_iter = model.best_iterationprint(f'Fold: {blu}{fold:>3}{res}| {self.metric.__name__}: {blu}{val_score:.5f}{res}'f' | Best iteration: {blu}{best_iter:>4}{res}')# Stores the modelself.models_.append(model)

        如上文所述,后续对每择都进行了模型训练和评价,最后将30(重复3次的10择交叉验证)个训练好的模型都保存到了self.models_列表中。

        在进行模型预测时,使用30个模型逐一进行预测,最后预测值除以30记得到最终预测值,代码如下。

    def predict(self, X):utils.validation.check_is_fitted(self, ['models_'])y = np.zeros(len(X))for model in tqdm(self.models_):y += model.predict(X)return y / len(self.models_)

        本次所用的参数设置如下:

config = {"SEED": 42,"FOLDS": 10,"N_ESTIMATORS": 2000,"EXP_NUM": "2","COMBINE": True,"KFOLD": True,'N_REPEATS': 3,"COL_DROP": ["RainingDays"]}params = {"learning_rate": 0.04,"max_bin": 1000,"colsample_bytree": 0.8,"subsample": 0.7,"bagging_freq": 1,"objective": "regression_l1","metric": "mae","early_stopping_rounds": 200,"n_jobs": -1,"verbosity": -1}def mae(y_true, y_pred):return metrics.mean_absolute_error(y_true, y_pred)

5、训练

target = ['yield']# 获得数据集
f_e = DataProcessor(verbose=True, combined=config['COMBINE'])
train, test = f_e.process_data()# 将数据集分割为x和y(输入和对应的输出)
X_train, y_train, test = xy_split(train, test, target, cols_to_drop=config['COL_DROP'])# 定义N次重复的K择交叉验证
cv = model_selection.RepeatedKFold(n_repeats=config['N_REPEATS'],n_splits=config['FOLDS'],random_state=config['SEED'])# 实例化类及模型
lgbm_model = LGBMCVModel(cv=cv,  **params)# 参数设置
lgbm_model.tuning_step = False
lgbm_model.general_config = config
lgbm_model.target_name = target[0]# 拟合
lgbm_model.fit(X_train, y_train, num_boost_round=config['N_ESTIMATORS'])# 评估
mae_full = lgbm_model.mean_cv_score
mae_true = mae(y_train.iloc[:15289], lgbm_model.oof[:15289])
print(f'{mae_full=}\n{mae_true=}')

        将该模型的训练结果保存到本地

path_submissions = ''
# 定义实验名字
exp_num = '2'# 保存训练集的模型输出 即在train.csv数据中模型的预测输出
oof = pd.DataFrame(lgbm_model.oof[:15289], columns=['oof_preds'])
oof.to_csv(os.path.join(path_submissions, f'oof_{exp_num}.csv'), index=False)# 将模型测试结果保存到本地 (即对test.csv的模型预测输出)
# Saves the submission file.
submission = pd.read_csv(PATH_SUB)
if lgbm_model.predictions is None:predictions = lgbm_model.predict(test)
else:predictions = lgbm_model.predictions
submission[lgbm_model.target_name] = predictions
submission.to_csv(os.path.join(path_submissions, f'sub_{exp_num}.csv'), index=False)

6、第一次后处理:组合

        本节组合的主要思路是结合之前K择交叉的预测输出来矫正目前的模型预测值。

        之前10择交叉验证,那么就会有10个模型oof和10个sub数据。(oof是指:模型训练好后,对训练集的模型输出。sub是指:训练好后的模型在其他数据集上的预测结果)

        所以,目前我们拥有:11(10个择内,1个总和)个模型的oof,11(10个择内,1个总和)个模型的sub

        本次只选择了其中8个来进行分析:

for i in tqdm([1, 2, 4, 5, 6, 7, 8]):pub_oof = pd.read_csv(f'{PATH_OOFS}/p{i}_oof.csv').iloc[:15289, :]sub_oof = pd.read_csv(f'{PATH_OOFS}/p{i}_sub.csv')if MATT:oofs_df[f'p{i}'] = mattop_post_process(pub_oof.oof_preds)subs_df[f'p{i}'] = mattop_post_process(sub_oof[target_name[0]])else:oofs_df[f'p{i}'] = pub_oof.oof_predssubs_df[f'p{i}'] = sub_oof[target_name[0]]if MATT:oofs_df[f's{exp_num}'] = mattop_post_process(oof.oof_preds)subs_df[f's{exp_num}'] = mattop_post_process(submission[target_name[0]])
else: oofs_df[f's{exp_num}'] = oof.oof_predssubs_df[f's{exp_num}'] = submission[target_name[0]]for i, col in enumerate(oofs_df.columns):score = mae(train[target_name[0]], oofs_df[col])print(f'OOF {red}{mae.__name__.upper()}{res} for model {col}: {red}{score:.4f}{res}')

         我试着解释一下此举的含义:现在选择了8个择内oof,那么现在就共有9列数据(8个择内oof和一个总和oof),一个oof的维度是15289行(不加入原始的数据)1列,那么现在创建一个oofs_df的表单数据,维度为15289行9列,列名分别为p1~p8(代表8个择内)、s2(代表总和)。然后每列对应值是其对应列名的oof值经过mattop_post_process函数的结果。sub数据也是同理。

        经过上述之后,就会得到一个15289行9列的oofs_df的表单数据和10194行9列的subs_df的表单数据。

        然后将这15289行9列作为输入的特征数据,真值数据作为标签数据,建立回归模型。

LADRegression_blend = LADRegression(positive=True)
LADRegression_blend.fit(oofs_df, train[target_name[0]])
lad_score = mae(train[target_name[0]], LADRegression_blend.predict(oofs_df))
print(f"{blk}MAE with LAD Regression of OOFS predictions : {red}{lad_score}{blk}\n\nCoefficients :{res}")
display(pd.Series(LADRegression_blend.coef_.round(2), oofs_df.columns, name='weight'))

(LADRegression:Least absolute deviation Regression)

        得到模型的各个特征的回归方程系数:

         基于上述回归方程及其拟合系数,同样可以基于10194行9列的subs_df来得到拟合后的结果。

        以上为第一次结果的后处理

7、第二次后处理

        第二次后处理是将数据中的train.csv和test.csv中的两列数据做了精细的分析。随后对预测结果进行第二次后处理改正。

        其使用了fft.csv数据,其获得方式如下:

ffs = ['fruitset', 'fruitmass', 'id']# merges train and test and finds final common samples.
ids = train[ffs].merge(test[ffs], on=ffs[:-1], how='inner')['id_y'].unique()
final_samples = test[test.id.isin(ids)].drop_duplicates(subset=ffs[:-1])[ffs[:-1]]
train['pred'] = cop # already blended and corrected oofs up to mae 335.9858.
d = dict()VERBOSE = False# runs for loop to check what re-assigned value gives the bigger improvement 
# for out-of-fold predictions, in the end of the loop it sets values to default.
for i in tqdm(final_samples.values):best_mae = 335.9858for yl in sorted(train['yield'].unique()):train.loc[train['fruitset'].eq(i[0]) & train['fruitmass'].eq(i[1]), 'pred'] = ylsc = mae(train[target_name[0]], train['pred'])if sc < best_mae:best_mae = scd['_'.join(i.astype(str))] = [sc, yl]if VERBOSE and sc < 335.95:print(sc, yl)train['pred'] = cop

 第二次后处理代码:

def second_postprocessing(indx: str):"""Walks thru grouped ffs.csv file fetching `fruitmass_fruitset`as indx (0.334593591_0.379871916) and splits it.    The idea was to assign the best performing value (last in `grp`) from ffs.csvbut original value performed better and was robust.Args:indx: str of 'fruitmass_fruitset' (example: 0.334593591_0.379871916)Returns:tr_idx, te_idx, orig_value - the respective indexes of train/test `yeild` to be corrected.None if dangerous zone condition is not satisfied."""txt = indxtxt = list(map(float, txt.split('_')))dsp_origin = origin.query('fruitset == @txt[0] and fruitmass == @txt[1]')dsp_train = train.query('fruitset == @txt[0] and fruitmass == @txt[1]').iloc[:, 7:]dsp_test = test.query('fruitset == @txt[0] and fruitmass == @txt[1]').iloc[:, 6:]# Dangerous zone params:if len(dsp_train) > 2 and len(dsp_test) > 1:if not dsp_origin.empty:orig_value = dsp_origin['yield'].values[0]tr_idx = train.loc[train.fruitset.eq(txt[0]) & train.fruitmass.eq(txt[1]), 'pred'].index.tolist()te_idx = test.loc[test.fruitset.eq(txt[0]) & test.fruitmass.eq(txt[1]), 'pred'].index.tolist()return tr_idx, te_idx, orig_valueelse:return Nonetridx_teidx_value_to_assign = Parallel(n_jobs=-1)(delayed(second_postprocessing)(indx) for indx in tqdm(grp.index))
# Removes Nones from the list.
tridx_teidx_value_to_assign = [i for i in tridx_teidx_value_to_assign if i is not None]# Corrects the yield values based on the found original values
num_of_tr_rows_affected = 0
num_of_te_rows_affected = 0
for i in tridx_teidx_value_to_assign:tr_idx, te_idx, orig_value = itrain.loc[tr_idx, 'pred'] = orig_valuetest.loc[te_idx, 'pred'] = orig_valuenum_of_tr_rows_affected += len(tr_idx)num_of_te_rows_affected += len(te_idx)new_score = mae(train[target_name[0]], train['pred'])
print(f'{red}MAE {blk}dangerous zone:        {red}{new_score:.4f}{res}')
print(f'{blk}Number of rows affected train: {red}{num_of_tr_rows_affected}{res}')
print(f'{blk}Number of rows affected test:   {red}{num_of_te_rows_affected}{res}')

余下部分均是模型结果验证及结果可视化部分。可自行学习。


才疏学浅,敬请指正!

共勉。


需要完整可运行代码可邮件联系(邮箱:rton.xu@qq.com,QQ:2264787072

有任何疑问和建议欢迎交流,静候。

相关文章:

Kaggle First Place Winner Solution Study——多变量回归问题

本期分享一个Kaggle上playground系列多变量回归问题的第一名解决方案。试着分析、复现、学习一下金牌选手的数据分析思路。 赛题链接&#xff1a; Prediction of Wild Blueberry Yield | Kagglehttps://www.kaggle.com/competitions/playground-series-s3e14第一名解决方案链…...

分布式应用:Zookeeper 集群与kafka 集群部署

目录 一、理论 1.Zookeeper 2.部署 Zookeeper 集群 3.消息队列 4.Kafka 5.部署 kafka 集群 6.FilebeatKafkaELK 二、实验 1.Zookeeper 集群部署 2.kafka集群部署 3.FilebeatKafkaELK 三、问题 1.解压文件异常 2.kafka集群建立失败 3.启动 filebeat报错 4.VIM报错…...

Last-Mile Embodied Visual Navigation 论文阅读

论文阅读 题目&#xff1a;Last-Mile Embodied Visual Navigation 作者&#xff1a;JustinWasserman, Karmesh Yadav 来源&#xff1a;CoRL 时间&#xff1a;2023 代码地址&#xff1a;https://jbwasse2.github.io/portfolio/SLING Abstract 现实的长期任务&#xff08;例如…...

thunder gbm

文章目录 背景参考官网信息训练调参模型保存推理 背景 想在 GPU 上使用使用闪电般快速的提升方法&#xff1f;了解这个库就好了。在很多任务上&#xff0c;它都比 LightGBM 和 XGBoost 快。 ThunderGBM 的主要特征如下&#xff1a; 通常是其它库的 10 倍。 支持 Python&#x…...

数据结构--单链表

前言 上一章&#xff0c;我们讲了数据结构--动态顺序表&#xff0c;我们会发现有以下问题&#xff1a; 1.当我们要头部或者插入或删除时&#xff0c;都需要进行位置挪动&#xff0c;腾出某一个位置&#xff0c;时间复杂度为0(N)&#xff1b; 2.增容需要申请新空间&#xff0c;…...

过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程

Ⅰ、准备工作&#xff1a; 1、Git 查看&#xff1a; 其一、命令&#xff1a;git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git &#xff1a; A、命令 &#xff1a; sudo apt-get install git B、然后再输入虚…...

机器学习笔记之优化算法(九)收敛速度的简单认识

机器学习笔记之优化算法——收敛速度的简单认识 引言收敛速度的判别标准 Q \mathcal Q Q-收敛速度 R \mathcal R R-收敛速度关于算法复杂度与收敛速度 引言 本节对收敛速度简单介绍。 收敛速度的判别标准 我们之前几节介绍了线搜索方法 ( Line Search Method ) (\text{Line …...

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…...

经纬度坐标工具

LngLatUtil :用于计算里程数 import cn.hutool.core.util.ArrayUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.Getter; import lombok.Setter;import java.io.FileInputStream; import java.io.Serializable; import java.t…...

如何使用伪元素::before和::after?

伪元素(::before和::after)是CSS中非常有用的特性&#xff0c;它们允许你在元素的内容之前或之后插入额外的内容&#xff0c;并且不需要在HTML结构中添加额外的标记。这样可以方便地在页面上添加装饰性元素、图标、或者样式效果。以下是使用伪元素的基本方法&#xff1a; 1、创…...

Visual Studio Code中对打开的脚本格式统一

什么是Language Server Protocol (LSP)? Language Server Protocol&#xff08;语言服务器协议&#xff0c;简称LSP&#xff09;是微软在2016年提出的一套统一的通讯协议方案。LSP定义了一套编辑器或者IDE与语言服务器&#xff08;Language Server&#xff09;之间使用的协议&…...

补充JDK源码-IDEA集成工具

在阅读JDK8源码的时候发现&#xff0c;只有一小部分常用包是存在源码及其注释的&#xff0c;而很多内部包是没有源码&#xff0c;class文件在阅读的时候对阅读者十分不友好。在网上搜集了很多资料都没有解决问题。 解决问题办法&#xff1a;参考文档。本文主要是根据这篇文章记…...

Git Submodule 更新子库失败 fatal: Unable to fetch in submodule path

编辑本地目录 .git/config 文件 在 [submodule “Assets/CommonModule”] 项下 加入 fetch refs/heads/:refs/remotes/origin/...

Springboot切面打印日志

切面打印完整日志,以下代码用于扫描RestController 注解修饰的接口,并打印相关日志 import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; impor…...

ubuntu上回环设备/dev/loop0占用100%清理

查看磁盘占用情况时&#xff1a; df -h/dev/loopn这些设备在Linux下被称为回环设备。 终端输入&#xff1a; sudo apt autoremove --purge snapd再次查看&#xff1a;...

List list=new ArrayList()抛出的ArrayIndexOutOfBoundsException异常

1.应用场景&#xff0c;今天生产日志监控到一下ArrayList 进行add 异常&#xff0c;具体日志如下&#xff1a; eptionHandler.handler(178): TXXYBUSSINESS|执行异常 java.util.concurrent.CompletionException: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bo…...

桶排序算法

桶排序算法 算法思想概述&#xff1a;桶排序的主要步骤如下&#xff1a; 算法goland实现&#xff1a;图解演示&#xff1a; 算法思想概述&#xff1a; 桶排序&#xff08;Bucket Sort&#xff09;是一种非比较性的排序算法&#xff0c;它将待排序的元素分到有限数量的桶&#…...

P8604 [蓝桥杯 2013 国 C] 危险系数

题目背景 抗日战争时期&#xff0c;冀中平原的地道战曾发挥重要作用。 题目描述 地道的多个站点间有通道连接&#xff0c;形成了庞大的网络。但也有隐患&#xff0c;当敌人发现了某个站点后&#xff0c;其它站点间可能因此会失去联系。 我们来定义一个危险系数 DF(x,y)&…...

Excel·VBA表格横向、纵向相互转换

如图&#xff1a;对图中区域 A1:M6 横向表格&#xff0c;转换成区域 A1:C20 纵向表格&#xff0c;即 B:M 列转换成每2列一组按行写入&#xff0c;并删除空行。同理&#xff0c;反向操作就是纵向表格转换成横向表格 目录 横向转纵向实现方法1转换结果 实现方法2转换结果 纵向转横…...

Leetcode-每日一题【剑指 Offer 06. 从尾到头打印链表】

题目 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,3,2]输出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 链表长度 < 10000 解题思路 1.题目要求我们从尾到头反过…...

LeetCode--HOT100题(22)

目录 题目描述&#xff1a;160. 相交链表&#xff08;简单&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;160. 相交链表&#xff08;简单&#xff09; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表…...

产品体系架构202308版

1.前言 当我们不断向前奔跑时&#xff0c;需要回头压实走过的路。不断扩张的同时把相应的内容沉淀下来&#xff0c;为后续的发展铺垫基石。 不知从何时起&#xff0c;产品的架构就面向了微服务/中台化/前后端分离/低代码化/分布式/智能化/运行可观测化的综合体&#xff0c;让…...

Linux systemctl 简单介绍与使用

在Linux下&#xff0c;systemctl是一个管理系统服务的命令。它提供了对systemd服务的控制和管理。 在系统中使用systemctl命令&#xff0c;您可以执行以下操作&#xff1a; 启动服务&#xff1a;systemctl start servicename停止服务&#xff1a;systemctl stop servicename重…...

恺英网络宣布:与华为鸿蒙系统展开合作,将开发多款手游

8月5日消息&#xff0c;恺英网络宣布旗下子公司盛和网络参加了华为开发者大会&#xff08;HDC.Together&#xff09;游戏服务论坛&#xff0c;并在华为鸿蒙生态游戏先锋合作启动仪式上进行了亮相。恺英网络表示&#xff0c;将逐步在HarmonyOS上开发多款游戏&#xff0c;利用Har…...

Vue CORS

使用Vue框架报错&#xff0c;客户端浏览器有CORS错误&#xff0c;怎么解决&#xff1f; 参考API Proxying During Development&#xff0c;可以新增或修改config/index.js下的proxyTable属性。 留意到 proxyTable的key值为/api&#xff0c;代表所有服务端域名都改成以/api开头…...

Godot 4 源码分析 - 文件读入编码处理

今天需要读入xml文件进行处理&#xff0c;结果读入一个带中文的文件时&#xff0c;出错了。当然程序还能运行&#xff0c;但编译器一直报错&#xff0c;而且XML解析也不正确 单步调试发现读入的内容出现乱码&#xff0c;具体逻辑&#xff1a; String FileAccess::get_as_text…...

Linux 中使用 verdaccio 搭建私有npm 服务器

安装 Node Linux中安装Node 安装verdaccio npm i -g verdaccio安装完成 输入verdaccio,出现下面信息代表安装成功&#xff0c;同时输入verdaccio后verdaccio已经处于运行状态&#xff0c;当然这种启动时暂时的&#xff0c;我们需要通过pm2让verdaccio服务常驻 ygiZ2zec61wsg…...

C++入门之stl六大组件--stack和queue源码深度剖析及模拟实现

目录 前言 一、stack的介绍和使用 1.stack的介绍 2.stack的使用 3.stack的模拟实现 二、queue的介绍和使用 1.queue的介绍 2.queue的使用 3.queue的模拟实现 三、priority_queue的介绍和使用 1.priority_queue的介绍 2.priority_queue的使用 3.priority_queue的模…...

MyCat配置文件schema.xml讲解

1.MyCat配置 1.1 schema标签 如果checkSQLschema配置的为false&#xff0c;那么执行DB01.TB_ORDER时就会报错&#xff0c;必须用use切换逻辑库以后才能进行查询。 sqlMaxLimit如果未指定limit进行查询&#xff0c;列表查询模式默认为100,最多只查询100条。因为用mycat后默认数…...

Grafana集成prometheus(2.Grafana安装)

查找镜像 docker search grafana下载指定版本 docker pull grafana/grafana:10.0.1启动容器脚本 docker run -d -p 3000:3000 --namegrafana grafana/grafana:10.0.1查看是否启动 docker ps防火墙开启 检查防火墙3000端口是否开启 默认用户及密码 admin/admin 登录 ht…...