Averaging replicates
Technical replicates are several rows of the data table with the same
sample_name: for example, the same solution measured in three wells. With
Average technical replicates on, the rows of each sample are replaced by
one row with the mean values, and the model is built on these averaged rows.
The option is in the preprocessing section of the analysis dialog and is off
by default (average_replicates: false). It is available for all
classification methods. PCA ignores it.
What is averaged
- Rows are grouped by the
sample_namecolumn, whatever the label column is. - Features: every selected feature column is averaged within the group.
- Label: all rows of one sample must have the same label. If a sample has
rows with different labels, the analysis stops with the error
Group <n> has conflicting labels: [...]. Fix the labels in the data table and run the analysis again.
After averaging, the data set has one row per sample. The number of rows in the results (predictions, score plots, confusion matrices) is the number of samples, not the number of wells.
With LDA, averaging is not available when the label column is
sample_name: every class would then have a single row, and LDA needs at
least two rows per class. The dialog disables the checkbox and shows a
warning.
Mean and median
The averaging function supports the mean and the median, but the analyses
always call it with the mean (method='mean'). There is no median option
in the dialog.
Effect on cross-validation
After averaging, each row is its own group. There are no replicates left that could leak between folds, so every CV scheme gives an honest estimate.
| Averaging on | Averaging off + Group K-Fold | |
|---|---|---|
| Rows the model sees | One per sample | All replicates |
| Measurement noise | Reduced by averaging | Stays in the data; the model has to cope with it |
| Leakage between folds | None | None (replicates stay in one fold) |
| Rows for training | Fewer | More, but not independent |
| Prediction on new data | The model expects averaged rows: turn on averaging in the prediction dialog too | The model expects single wells |
Use averaging when you want a model of samples and you will always measure new samples in replicates. Keep the replicates and use Group K-Fold when you want a model that works on single wells. More on the choice in Replicates and Group K-Fold.
Source code
def average_replicates(X, y, groups, method='mean'):
"""
Average rows that belong to the same group (technical replicates).
Parameters:
-----------
X : ndarray (n_samples, n_features)
Feature matrix.
y : ndarray (n_samples,)
Labels.
groups : ndarray (n_samples,)
Group indices (same value = same sample_name).
method : str, default='mean'
Aggregation method for X (e.g., 'mean', 'median').
Returns:
--------
X_avg : ndarray (n_unique_groups, n_features)
Averaged feature matrix.
y_avg : ndarray (n_unique_groups,)
Labels for each group (majority vote, or raise if conflict).
new_groups : ndarray (n_unique_groups,)
New group indices (0..n_unique_groups-1) for possible future use.
"""
import numpy as np
from scipy.stats import mode
unique_groups = np.unique(groups)
n_groups = len(unique_groups)
n_features = X.shape[1]
X_avg = np.zeros((n_groups, n_features))
y_avg = np.zeros(n_groups, dtype=object)
for i, g in enumerate(unique_groups):
mask = groups == g
if method == 'mean':
X_avg[i] = np.mean(X[mask], axis=0)
elif method == 'median':
X_avg[i] = np.median(X[mask], axis=0)
else:
raise ValueError("method must be 'mean' or 'median'")
# Determine label for this group – majority vote, or raise if conflicting
labels = y[mask]
uniq, counts = np.unique(labels, return_counts=True)
if len(uniq) > 1:
# Option 1: raise an error (recommended for supervised tasks)
raise ValueError(f"Group {g} has conflicting labels: {uniq.tolist()}")
# Option 2: use majority vote (uncomment below)
# y_avg[i] = uniq[np.argmax(counts)]
else:
y_avg[i] = labels[0]
# New groups are just sequential (each row is its own group)
new_groups = np.arange(n_groups)
return X_avg, y_avg, new_groups
It is the first step of every run_<method>_analysis function. Example from
LDA:
if parameters.get('average_replicates', False):
groups = parameters.get('groups')
if groups is None:
raise ValueError("'groups' must be provided when averaging replicates is enabled.")
X, y, new_groups = average_replicates(X, y, groups, method='mean')
parameters['groups'] = new_groups