Skip to main content

tidymodels-review-patterns

Review patterns for tidymodels workflows, including leakage, resampling, tuning, metrics, and reproducibility.

跳到安装

来源信息

仓库
choxos/BiostatAgent
最近来源活动
2026年5月27日 20:44
检测到的 SKILL.md 语言
英语
星标
11
分支
1

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
tidymodels-review-patterns
description
Review patterns for tidymodels workflows, including leakage, resampling, tuning, metrics, and reproducibility.
# Tidymodels Code Review Patterns ## Overview Anti-pattern detection and best practices for tidymodels workflows based on "Tidy Modeling with R" (TMwR) principles. This skill enables systematic code review for data leakage, resampling violations, workflow issues, evaluation problems, and reproducibility concerns. ## Data Leakage Patterns (CRITICAL) ### DL-001: Recipe Fitted on Test Data **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Fitting recipe on test data rec <- recipe(outcome ~ ., data = test_data) |> prep() # WRONG: prep() using test data rec <- recipe(outcome ~ ., data = train_data) |> prep(training = test_data) ``` **Correct Pattern**: ```r # CORRECT: Recipe always prepped on training data only rec <- recipe(outcome ~ ., data = train_data) |> prep(training = train_data) # BEST: Use workflow (handles automatically) wf <- workflow() |> add_recipe(rec) |> add_model(model_spec) fit <- fit(wf, data = train_data) ``` **Detection**: Look for `prep()` calls with test data or recipes defined on test sets. --- ### DL-002: Preprocessing Before Split **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Normalizing before splitting df_normalized <- df |> mutate(across(where(is.numeric), scale)) split <- initial_split(df_normalized) # WRONG: Feature selection before split important_vars <- df |> select(where(~cor(.x, df$outcome) > 0.3)) split <- initial_split(important_vars) ``` **Correct Pattern**: ```r # CORRECT: Split first, then preprocess in recipe split <- initial_split(df, strata = outcome) train_data <- training(split) rec <- recipe(outcome ~ ., data = train_data) |> step_normalize(all_numeric_predictors()) |> step_corr(all_numeric_predictors(), threshold = 0.9) ``` **Detection**: Any transformations (scale, normalize, mutate) applied before `initial_split()`. --- ### DL-003: Target Encoding Without Cross-Validation **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Target encoding using full dataset statistics rec <- recipe(outcome ~ ., data = train_data) |> step_lencode_mixed(all_nominal_predictors(), outcome = vars(outcome)) # Then prep without proper CV prepped <- prep(rec) ``` **Correct Pattern**: ```r # CORRECT: Target encoding within workflow with resampling rec <- recipe(outcome ~ ., data = train_data) |> step_lencode_mixed(all_nominal_predictors(), outcome = vars(outcome)) wf <- workflow() |> add_recipe(rec) |> add_model(model_spec) # Encoding computed fresh for each fold cv_results <- fit_resamples(wf, resamples = vfold_cv(train_data)) ``` **Detection**: `step_lencode_*` or `step_embed` used outside workflow with resampling. --- ### DL-004: Feature Selection Using Test Data **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Selecting features based on test correlations test_cors <- cor(test_data[, -1], test_data$outcome) selected_vars <- names(test_cors[abs(test_cors) > 0.3]) # WRONG: Using test data for variable importance importance <- varImp(model, data = test_data) ``` **Correct Pattern**: ```r # CORRECT: Feature selection in recipe (computed on training only) rec <- recipe(outcome ~ ., data = train_data) |> step_select_vip(all_predictors(), outcome = "outcome", threshold = 0.8) # CORRECT: Or use recursive feature elimination with CV rfe_results <- rfe_fit( wf, resamples = vfold_cv(train_data), sizes = c(5, 10, 15, 20) ) ``` **Detection**: Variable selection operations referencing test data. --- ### DL-005: prep() Called Before initial_split() **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Prepping recipe on full data before split full_rec <- recipe(outcome ~ ., data = full_data) |> step_normalize(all_numeric_predictors()) |> prep() # Then splitting split <- initial_split(full_data) ``` **Correct Pattern**: ```r # CORRECT: Always split first split <- initial_split(full_data, strata = outcome) train_data <- training(split) rec <- recipe(outcome ~ ., data = train_data) |> step_normalize(all_numeric_predictors()) # Don't prep manually - let workflow handle it ``` **Detection**: Sequence analysis - `prep()` appearing before `initial_split()`. --- ## Resampling Violations (MAJOR/CRITICAL) ### RS-001: Missing Stratified Sampling **Severity**: MAJOR (CRITICAL for imbalanced data) **Anti-Pattern**: ```r # WRONG: No stratification for imbalanced outcome split <- initial_split(df) # outcome is 95%/5% imbalanced # WRONG: Unstratified CV folds <- vfold_cv(train_data, v = 10) ``` **Correct Pattern**: ```r # CORRECT: Stratify by outcome split <- initial_split(df, strata = outcome) # CORRECT: Stratified CV folds <- vfold_cv(train_data, v = 10, strata = outcome) # CORRECT: For continuous outcomes, stratify by bins split <- initial_split(df, strata = outcome, breaks = 4) ``` **Detection**: Missing `strata =` argument with classification outcomes or highly skewed continuous outcomes. --- ### RS-002: Evaluating Model on Training Data **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Predictions on training data for evaluation fit <- fit(wf, data = train_data) preds <- predict(fit, train_data) metrics <- yardstick::metrics(preds, truth = outcome, estimate = .pred) ``` **Correct Pattern**: ```r # CORRECT: Evaluate on held-out test data fit <- fit(wf, data = train_data) preds <- predict(fit, test_data) metrics <- yardstick::metrics( bind_cols(test_data, preds), truth = outcome, estimate = .pred ) # BEST: Use resampling for robust estimates cv_results <- fit_resamples(wf, resamples = folds) collect_metrics(cv_results) ``` **Detection**: `predict()` and metrics computed on same data used for `fit()`. --- ### RS-003: Tuning Without Nested Cross-Validation **Severity**: MAJOR **Anti-Pattern**: ```r # WRONG: Tune on same folds used for final evaluation folds <- vfold_cv(train_data) tune_results <- tune_grid(wf, resamples = folds) best_params <- select_best(tune_results) # Using same folds for "final" evaluation final_wf <- finalize_workflow(wf, best_params) fit_resamples(final_wf, resamples = folds) # Overly optimistic! ``` **Correct Pattern**: ```r # CORRECT: Nested CV or separate test set # Option 1: Hold out test set for final evaluation split <- initial_split(df, strata = outcome) train_data <- training(split) test_data <- testing(split) inner_folds <- vfold_cv(train_data, strata = outcome) tune_results <- tune_grid(wf, resamples = inner_folds) # Final evaluation on untouched test set final_fit <- fit(finalize_workflow(wf, select_best(tune_results)), train_data) predict(final_fit, test_data) # Option 2: Nested CV outer_folds <- nested_cv(train_data, outside = vfold_cv(v = 5), inside = vfold_cv(v = 5)) ``` **Detection**: Same resampling object used for both tuning and final evaluation. --- ### RS-004: Missing Random Seeds **Severity**: MAJOR **Anti-Pattern**: ```r # WRONG: No seed before random operations split <- initial_split(df) # Non-reproducible folds <- vfold_cv(train_data) # Different each run boot <- bootstraps(train_data) # Non-reproducible ``` **Correct Pattern**: ```r # CORRECT: Set seed before each random operation set.seed(123) split <- initial_split(df, strata = outcome) set.seed(456) folds <- vfold_cv(training(split), strata = outcome) # Or use tidymodels control options ctrl <- control_resamples(save_pred = TRUE) ``` **Detection**: Random operations (`initial_split`, `vfold_cv`, `bootstraps`, `mc_cv`) without preceding `set.seed()`. --- ### RS-005: Validation Set Reuse **Severity**: CRITICAL **Anti-Pattern**: ```r # WRONG: Using validation set multiple times for decisions val_split <- validation_split(train_data) # First use: model selection results1 <- fit_resamples(wf1, val_split) results2 <- fit_resamples(wf2, val_split) # Choose wf1 based on validation # Second use: hyperparameter tuning tune_results <- tune_grid(wf1, val_split) # Choose best params based on same validation set # Third use: final "evaluation" on same validation final_results <- fit_resamples(final_wf, val_split) # Overfit to validation! ``` **Correct Pattern**: ```r # CORRECT: Use CV for development, hold out final test split <- initial_split(df, strata = outcome) train_data <- training(split) test_data <- testing(split) # Touch only ONCE at the end # Use CV for all development decisions folds <- vfold_cv(train_data, strata = outcome) # Model selection via CV results1 <- fit_resamples(wf1, folds) results2 <- fit_resamples(wf2, folds) # Tuning via CV tune_results <- tune_grid(wf1, folds) # Final evaluation on test_data (only once!) final_fit <- last_fit(final_wf, split) ``` **Detection**: Same validation/test split used in multiple `fit_resamples()` or `tune_grid()` calls. --- ## Workflow Issues (MINOR/MAJOR) ### WF-001: Not Using Workflows **Severity**: MINOR to MAJOR **Anti-Pattern**: ```r # WRONG: Manual prep/bake/fit rec <- recipe(outcome ~ ., data = train_data) |> step_normalize(all_numeric_predictors()) |> prep() train_baked <- bake(rec, new_data = NULL) test_baked <- bake(rec, new_data = test_data) model <- linear_reg() |> set_engine("lm") fit <- fit(model, outcome ~ ., data = train_baked) preds <- predict(fit, test_baked) ``` **Correct Pattern**: ```r # CORRECT: Use workflow wf <- workflow() |> add_recipe(recipe(outcome ~ ., data = train_data) |> step_normalize(all_numeric_predictors())) |> add_model(linear_reg() |> set_engine("lm")) fit <- fit(wf, data = train_data) preds <- predict(fit, new_data = test_data) ``` **Benefits of workflows**: - Automatic handling of preprocessing on new data - Proper integration with tuning and resampling - Cleaner code organization - Reduced risk of data leakage --- ### WF-002: Inconsistent Preprocessing Train/Test **Severity**: MAJOR **Anti-Pattern**: ```r # WRONG: Different preprocessing for train vs test train_processed <- train_data |> mutate(across(where(is.numeric), ~(.x - mean(.x)) / sd(.x))) test_processed <- test_data |>
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看