Skip to content

ddmc.logistic_regression

Helpers for using DDMC cluster centers as features in a logistic regression classifier, to predict clinical/genetic features of CPTAC patients (e.g. mutation status, tumor vs. NAT, hot/cold immune infiltration).

ddmc.logistic_regression

Logistic Regression Model functions to predict clinical features of CPTAC patients given their clustered phosphoproteomes.

Contains
  • normalize_cluster_centers: mean-centers DDMC cluster centers along the patient dimension, for use as classifier features.
  • get_highest_weighted_clusters: picks out the clusters a fitted classifier weighted most heavily.
  • plot_cluster_regression_coefficients / plot_roc: plotting helpers for a classifier's per-cluster coefficients and its cross-validated ROC curve.

normalize_cluster_centers

normalize_cluster_centers(centers: ndarray) -> np.ndarray

Mean-center cluster centers along the patient/sample dimension.

Parameters:

Name Type Description Default
centers ndarray

Cluster centers of shape (n_samples, n_components), e.g. from DDMC.transform().

required

Returns:

Type Description
ndarray

centers with each cluster's (column's) values shifted to have

ndarray

zero mean across samples, same shape as centers.

Source code in ddmc/logistic_regression.py
29
30
31
32
33
34
35
36
37
38
39
40
41
def normalize_cluster_centers(centers: np.ndarray) -> np.ndarray:
    """Mean-center cluster centers along the patient/sample dimension.

    Args:
        centers: Cluster centers of shape (n_samples, n_components), e.g.
            from `DDMC.transform()`.

    Returns:
        `centers` with each cluster's (column's) values shifted to have
        zero mean across samples, same shape as `centers`.
    """
    # normalize centers along along patient dimension
    return StandardScaler(with_std=False).fit_transform(centers.T).T

get_highest_weighted_clusters

get_highest_weighted_clusters(
    model: DDMC, coefficients: ndarray, n_clusters: int = 3
) -> list[int]

Pick out the (nonempty) clusters a fitted classifier weighted most heavily.

Parameters:

Name Type Description Default
model DDMC

The fitted DDMC model the classifier's features came from (used to exclude empty clusters).

required
coefficients ndarray

Per-cluster classifier coefficients, e.g. lr.coef_, of shape (1, n_components) or (n_components,).

required
n_clusters int

Maximum number of top clusters to return.

3

Returns:

Type Description
list[int]

Up to n_clusters nonempty cluster indices, ordered by decreasing

list[int]

absolute coefficient magnitude.

Source code in ddmc/logistic_regression.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def get_highest_weighted_clusters(
    model: DDMC, coefficients: np.ndarray, n_clusters: int = 3
) -> list[int]:
    """Pick out the (nonempty) clusters a fitted classifier weighted most heavily.

    Args:
        model: The fitted `DDMC` model the classifier's features came from
            (used to exclude empty clusters).
        coefficients: Per-cluster classifier coefficients, e.g.
            `lr.coef_`, of shape (1, n_components) or (n_components,).
        n_clusters: Maximum number of top clusters to return.

    Returns:
        Up to `n_clusters` nonempty cluster indices, ordered by decreasing
        absolute coefficient magnitude.
    """
    top_clusters = np.flip(np.argsort(np.abs(coefficients.squeeze())))
    top_clusters = [
        cluster for cluster in top_clusters if cluster in model.get_nonempty_clusters()
    ]
    return top_clusters[:n_clusters]

plot_cluster_regression_coefficients

plot_cluster_regression_coefficients(
    ax: Axes,
    lr: Any,
    hue: Sequence[str] | None = None,
    title=False,
) -> None

Plot LR coeficients of clusters.

Parameters:

Name Type Description Default
ax Axes

Axes to plot onto.

required
lr Any

A fitted scikit-learn linear classifier exposing coef_ of shape (1, n_components).

required
hue Sequence[str] | None

If given, per-cluster-run labels formatted as "{cluster}_{sample}" (split on "_") to group/color bars by sample when coefficients from multiple runs are concatenated; not used by any current figure (all call plot_roc with the default hue=None, one bar per cluster).

None
title str | bool

If given (and not False), set as the axes title.

False
Source code in ddmc/logistic_regression.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def plot_cluster_regression_coefficients(
    ax: Axes, lr: Any, hue: Sequence[str] | None = None, title=False
) -> None:
    """Plot LR coeficients of clusters.

    Args:
        ax: Axes to plot onto.
        lr: A fitted scikit-learn linear classifier exposing `coef_` of
            shape (1, n_components).
        hue: If given, per-cluster-run labels formatted as
            `"{cluster}_{sample}"` (split on `"_"`) to group/color bars by
            sample when coefficients from multiple runs are concatenated;
            not used by any current figure (all call `plot_roc` with the
            default `hue=None`, one bar per cluster).
        title (str | bool): If given (and not `False`), set as the axes title.
    """
    coefs_ = pd.DataFrame(lr.coef_.T, columns=["LR Coefficient"])
    if hue:
        coefs_["Cluster"] = [label.split("_")[0] for label in hue]
        coefs_["Sample"] = [label.split("_")[1] for label in hue]
        hue = "Sample"
    else:
        coefs_["Cluster"] = np.arange(coefs_.shape[0])
    p = sns.barplot(
        ax=ax,
        x="Cluster",
        y="LR Coefficient",
        hue=hue,
        data=coefs_,
        color="darkblue",
        **{"linewidth": 0.5},
        **{"edgecolor": "black"},
    )

    p.tick_params(axis="x", labelsize=6)
    if title:
        ax.set_title(title)

