Outputs you create
cv_results- Dataframe indexed by the requested model IDs, with
initial_score,selected_scoreandsettingscolumns. - Scores are observed mean training-fold scores;
settingsdescribes the chosen recipe.
- Dataframe indexed by the requested model IDs, with
reference_resultscross_validateresult for the dummy reference; test_score contains five scores.
chosen_name- Model ID nominated from training evidence.
residuals- Training-only
actual,predictedandactual-minus-predictedresidualevidence.
- Training-only
final_rmse- Root mean squared error in original target units.
competitorname | chocolate | fruity | caramel | peanutyalmondy | nougat | crispedricewafer | hard | bar | pluribus | sugarpercent | pricepercent | winpercent |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 100 Grand | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0.732 | 0.86 | 66.9717 |
| 3 Musketeers | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0.604 | 0.511 | 67.6029 |
| One dime | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.011 | 0.116 | 32.2611 |
| One quarter | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.011 | 0.511 | 46.1165 |
| Air Heads | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.906 | 0.511 | 52.3415 |
| Almond Joy | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0.465 | 0.767 | 50.3475 |
| Baby Ruth | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0.604 | 0.767 | 56.9145 |
| Boston Baked Beans | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0.313 | 0.511 | 23.4178 |
Your task
- Compare mixed-input linear regression and a tree on Candy with common training
folds, a reference, diagnosis and final evidence.
cv_resultsreference_resultschosen_nameresidualsfinal_rmseHints
Think: Reconstruct a fair comparison of two different flexibility assumptions.
Tools: Common KFold, mixed pipelines, tree-depth search, OOF residuals and final RMSE.
Approach: Evaluate both candidates and the reference, tune only the declared tree grid, nominate from training evidence, diagnose and finish.
Explained solution
from sklearn.base import clone
from sklearn.model_selection import train_test_split, KFold, cross_validate, cross_val_predict, GridSearchCV
from sklearn.dummy import DummyRegressor
from sklearn.metrics import root_mean_squared_error
import matplotlib.pyplot as plt
import seaborn as sns
X=df[['sugarpercent', 'pricepercent', 'chocolate', 'fruity', 'caramel', 'peanutyalmondy', 'nougat', 'crispedricewafer', 'hard', 'bar', 'pluribus']]
y=df['winpercent']
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=.2,random_state=42)
folds=KFold(n_splits=5,shuffle=True,random_state=42)
candidates={}
grids={}
# Prepare inputs and construct the candidate pipeline.
preprocessor = 'passthrough'
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LinearRegression
model = LinearRegression()
pipeline = Pipeline([('prepare', preprocessor), ('model', model)])
model = pipeline
candidates['multiple_linear']=model
grids['multiple_linear']=None
# Prepare inputs and construct the candidate pipeline.
preprocessor = 'passthrough'
from sklearn.pipeline import Pipeline
from sklearn.tree import DecisionTreeRegressor
model = DecisionTreeRegressor(random_state=42)
pipeline = Pipeline([('prepare', preprocessor), ('model', model)])
model = pipeline
candidates['regression_tree']=model
grids['regression_tree']={'model__max_depth': [3, 5, None]}
# Validate initial candidates on matching training folds.
initial_results={name:cross_validate(candidate,X_train,y_train,cv=folds,scoring='neg_root_mean_squared_error') for name,candidate in candidates.items()}
# Compare with a simple reference before tuning.
reference_results=cross_validate(DummyRegressor(strategy='mean'),X_train,y_train,cv=folds,scoring='neg_root_mean_squared_error')
rows=[]
selected={}
for name,candidate in candidates.items():
initial=initial_results[name]
grid=grids[name]
if grid:
search=GridSearchCV(candidate,grid,cv=folds,scoring='neg_root_mean_squared_error',n_jobs=1)
search.fit(X_train,y_train)
selected[name]=search.best_estimator_
validation_score=float(search.best_score_)
settings=search.best_params_
else:
selected[name]=candidate
validation_score=float(np.mean(initial['test_score']))
settings='Keep defaults: no production search'
rows.append({'model':name,'initial_score':float(np.mean(initial['test_score'])),'selected_score':validation_score,'settings':str(settings)})
cv_results=pd.DataFrame(rows).set_index('model')
# This solution nominates by mean training-fold score. Other evidence-based choices can be defensible.
chosen_name=cv_results.selected_score.idxmax()
chosen=selected[chosen_name]
oof_predictions=cross_val_predict(chosen,X_train,y_train,cv=folds)
diagnostic_y=y_train
residuals=pd.DataFrame({'actual':diagnostic_y,'predicted':oof_predictions,'residual':diagnostic_y-oof_predictions})
fig,ax=plt.subplots(figsize=(6,4))
sns.scatterplot(x=residuals.predicted,y=residuals.residual,s=12,ax=ax)
ax.axhline(0,color='black')
ax.set(xlabel='Training-validation prediction',ylabel='Residual',title='Training-only residuals')
fig.tight_layout()
fig.savefig('diagnostic.png',dpi=150,bbox_inches='tight')
final_model=clone(chosen).fit(X_train,y_train)
final_predictions=final_model.predict(X_test)
final_rmse=root_mean_squared_error(y_test,final_predictions)
print('Final RMSE:',final_rmse)
The matched design isolates candidate differences; original-unit residuals qualify the nomination before a single final evaluation of the selected workflow.