# ml pack 20 nodes, in `noodlelab[ml]` and in the tiers that build on it. ## Machine Learning/Data (node-ml.practice_data)= ### Practice Data `ml.practice_data` A made-up table to learn on, drawn from a known rule plus noise. ``beams``: a beam's deflection (mm) from its load (kN) and length (mm), close to a straight line: for regression. ``welds``: welding settings and whether the weld passed inspection; it fails when the heat per millimetre of plate is too low or too high: for classification. ``machines``: sensor readings from machines in three operating modes, with 2% faults mixed in and no labels: for principal components, clustering and anomaly detection. **Inputs** | Name | Type | Description | |---|---|---| | `dataset` | `Literal['beams', 'welds', 'machines']` | Default `'beams'`. | | `rows` | `int` | Default `500`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-ml.train_test_split)= ### Train/Test Split `ml.train_test_split` Rows chosen at random (with the seed) for testing, the rest for training. Judge a model only on rows it never saw: a score on its own training rows flatters it. ``group`` keeps repeats of one thing on one side, so the test is not passed by recognising them. The split keeps the tables' units and descriptions. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `test_share` | `float` | Share of rows held out Default `0.25`. | | `stratify` | `str` | Keep this column's classes in proportion; empty: no Default `''`. | | `group` | `str` | Rows with the same value go to the same side (a specimen, a site) Default `''`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `train` | `DataFrame` | | | `test` | `DataFrame` | | ## Machine Learning/Evaluate (node-ml.cross_validate)= ### Cross-Validate `ml.cross_validate` Refit the model on all but one fold of the rows and score it on that fold, for each fold in turn: a steadier judgement than one split. The model is refitted exactly as it was fitted (same settings, preprocessing and early stopping), and its own fit is left as it is. **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `table` | `DataFrame` | | | `folds` | `int` | Parts the rows are split into Default `5`. | | `metric` | `Literal['auto', 'rmse', 'mae', 'r2', 'accuracy', 'f1']` | Default `'auto'`. | | `group` | `str` | Rows with the same value stay in one fold (e.g. a specimen); empty: none Default `''`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `score` | `Uncertain` | | | `folds` | `DataFrame` | | | `summary` | `dict[str, Any]` | | (node-ml.evaluate_model)= ### Evaluate Model `ml.evaluate_model` How well a model predicts a table it was not trained on (the test part of Train/Test Split): for a regressor the RMSE and MAE in the target's unit, R², and a plot of predicted against true values; for a classifier the accuracy, the F1 score (the mean of each class's F1, so a rare class counts as much as a common one), and the confusion matrix. Metrics that do not apply are None (NaN would say something went wrong). **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `table` | `DataFrame` | | **Outputs** | Name | Type | Description | |---|---|---| | `rmse` | `Any` | | | `mae` | `Any` | | | `r_squared` | `float \| None` | | | `accuracy` | `float \| None` | | | `f1` | `float \| None` | | | `confusion` | `DataFrame` | | | `chart` | `Figure` | | | `summary` | `dict[str, Any]` | | (node-ml.learning_curve)= ### Learning Curve `ml.learning_curve` The cross-validated score for growing shares of the training rows: whether more data would help (the validation score still rising) or the model is as good as it gets (the two curves have met). **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `table` | `DataFrame` | | | `folds` | `int` | Parts the rows are split into Default `5`. | | `steps` | `int` | Training sizes to try Default `6`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `chart` | `Figure` | | (node-ml.permutation_importance)= ### Permutation Importance `ml.permutation_importance` How much worse the model predicts a table when one feature's values are shuffled, breaking its link to the target: the drop in R² (or accuracy), mean and standard deviation over ``repeats`` shuffles. Works for every model and on the original columns (a text column counts once), unlike a tree's own importance. Use a held-out table, or it measures what the model memorised. **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `table` | `DataFrame` | | | `repeats` | `int` | Default `10`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `chart` | `Figure` | | (node-ml.predict)= ### Predict `ml.predict` The model's predictions for each row of a table with the feature columns (the target column is not needed). Columns in other units of the same dimension are converted to the ones the model was trained on. **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `table` | `DataFrame` | | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `values` | `Any` | | | `probabilities` | `DataFrame` | | ## Machine Learning/Models (node-ml.gaussian_process)= ### Gaussian Process `ml.gaussian_process` A Gaussian process regressor: a smooth model that knows how sure it is. Predict Values gives each prediction with its standard uncertainty (as an uncertain value, correlated between nearby points), which grows away from the training data. The kernel is a constant times an RBF with one length scale per feature (so it learns which features matter), plus white noise when ``noise`` is on. Suited to surrogate models of a few hundred to a few thousand runs; the fit grows with the cube of the rows. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `noise` | `bool` | The target has scatter (measurements), not exact values Default `True`. | | `restarts` | `int` | Extra starts of the kernel fit Default `2`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.gradient_boosting)= ### Gradient Boosting `ml.gradient_boosting` scikit-learn's histogram gradient boosting: trees added one at a time, each correcting the ones before. Takes gaps in the data as they are. See also the XGBoost and LightGBM nodes. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `iterations` | `int` | Default `200`. | | `learning_rate` | `float` | Default `0.1`. | | `max_depth` | `int` | Deepest tree; 0: no limit Default `0`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.lightgbm)= ### LightGBM `ml.lightgbm` Gradient-boosted trees with LightGBM, which grows each tree leaf by leaf: fast on large tables. The outputs are those of the XGBoost node. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `trees` | `int` | Most trees to add Default `500`. | | `learning_rate` | `float` | Shrinks each tree Default `0.05`. | | `leaves` | `int` | Most leaves per tree Default `31`. | | `subsample` | `float` | Share of rows each tree sees Default `1.0`. | | `early_stopping` | `int` | Stop after this many trees without improvement; 0: off Default `20`. | | `validation` | `float` | Share of rows held back to stop on Default `0.2`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `importance` | `DataFrame` | | | `best_iteration` | `int` | | | `summary` | `dict[str, Any]` | | (node-ml.linear_model)= ### Linear Model `ml.linear_model` A straight-line (linear) model of a number: ordinary least squares, ridge (shrinks the coefficients: steadier with correlated features) or lasso (sets some to zero: picks features). ``strength`` is the penalty of ridge and lasso, on standardised features. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `method` | `Literal['least squares', 'ridge', 'lasso']` | Default `'least squares'`. | | `strength` | `float` | Regularisation: larger is simpler Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.logistic_regression)= ### Logistic Regression `ml.logistic_regression` A linear classifier: the probability of each class from a weighted sum of the (standardised) features. ``strength`` is the L2 penalty (1/C). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `strength` | `float` | Regularisation: larger is simpler Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.random_forest)= ### Random Forest `ml.random_forest` Many decision trees, each on a bootstrap sample of the rows, averaged (or voting). Robust with little tuning, and needs no scaling. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `trees` | `int` | Default `200`. | | `max_depth` | `int` | Deepest tree; 0: no limit Default `0`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.support_vector_machine)= ### Support Vector Machine `ml.support_vector_machine` A support vector machine on the standardised features; the ``rbf`` kernel draws curved boundaries. ``strength`` is the penalty (1/C). Slow beyond some ten thousand rows. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `kernel` | `Literal['rbf', 'linear', 'poly']` | Default `'rbf'`. | | `strength` | `float` | Regularisation: larger is simpler Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | (node-ml.xgboost)= ### XGBoost `ml.xgboost` Gradient-boosted trees with XGBoost. Gives the model, its training score, each feature's importance (its share of the total gain: how much the splits on it improved the fit; plot it with Bar Chart) and the number of trees kept. There is no chart among the outputs, so the fitted model can be checkpointed without pickle. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `trees` | `int` | Most trees to add Default `500`. | | `learning_rate` | `float` | Shrinks each tree Default `0.05`. | | `max_depth` | `int` | Default `6`. | | `subsample` | `float` | Share of rows each tree sees Default `1.0`. | | `early_stopping` | `int` | Stop after this many trees without improvement; 0: off Default `20`. | | `validation` | `float` | Share of rows held back to stop on Default `0.2`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `importance` | `DataFrame` | | | `best_iteration` | `int` | | | `summary` | `dict[str, Any]` | | (node-ml.k_nearest_neighbours)= ### k-Nearest Neighbours `ml.k_nearest_neighbours` Predicts from the ``neighbours`` training rows nearest in the (standardised) features: their mean, or their most common class. ``distance`` weights nearer rows more. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `target` | `str` | The column to predict Default `''`. | | `features` | `str` | Columns to learn from, comma-separated; empty: every column but the target Default `''`. | | `task` | `Literal['regression', 'classification']` | Predict a number, or a category Default `'regression'`. | | `neighbours` | `int` | Default `5`. | | `weights` | `Literal['uniform', 'distance']` | Default `'uniform'`. | **Outputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `score` | `float` | | | `summary` | `dict[str, Any]` | | ## Machine Learning/Uncertainty (node-ml.prediction_interval)= ### Prediction Interval `ml.prediction_interval` Predictions with an interval that holds the true value with the chosen probability, for any regressor (split conformal prediction). Calibrate on rows the model was not trained on, with the target column: the test part of a Train/Test Split, say. **Inputs** | Name | Type | Description | |---|---|---| | `model` | `Model` | | | `calibration` | `DataFrame` | | | `table` | `DataFrame` | | | `coverage` | `float` | Share of true values inside Default `0.9`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `values` | `Any` | | | `half_width` | `Any` | | | `summary` | `dict[str, Any]` | | ## Machine Learning/Unsupervised (node-ml.cluster)= ### Cluster `ml.cluster` Groups of similar rows, as a ``cluster`` column. k-means finds ``clusters`` round groups; DBSCAN finds dense regions of any shape and marks rows in none as -1 (noise). ``silhouette`` (−1 to 1) says how well separated the groups are; NaN with fewer than two groups. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `features` | `str` | Columns to use, comma-separated; empty: every numeric one Default `''`. | | `method` | `Literal['k-means', 'dbscan']` | Default `'k-means'`. | | `clusters` | `int` | k-means: how many Default `3`. | | `radius` | `float` | DBSCAN: neighbourhood size Default `0.5`. | | `min_rows` | `int` | DBSCAN: rows to make a core Default `5`. | | `scale` | `bool` | Standardise each column first, so units do not set the weights Default `True`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `counts` | `DataFrame` | | | `silhouette` | `float` | | (node-ml.detect_anomalies)= ### Detect Anomalies `ml.detect_anomalies` Rows unlike the rest, judged on several columns at once: a reading can be ordinary in each column and still odd in combination, which Detect Outliers (one column at a time) misses. An isolation forest counts how few random cuts set a row apart; the local outlier factor compares how crowded a row's neighbourhood is with its neighbours'. ``share`` is how many rows to flag. ``flagged`` gains ``anomaly`` and ``anomaly_score`` (higher is odder); ``clean`` is the table without them. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `features` | `str` | Columns to use, comma-separated; empty: every numeric one Default `''`. | | `method` | `Literal['isolation forest', 'local outlier factor']` | Default `'isolation forest'`. | | `share` | `float` | The share of rows you expect to be odd Default `0.02`. | | `scale` | `bool` | Standardise each column first, so units do not set the weights Default `True`. | | `seed` | `int` | The same seed gives the same model Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `flagged` | `DataFrame` | | | `clean` | `DataFrame` | | | `count` | `int` | | (node-ml.principal_components)= ### Principal Components `ml.principal_components` Principal component analysis: the directions along which the rows vary most. ``table`` gains columns PC1, PC2...; ``explained`` gives each component's share of the variance; ``loadings`` how much each column contributes to each component. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `features` | `str` | Columns to use, comma-separated; empty: every numeric one Default `''`. | | `components` | `int` | Default `2`. | | `scale` | `bool` | Standardise each column first, so units do not set the weights Default `True`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `explained` | `DataFrame` | | | `loadings` | `DataFrame` | | | `chart` | `Figure` | |