plot_roc

plot_roc(
    classifier: Any,
    X: ndarray,
    y: ndarray | Series,
    cv_folds: int = 4,
    title=False,
    return_mAUC: bool = False,
    kfold: str = "Stratified",
    ax: Axes | None = None,
) -> float | None

Plot Receiver Operating Characteristc with cross-validation folds of a given classifier model.

Fits a fresh copy of classifier on each cross-validation fold, plots the mean ROC curve (+/- 1 SEM band) across folds, and optionally returns just the mean AUC instead of plotting.

Parameters:

Name Type Description Default
classifier Any

A scikit-learn-compatible classifier exposing fit.

required
X ndarray

Feature matrix of shape (n_samples, n_features).

required
y ndarray | Series

Binary target labels of shape (n_samples,).

required
cv_folds int

Number of cross-validation folds.

4
title str | bool

If given (and not False), set as the axes title.

False
return_mAUC bool

If True, skip plotting and just return the mean AUC.

False
kfold str

Cross-validation strategy: "Stratified" (StratifiedKFold) or "Repeated" (RepeatedKFold, 10 repeats).

'Stratified'
ax Axes | None

Axes to plot onto; defaults to the current axes (plt.gca()).

None

Returns:

Type Description
float | None

The mean AUC across folds if return_mAUC is True, else None

float | None

(the ROC curve is plotted onto ax instead).

Source code in ddmc/logistic_regression.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def plot_roc(
    classifier: Any,
    X: np.ndarray,
    y: np.ndarray | pd.Series,
    cv_folds: int = 4,
    title=False,
    return_mAUC: bool = False,
    kfold: str = "Stratified",
    ax: Axes | None = None,
) -> float | None:
    """Plot Receiver Operating Characteristc with cross-validation folds of a given classifier model.

    Fits a fresh copy of `classifier` on each cross-validation fold, plots
    the mean ROC curve (+/- 1 SEM band) across folds, and optionally
    returns just the mean AUC instead of plotting.

    Args:
        classifier: A scikit-learn-compatible classifier exposing `fit`.
        X: Feature matrix of shape (n_samples, n_features).
        y: Binary target labels of shape (n_samples,).
        cv_folds: Number of cross-validation folds.
        title (str | bool): If given (and not `False`), set as the axes title.
        return_mAUC: If True, skip plotting and just return the mean AUC.
        kfold: Cross-validation strategy: `"Stratified"`
            (`StratifiedKFold`) or `"Repeated"` (`RepeatedKFold`, 10
            repeats).
        ax: Axes to plot onto; defaults to the current axes (`plt.gca()`).

    Returns:
        The mean AUC across folds if `return_mAUC` is True, else `None`
        (the ROC curve is plotted onto `ax` instead).
    """
    X = np.asarray(X)
    y = np.asarray(y)
    if kfold == "Stratified":
        cv = StratifiedKFold(n_splits=cv_folds)
    elif kfold == "Repeated":
        cv = RepeatedKFold(n_splits=cv_folds, n_repeats=10)
    tprs = []
    aucs = []
    mean_fpr = np.linspace(0, 1, 100)

    for _, (train, test) in enumerate(cv.split(X, y)):
        classifier.fit(X[train], y[train])
        viz = RocCurveDisplay.from_estimator(classifier, X[test], y[test])
        plt.close()
        interp_tpr = np.interp(mean_fpr, viz.fpr, viz.tpr)
        interp_tpr[0] = 0.0
        tprs.append(interp_tpr)
        aucs.append(viz.roc_auc)

    mean_tpr = np.mean(tprs, axis=0)
    mean_tpr[-1] = 1.0
    mean_auc = auc(mean_fpr, mean_tpr)

    if return_mAUC:
        return mean_auc

    if ax is None:
        ax = plt.gca()

    ax.plot([0, 1], [0, 1], linestyle="--", lw=2, color="r", label="Chance", alpha=0.8)
    sem_auc = sem(aucs)
    ax.plot(
        mean_fpr,
        mean_tpr,
        color="b",
        label=rf"Mean ROC (AUC = {mean_auc:0.2f} $\pm$ {sem_auc:0.2f})",
        lw=2,
        alpha=0.8,
    )

    sem_tpr = sem(tprs, axis=0)
    tprs_upper = np.minimum(mean_tpr + sem_tpr, 1)
    tprs_lower = np.maximum(mean_tpr - sem_tpr, 0)
    ax.fill_between(
        mean_fpr, tprs_lower, tprs_upper, color="grey", alpha=0.2, label=r"$\pm$ 1 SEM"
    )

    ax.set(xlim=[-0.05, 1.05], ylim=[-0.05, 1.05], title="ROC")

    if title:
        ax.set_title(title)

    ax.legend(loc=4, prop={"size": 8}, labelspacing=0.2)