From 0ddb4d0044868e749b0daa71a00385e4f4d47438 Mon Sep 17 00:00:00 2001 From: Stephan Breimann Date: Fri, 18 Sep 2026 19:22:49 +0200 Subject: [PATCH] docs(tutorials): add the FASTA-to-scikit-learn upstream bridge recipe The upstream adapters ship as code, but no notebook carried another tool's output end to end, and the only runnable scikit-learn pipeline lived inside tests/integration/test_sklearn_pipeline_compat.py. Protocol 8 shows one too, but only as an unexecuted fenced block in a markdown cell, so a reader could not run it. Adds tutorials/tutorial8_upstream_bridge.ipynb: a FASTA file from an upstream tool through read_fasta, get_df_parts and SequenceFeatureTransformer into a real sklearn Pipeline, cross-validated leak-free, with the CPP feature ids preserved through get_feature_names_out and a second unlabeled delivery scored end to end. Every public parameter of read_fasta, to_fasta and SequenceFeatureTransformer (constructor plus fit / transform / get_feature_names_out) is passed by name. Runs offline on the bundled DOM_GSEC benchmark and adds no dependency (scikit-learn is already core). Wired into docs/source/tutorials.rst under a new Interoperability section, with a gallery thumbnail. One bridge only, per the issue's scope note; the embedding and structure bridges stay with the Embeddings & AlphaFold tutorial. Gates: nbmake 1 passed in 64s (timeout 120); docs build 0 errors, critical at baseline 167; tests/unit/api_tests 341 passed. Refs #555 Co-Authored-By: Claude Fable 5.1 --- docs/source/_static/img/thumbs/tut8.png | Bin 0 -> 63884 bytes docs/source/tutorials.rst | 20 +- tutorials/tutorial8_upstream_bridge.ipynb | 1081 +++++++++++++++++++++ 3 files changed, 1100 insertions(+), 1 deletion(-) create mode 100644 docs/source/_static/img/thumbs/tut8.png create mode 100644 tutorials/tutorial8_upstream_bridge.ipynb diff --git a/docs/source/_static/img/thumbs/tut8.png b/docs/source/_static/img/thumbs/tut8.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8249c21c4c759bc6a90ef9324dcf1ac0f2d3ad GIT binary patch literal 63884 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�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
    _nhz5cxTN{Zv8vC@t|dAj~!W`W3(8>TF}< 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 explanation
    ShapModel
    Evaluation and comparison
    Evaluation
    SeqOpt protein engineering
    Protein engineering
    + Upstream bridge from FASTA to a scikit-learn pipeline
    Upstream bridge
    Data Handling @@ -119,4 +120,21 @@ mutation map and lineage. .. toctree:: :maxdepth: 1 - generated/tutorial7_protein_engineering \ No newline at end of file + generated/tutorial7_protein_engineering + +Interoperability +---------------- +Most analyses start with another tool's output. The **Upstream bridge** tutorial is the +recipe that carries a plain FASTA file, the one hand-off every tool can produce, all the way +to a fitted model: :func:`~aaanalysis.read_fasta` reads it, :meth:`~aaanalysis.SequenceFeature.get_df_parts` +adapts it to the part geometry CPP needs, and :class:`~aaanalysis.SequenceFeatureTransformer` +drops into a stock ``scikit-learn`` ``Pipeline`` so CPP feature selection runs *inside* +cross-validation instead of before it. This is the counterpart to the fixed-feature route, +where :meth:`~aaanalysis.SequenceFeature.feature_matrix` hands a plain numeric matrix to any +estimator. The two heavy representations, language model embeddings and AlphaFold channels, +have their own bridge in the **Embeddings & AlphaFold** tutorial above. + +.. toctree:: + :maxdepth: 1 + + generated/tutorial8_upstream_bridge \ No newline at end of file diff --git a/tutorials/tutorial8_upstream_bridge.ipynb b/tutorials/tutorial8_upstream_bridge.ipynb new file mode 100644 index 00000000..b87f7ab1 --- /dev/null +++ b/tutorials/tutorial8_upstream_bridge.ipynb @@ -0,0 +1,1081 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c00-lede", + "metadata": {}, + "source": [ + "# Upstream bridge: from another tool's FASTA to a scikit-learn pipeline\n", + "\n", + "Most AAanalysis work starts somewhere else. A homology search, a motif scanner, a\n", + "proteomics filter or a colleague's export hands you a **FASTA file** plus a label per\n", + "record, and the question is how to get from there to a model. This tutorial is that\n", + "recipe, end to end, in three hops:\n", + "\n", + "1. **Read** the upstream file into a `df_seq` with `aa.read_fasta`.\n", + "2. **Adapt** the sequences into the part geometry CPP needs with `aa.SequenceFeature.get_df_parts`.\n", + "3. **Bridge** into a real `sklearn.pipeline.Pipeline` with `aa.SequenceFeatureTransformer`, so CPP feature selection happens *inside* cross-validation instead of before it." + ] + }, + { + "cell_type": "raw", + "id": "c01-box", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. admonition:: You will learn\n", + " :class: tip\n", + "\n", + " - **Tool**: :class:`~aaanalysis.SequenceFeatureTransformer` (with :func:`~aaanalysis.read_fasta` as the entry point)\n", + " - **Input**: a FASTA file written by an upstream tool, one label per record\n", + " - **Output**: a CPP feature matrix and a fitted ``sklearn.pipeline.Pipeline``\n", + " - **Best used for**: wiring another tool's sequence output into a leak-free scikit-learn workflow\n", + " - **Related protocol**: :doc:`P8: Prediction `\n", + " - **Related API**: :func:`~aaanalysis.read_fasta`, :func:`~aaanalysis.to_fasta`, :meth:`~aaanalysis.SequenceFeature.get_df_parts`, :class:`~aaanalysis.SequenceFeatureTransformer`" + ] + }, + { + "cell_type": "markdown", + "id": "c02-h1", + "metadata": {}, + "source": [ + "Everything below runs offline on the bundled `DOM_GSEC` benchmark and needs no extra\n", + "package: `scikit-learn` is already a core dependency. Only **one** bridge is covered here,\n", + "the FASTA hand-off, because it is the one every tool can produce. The two heavy\n", + "representations, protein language model embeddings and AlphaFold-derived channels, have\n", + "their own bridge in the *Embeddings & AlphaFold* tutorial.\n", + "\n", + "## 1. The hand-off: a FASTA file from an upstream tool\n", + "\n", + "To keep the notebook self-contained, the \"upstream tool\" is simulated: a bundled dataset is\n", + "written out with `aa.to_fasta`, which is also the writer you use in the other direction, to\n", + "hand AAanalysis results to the next tool in your chain. Substitute your own file here and\n", + "the rest of the notebook is unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c03-write", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:17:16.402065Z", + "iopub.status.busy": "2026-09-18T17:17:16.401993Z", + "iopub.status.idle": "2026-09-18T17:17:17.721695Z", + "shell.execute_reply": "2026-09-18T17:17:17.721459Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ">Q14802|0\n", + "MQKVTLGLLVFLAGFPVLDANDLEDKNSPFYYDWHSLQVGGLICAGVLCAMGIIIVMSAKCKCK ...\n", + ">Q86UE4|0\n", + "MAARSWQDELAQQAEEGSARLREMLSVGLGFLRTELGLDLGLEPKRYPGWVILVGTGALGLLLL ...\n" + ] + } + ], + "source": [ + "import os\n", + "import tempfile\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import aaanalysis as aa\n", + "\n", + "aa.options[\"verbose\"] = False\n", + "\n", + "# Stand-in for the upstream tool: 40 gamma-secretase candidates written to a FASTA file,\n", + "# with the class label carried in the header (1 = substrate, 0 = non-substrate).\n", + "df_hits = aa.load_dataset(name=\"DOM_GSEC\", n=20)\n", + "folder_upstream = tempfile.mkdtemp()\n", + "file_hits = os.path.join(folder_upstream, \"upstream_hits.fasta\")\n", + "\n", + "aa.to_fasta(df_seq=df_hits[[\"entry\", \"sequence\", \"label\"]], file_path=file_hits,\n", + " col_id=\"entry\", col_seq=\"sequence\", sep=\"|\", col_db=None, cols_info=[\"label\"])\n", + "\n", + "for line in open(file_hits).read().splitlines()[:4]:\n", + " print(line if line.startswith(\">\") else line[:64] + \" ...\")" + ] + }, + { + "cell_type": "markdown", + "id": "c04-md", + "metadata": {}, + "source": [ + "The header convention is the only thing you have to match. `aa.read_fasta` splits each\n", + "header on `sep` and maps the pieces onto columns: `col_id` takes the first field, `col_db`\n", + "an optional leading database tag (as in `>sp|P12345|...`), and `cols_info` names whatever\n", + "follows. Here the single trailing field is the label." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c05-read", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:17:17.722711Z", + "iopub.status.busy": "2026-09-18T17:17:17.722649Z", + "iopub.status.idle": "2026-09-18T17:17:17.747076Z", + "shell.execute_reply": "2026-09-18T17:17:17.746856Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame shape: (40, 3)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
     entrysequencelabel
    1Q14802MQKVTLGLLVFLAGF...PGETPPLITPGSAQS0
    2Q86UE4MAARSWQDELAQQAE...SPKQIKKKKKARRET0
    3Q969W9MHRLMGVNSTAAAAA...AIWSKEKDKQKGHPL0
    4P53801MAPGVARGPTPYWRL...GLFKEENPYARFENN0
    5Q8IUW5MAPRALPGSAVLAAA...EVPATPVKRERSGTE0
    6P01135MVPSAGQLALFALGI...LLKGRTACCHSETVV0
    7O43914MGGLEPCSRLLLLPL...SDVYSDLNTQRPYYK0
    8P05556MNLQPIFWIGLISSV...KSAVTTVVNPKYEGK0
    9P16234MGTSHPAFLVLGCLL...DIGIDSSDLVEDSFL0
    10P50895MEPPDAPAQARGAPR...SGGARGGSGGFGDEC0
    \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df_seq = aa.read_fasta(file_path=file_hits, col_id=\"entry\", col_seq=\"sequence\",\n", + " sep=\"|\", col_db=None, cols_info=[\"label\"])\n", + "\n", + "# Header fields arrive as strings; the label is the only one that has to be numeric.\n", + "df_seq[\"label\"] = df_seq[\"label\"].astype(int)\n", + "aa.display_df(df=df_seq, n_rows=10, show_shape=True)" + ] + }, + { + "cell_type": "markdown", + "id": "c06-h2", + "metadata": {}, + "source": [ + "## 2. The adapter: sequences into CPP parts\n", + "\n", + "CPP compares **parts** of a sequence, not whole sequences. `aa.SequenceFeature.get_df_parts`\n", + "is the adapter. When the upstream file carries domain boundaries (a `tmd_start` / `tmd_stop`\n", + "pair) it slices TMD, JMD-N and JMD-C. A plain FASTA has no such annotation, so it falls back\n", + "to a sequence-level geometry: the whole sequence as the target part plus its two flanks,\n", + "sized by `jmd_n_len` / `jmd_c_len`. That fallback is what makes an unannotated upstream file\n", + "usable at all." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c07-parts", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:17:17.748024Z", + "iopub.status.busy": "2026-09-18T17:17:17.747966Z", + "iopub.status.idle": "2026-09-18T17:17:17.751598Z", + "shell.execute_reply": "2026-09-18T17:17:17.751397Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parts: ['tmd', 'jmd_n_tmd_n', 'tmd_c_jmd_c']\n", + "DataFrame shape: (40, 3)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
     tmdjmd_n_tmd_ntmd_c_jmd_c
    entry   
    Q14802FLAGFPVLDANDLED...CKFGQKSGHHPGETPMQKVTLGLLVFLAGF...FYYDWHSLQVGGLICAGVLCAMGIIIVMSA...PGETPPLITPGSAQS
    Q86UE4AQQAEEGSARLREML...SQTKSETSWESPKQIMAARSWQDELAQQAE...ENLTVNGGGWNEKSVKLSSQISAGEEKWNS...SPKQIKKKKKARRET
    Q969W9AAAAAGQPNVSCTCN...HTHIAPLESAAIWSKMHRLMGVNSTAAAAA...FHRFQPTYPYLQHEIDLPPTISLSDGEEPP...AIWSKEKDKQKGHPL
    P53801PYWRLRLGGAALLLL...TRHDEIRKKYGLFKEMAPGVARGPTPYWRL...PPASLCKLSSARWGVCWVNFEALIITMSVV...GLFKEENPYARFENN
    Q8IUW5VLAAAVFVGGAVSSP...SVSGAETVNGEVPATMAPRALPGSAVLAAA...HYIMKNEANADVLKAMVADNSLYDPESPVT...EVPATPVKRERSGTE
    \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sf = aa.SequenceFeature(verbose=False)\n", + "df_parts = sf.get_df_parts(df_seq=df_seq, list_parts=None, all_parts=False,\n", + " jmd_n_len=10, jmd_c_len=10, tmd_len=None,\n", + " remove_entries_with_gaps=False, replace_non_canonical_aa=False)\n", + "\n", + "print(\"parts:\", list(df_parts.columns))\n", + "aa.display_df(df=df_parts, n_rows=5, show_shape=True)" + ] + }, + { + "cell_type": "markdown", + "id": "c08-note", + "metadata": {}, + "source": [ + "You can stop here and hand `df_parts` to `aa.CPP` yourself, which is what the\n", + ":class:`~aaanalysis.CPP` tutorial does. The rest of this notebook takes the other route and\n", + "lets the pipeline own that step." + ] + }, + { + "cell_type": "markdown", + "id": "c09-h3", + "metadata": {}, + "source": [ + "## 3. The bridge: `SequenceFeatureTransformer` inside a real `Pipeline`\n", + "\n", + "`aa.SequenceFeatureTransformer` wraps the whole `get_df_parts` to `aa.CPP.run` to\n", + "`feature_matrix` chain behind the scikit-learn `fit` / `transform` API. It takes the\n", + "`df_seq` **itself** as `X`, which is what makes it a drop-in first step: everything\n", + "downstream is stock scikit-learn.\n", + "\n", + "That ordering is the point. Selecting CPP features once on the full labeled set and then\n", + "cross-validating the classifier lets every test fold influence which features exist, and\n", + "the score comes out optimistic. Inside a `Pipeline`, `fit` runs on the training fold only,\n", + "so each fold re-mines its own signature and the held-out proteins stay held out." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c10-pipe", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:17:17.752461Z", + "iopub.status.busy": "2026-09-18T17:17:17.752379Z", + "iopub.status.idle": "2026-09-18T17:18:08.719430Z", + "shell.execute_reply": "2026-09-18T17:18:08.719204Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "balanced accuracy per fold: [0.5 0.75 0.875 0.875 0.625]\n", + "mean: 0.725\n" + ] + } + ], + "source": [ + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.model_selection import StratifiedKFold, cross_val_score\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "# X is the sequence table (the label column is deliberately NOT part of X); y the labels.\n", + "X_seq = df_seq[[\"entry\", \"sequence\"]]\n", + "labels = df_seq[\"label\"].to_numpy()\n", + "\n", + "sft = aa.SequenceFeatureTransformer(\n", + " split_kws=aa.SequenceFeature.get_split_kws(split_types=[\"Segment\", \"Pattern\"]),\n", + " df_scales=aa.load_scales(name=\"scales\", top60_n=20),\n", + " n_filter=25,\n", + " label_test=1,\n", + " label_ref=0,\n", + " max_overlap=0.5,\n", + " max_cor=0.5,\n", + " simplify=False,\n", + " n_jobs=1,\n", + " random_state=0,\n", + " verbose=False)\n", + "\n", + "pipe = Pipeline([(\"sft\", sft),\n", + " (\"scaler\", StandardScaler()),\n", + " (\"clf\", RandomForestClassifier(n_estimators=100, random_state=0))])\n", + "\n", + "cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)\n", + "scores = cross_val_score(pipe, X_seq, labels, cv=cv, scoring=\"balanced_accuracy\")\n", + "\n", + "print(\"balanced accuracy per fold:\", np.round(scores, 3))\n", + "print(f\"mean: {scores.mean():.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c11-caveat", + "metadata": {}, + "source": [ + "Forty proteins across five folds is a small-N estimate and the fold spread shows it. Read\n", + "the mean as a sanity check that the bridge carries signal, not as a performance claim. The\n", + "*P10: Validation* protocol covers how to report such a number honestly." + ] + }, + { + "cell_type": "markdown", + "id": "c12-h4", + "metadata": {}, + "source": [ + "## 4. Interpretability survives the bridge\n", + "\n", + "A wrapped estimator usually costs you the column names. This one does not:\n", + "`get_feature_names_out` returns the selected CPP feature ids, so every column of the matrix\n", + "the classifier saw is still a readable `PART-SPLIT-SCALE` statement, and the fitted\n", + "transformer keeps the full `df_feat` next to it." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c13-fit", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:18:08.720441Z", + "iopub.status.busy": "2026-09-18T17:18:08.720371Z", + "iopub.status.idle": "2026-09-18T17:18:15.512013Z", + "shell.execute_reply": "2026-09-18T17:18:15.511801Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "selected features: 25\n", + "first three: ['JMD_N_TMD_N-Pattern(C,4,8,12)-TANS770102', 'TMD_C_JMD_C-Pattern(C,2,5,9,12)-OOBM850101', 'JMD_N_TMD_N-Pattern(C,1,5,8)-RICJ880117']\n", + "DataFrame shape: (25, 13)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
     featurecategorysubcategoryscale_namescale_descriptionabs_aucabs_mean_difmean_difstd_teststd_refp_val_mann_whitneyp_val_fdr_bhpositions
    1JMD_N_TMD_N-Pat...,12)-TANS770102Conformationα-helix (C-term, out)α-helix (C-terminal, outside)Normalized freq...Scheraga, 1977)0.3870000.1770000.1770000.1100000.0850000.0000280.0371559,13,17
    2TMD_C_JMD_C-Pat...,12)-OOBM850101Structure-ActivityStabilityStability (extended-coil)Optimized beta-...e et al., 1985)0.3700000.128000-0.1280000.0830000.0790000.0000620.03715529,32,36,39
    3JMD_N_TMD_N-Pat...5,8)-RICJ880117ConformationUnclassified (Conformation)α-helix (C-terminal, outside)Relative prefer...chardson, 1988)0.3630000.1170000.1170000.0820000.0730000.0000880.03715513,16,20
    4JMD_N_TMD_N-Pat...5,8)-FASG760105PolarityUnclassified (Polarity)pK-CpK-C (Fasman, 1976)0.3550000.253000-0.2530000.1880000.1690000.0001220.03715513,16
    5JMD_N_TMD_N-Seg...,12)-DAYM780201OthersMutabilityMutabilityRelative mutabi... et al., 1978b)0.3550000.0870000.0870000.0470000.0730000.0001220.0371552,3
    6JMD_N_TMD_N-Seg...,12)-QIAN880128ConformationCoil (N-term)Coil (N-terminal)Weights for coi...ejnowski, 1988)0.3550000.083000-0.0830000.0370000.0670000.0001220.0371552,3
    7TMD_C_JMD_C-Pat...,12)-OOBM850104PolarityHydrophilicityNon-bonded energy per atomOptimized avera...e et al., 1985)0.3530000.1230000.1230000.0890000.0840000.0001370.03715529,32,36,40
    8TMD_C_JMD_C-Pat...5,9)-WILM950103PolarityHydrophobicity (interface)Hydrophobicity (interface)Hydrophobicity ...e et al., 1995)0.3440000.1050000.1050000.0790000.0640000.0002000.04071032,36,39
    9TMD_C_JMD_C-Pat...4,7)-TANS770102Conformationα-helix (C-term, out)α-helix (C-terminal, outside)Normalized freq...Scheraga, 1977)0.3350000.1710000.1710000.1270000.1300000.0002890.04368221,24,27
    10TMD_C_JMD_C-Pat...5,9)-SUEM840102Structure-ActivityUnclassified (S...cture-Activity)Stability (extended-coil)Zimm-Bragg para...i et al., 1984)0.3320000.1830000.1830000.1450000.1170000.0003210.04675332,36,40
    \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pipe.fit(X_seq, labels)\n", + "sft_fitted = pipe.named_steps[\"sft\"]\n", + "\n", + "feature_names = sft_fitted.get_feature_names_out(input_features=None)\n", + "print(\"selected features:\", len(feature_names))\n", + "print(\"first three:\", list(feature_names[:3]))\n", + "\n", + "aa.display_df(df=sft_fitted.df_feat_, n_rows=10, show_shape=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c14-plot", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:18:15.512970Z", + "iopub.status.busy": "2026-09-18T17:18:15.512894Z", + "iopub.status.idle": "2026-09-18T17:18:15.603492Z", + "shell.execute_reply": "2026-09-18T17:18:15.603257Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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Lbvo125ulRvugjBAYznC2Ls63vYhIp6W9EIIIYTIevJE4oUZswnvw1BiYWWPII9ueo/LCGGcM7vAt8yKLSxTmAjuGLBSCccEPSwnO9PEo3///lYUc5dAZA0MCplkWrHPEFOoYi01sxBC5FBWDE5tH03v3/zZs2fbuWYi/eQI1UjEL8hmgh+ZSGlmHpcRwjhndkE0mig6nt94dxscTDBNJIyzM40frAzC3Q2JcCGEEELkNHJERFwIcWijiLgQQhwaKCKes5AhVAghhBBCiBCQEBdCCCGEECIEJMSFEEIIIYQIAQlxIYQQQgghQkBCXAghhBBCiBCQEBdCCCGEECK3riMuhDg8mNi/pR7uIIQQQiSJIuJCCCGEEEKEgIS4EEIIIYQQISAhLoQQQgghRAhIiAshhBBCCBECEuJCCCGEEEKEgIS4EEIIIYQQIaDlC4UQmUan52eaQhXXqEVFjmTF4E5hF0EIIVKgiLgQQgghhBAhICEuhBBCCCFECEiICyGEEEIIEQIS4kIIIYQQQoSAhLgQQgghhBAhICEuhBBCCCFECEiICyGEEEIIEQI5dh3xLVu2mLx585rixYubnTt3mn///deUL1/eFCxYMJpmz549ZsOGDaZixYpm3759ZtOmTXY7x5UqVcoULVo04Xl27dpltm/fbsqVKxfdduDAgWi+QaxZs8ami+XII480kUgkx5V379699rgyZcqYnMbBgwdtGzg2b95s2yNPnjz289atW02hQoXsKx60eWz9Yo/dtm2bfe+9LrHtm8x12b9/v1m3bp05+uij7ed//vnHlC5d2hQpUiRFutWrV9vtlI3/jzjiiBT15jj6DPnR572QlmNcuehXsWVhO9e/WLFiqbaTv/d88dID5+f75r0WQgghhMg6cuwv7gMPPGBeeukl+/6jjz4yxxxzjHnttddSpHnuuefs9qVLl5qJEyeaWrVqmaZNm5omTZpYcXPeeeelEjexcFyVKlXMkiVLotv++usvm088OnXqZNM0atQoel5e06dPz1HlRbzdeuutpmzZsua4444zFSpUMHfeeafJKfz+++/mmmuuiX5ev369ue6666wI532zZs1sfRGkt912W2A+c+fONSeeeGKKfPyOpa0GDhwYmE+y12XkyJGmcuXKZs6cOfbz4MGDzcUXX5wizbvvvmvOOecc89tvv9nrznXw8vnn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664zBw8ejHsejjv55JPNzp07o9s+/fRTc9NNNwUes27dOnseXhzrzstrw4YNOaq83vO0bdvWite+fftawRrEmDFjzHPPPZfqfWbw1VdfmVdffTX6+cMPPzQ1atQwDRs2NJUqVTKdOnWKDhy8rFy50jRo0CDazj179kyV5s8//zR16tQxjRs3tsJ73Lhxdvv06dNTnDOWl156ybapy5vXNddcE93/3Xff2ev59ttvpzju2WeftYOf0047zQrmbt262QERUIfevXvbQQ/tjxAmTwQ2vP/++ynO16hRI/PQQw/ZfS+++KI9rm7duvaaIebh3nvvtYODqlWrmnvuuSdFWf766y/bhvQ/R1D6iy++2Jx55pnRc3///fdxrpgQQgghcp0Qj6VJkyZm7NixKSJ9sRHF/v3726gp0erly5dbgfH8888nzLtYsWJWPCfL0Ucfbc/DC3HlzsvLRV5zUnndeYjqrl692sycOdN8/vnnvhFSRN8PP/xgo8gMONx7Ivne9ETJHewnMuzqx2fyIVK/bdu2aDrE/x133GEGDhxoP1Pffv36mXfffdcew/GIca8IdsydO9ecc8450XaOHeQ4AcsAbs2aNeaDDz4wt99+u91+4YUXmuHDh9trEMQVV1wRzZvXZ599Ft03YsQIc+2115phw4ZFt9GOjz32mPn111/NqlWrbPmJRn/00Ud2PwOFPHny2O2Uh4HEb7/9Fh3UMOjynqty5cr2mi5cuNA8+uijNl/amb72xRdfmPnz59t2oq8gujnPvHnzbF68b9WqVYprFC89/7Pfnf+ss84KbBchhBBCZB6HrBA/99xzzfjx46OfP/nkEyvMgiBC+fDDD5u33norYd6XXnqp+fnnn80vv/xy2JcX+8dJJ51ko8fkQTSaQQMC+KmnnjJLliwxI0eOtOIT4eneE7lGtBGVdhHnd955x+Z5ww03mA4dOlgrzrRp0+znzp0727yxZDzxxBM23aRJk8yxxx5rBxLwwgsv2EECEWUoWLCgefrpp60o9hPi9erVM//++6/ZvXu3b90434IFC2y7MICoWbOm3Y4gJvL7xhtvBLYLItYrxHft2mW3c+fh448/tuIY6wfl8A4s3OADy8rrr79uLrnkEityEdKcr2TJktHry353R8QLgxHy564Iopzry/Ug7zfffNPeJfjyyy/N+eefH7UWXXDBBWbC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5ttvvzVt27a1be/44osvzBlnnBG91itXrjTLly831apVM1WrVrVp//zzT1sHL0cffbQ54ogjfLc3aNAg2rb0jSZNmqQ4J/z666/m2GOPNQULFoxu80tPn6LPUCfql6ivCCGEEOIwEOKvvfaa+ffff61A++uvv6Ji69577zUrVqwwl112mXn44YfN3XffHT1myJAh5n//+58VGbz69etnj0NErFq1yv4/ZcoUK3Li8dlnn5krrrjCipr8+fNbYfTee++ZM888M6myDx061Iq+O++800ybNi3HlHXmzJnR88DmzZutEEUgHnPMMb75//zzz7acY8eOTfE+M/jjjz/Myy+/bF599VVbth49ephvvvnGClg3cOjTp4+93n589NFHtj6kC4L+c/rpp9v2vvDCC1PsmzRpkvnqq6+in3lPvzvrrLPM9ddfb0aPHp3mvokQ57qdf/75tq1uvvlmu4907du3t8deeeWV0bw6d+5s1q5dawdFN954o23bunXr2johksnrxx9/NCNHjowew3Xgmjdq1Mh3O+elrnxGUCOs33///RSDF/oHA0Y3OPFLT/9goEg5EOS8uE6lS5cObG8hhBBCHAZCHDEJn3zyiRW2n3/+eXQfwozI6YcffphC3CIoa9asGf2MQPIed/vtt5urr77aipsgEMG9e/c248aNsyIEJk+ebIUK+4oUKZLmuuSkssae56qrrjLPP/+8efLJJ83evXvtoIBo78knn2yFPQOJ1atX22ip9z0ikOgpUWXOU69evei1IbqLSK1cubKNahNhRsSxjaiqi8Ledttt5oknnkhRtoEDB1qxlyjyilBGtJNvEJFIxArTcuXK+e5noMQLELTUl/ZEjJYvX94Ocjp27JimvgkjRoww99xzjxk2bFhUiEOVKlXMK6+8Ys477zybvxei3vQR/nd3NAYPHmy+/vprc9FFF9kXIMq5Zg888IBN57eddvntt9/MwoULbV3oGzNmzLB3Fp577jnz4osv2rZxBKWnH9Aero3oV2+++aYZMGBA3GsjhBBCiMPcI37UUUdZAYidAYjmYcuIvQXvhYglUU/sFUEgsoiIOmELRDIRJt7b+IdLWevUqWNtMkTSa9eubYUjA4AWLVrY/R9//LFZtmyZmThxYor3HINYJ4rbs2dP07VrVyvuPvjgA9OhQwcrCknP506dOllBe8kll9iy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xaZDhw6mUKFC0TRs+/333815551n/+cF+fLlM0cddZQ56aSTTJ48eeKeh2O2b99umjRpEt22bt06s3z5ctO0aVPfY/bu3Ws+++wz331nnXWWWbVqVY4pr/c8efPmNWXKlDGnnXaafR+PjRs3mrJly6Z6nxn88MMPKcr633//mTlz5ti2Ovnkk03BggXjHv/999+b+vXrm+LFi6fa9++//9q8jjnmGFOnTp3APCKRiPnxxx9TtRn9jvaJR2ya6dOnmy1btkQ/t23b1mzYsMEUKVLE9l8/Pv/8c7N79+5U20855RRTqVIlc+DAAfPpp5+a1q1bm5IlS9p9XPcvv/zSnHPOOSmuH+noa/Qj18/+/PNPU61aNXPsscfatGzje+SlSpUqplixYr7bGzduHG2jffv2mebNm6fqM7RzvXr1on08KP3BgwftNadOzZo1M/nz6zlfQgghRHaQ7l/c+++/39x8881WdLz11lvmqaeeMh988IHp3LlzNM0dd9xhJkyYYPbv329GjRpl3n33XSsM+Lxo0SIrHqdMmRIVMn5w3NNPP23FCOINZs2aZYYOHWoFjh979uwxb775pn2PwEF4cF5ASOak8nrPA3///bct77fffmtFmB8jR440S5cuNY888kiK95nBuHHjbH5OAD/zzDPm0UcfNSeeeKLZunWrWb9+vR3kuPLG8tNPP5nTTz/dzJ0714pxL7/88os588wzTcOGDW179uvXz9x7772p8kAw0r9mzpxpvvrqq+j29957z/Y5yhCEX5ru3bubli1bRj9Tt6JFi5rrrrvO9gE/Ro8ebTZv3mzWrFljVqxYYQUqINwR4gj1nj17mkGDBplbbrkleu3OP/988/LLL9u6OS688EKzdu1aez2vuuoqM3nyZNs2DNAQ+9OmTTPz58+3/cDB9Sct7e63HSHetWtX88cff9jBIYOajz76KMVgqF27dmbJkiWmYsWKdptfevo214TBFgKcwcLXX39tChcuHNjGQgghhMgcMi301apVKzNmzJiosEXEICi9IFIQwED0DSGD4Hruuefi5k0U8sYbbzSffPJJUmUhEuvSIiIRGe68ObG83vMAohGBeNlll9lyIdKIaiIgd+zYYYXStm3bzOzZs6Pviaofd9xxNvJLJJQIPp/BfWZQ0qBBAyssGSRwZ4C7B5QXAUYdH3zwQSviYOrUqebZZ5818+bNs1FYePHFF02fPn3Mzz//nKoelOOGG26IDkBief/99+3+Bx54wN4hOPXUU1MJcaLKXBPq7Y2oX3HFFbYcRG+D8EvDeUqXLu17LSpXrmwHXueee26qfSNGjLD/cxyDqNjj2U89Xn31VTNw4MDonZKqVauaN954w3Tp0iUqgB0McBjELFu2LCp0aV+i0RdccIF9wTfffGNWrlxp+xrp/LbPmDHD1o3BDedm8EM0vk2bNubJJ5+0ZXAReAhKf+SRR9q7A7/99pvdzjXhjgL5CCGEEOIQ8Yjzw85tb3c7/+OPP/YVOA5EAhFobxQvCIQpgoE8c0N5a9SoYTZt2mS++OILU7duXfPEE0+Yyy+/3Fx66aVWaBN1RpxTfvceAYrgbtSokRk8eLAVs7feeqvN7/nnnzft27e3onfBggX2M/W/6aabbLT2kksusekmTpxohboTiYjNa665JirCgUjv2LFjfcuNyL7zzjtN+fLlffcz+CHiToT3f//7n7WIxLJr1y57jpdeeinFdqwmtEc8/NK4yDztQzSZwYYDgcvAIq1wbbC7EHknss57B233+OOP232xMCCgv9GHGHDAgAEDUtyVoXzUn3J5o9Kx2xHvXFMXxcb64u4eHH300bZflCpVKnp8UHrsZdyBYHBJJJ8BUOydDCGEEELkcCHOjztRNG7Zw4cffphCYPiBR5Zb/8lAlA/7BVHXw628REqJuCLQHnvsMSvgsfxgaaFcRG0RrwhgRBaRX0Qk1gr3HkF9991322g1gg0xiAWHvAHhhdUDMQzYFoiwIt6JviPGiJqecMIJ0XIRfWUgENtu1atX9412IxCxPwRBVJ4IPAKfdsenHEu5cuVMt27dUm2nngUKFIjbjn5piI6XKFHCRvqxdCDWd+7cafdRV+4oUPe0wLVA1OIxp+2JmHthgEE7TZo0KcV2Bmfjx4+314W2wO7ywgsvpBgc0AewvsS2Tex2BDP5eUU+/nvo1auXrbOXoPSUk3agTMOHD7d3UY444ohAyxf2JL+XG1gIIYQQIouEOFG1eCCgsHtwqxsB5P3h94PoblD0NBYmMRJl9PMUp5cwyovg5oW4dmALwNP+zjvvWPGLgMPDy0RAbA940vv27WtFbDyIdhMlJy+844hORBLERjnxygN1ZvIl9WeQgV3DK9b8BhL4nb3gdSb6jsinbog+bA9MIvWCXQdPNVFryspgAQ9zVoI/HJFJuWhn6sSgAbC+YPnBukR53bXB/hMPBhL0A9Iy+OB/2sAL3nquvRP9DoQ0gxD6HLYpyvbQQw9F97/++ut2gBVL7HbKjTB28J6BQRBB6akLkzYZkGE3wk4zZMgQ3zy4XvRFv5cb4AkhhBAii4Q4UVTno+YWe+wPP5O+iLISMUwUXQZEsJsElwxYM5jk5+dPTg9hlBeRzOvtt99O4RF3EfFhw4ZFo57YURDzRC5d5DZe9BbxjzXECUqOddHr2Ehx7MoY5EsU1btSCCvMMGnRCx5joqZegUk0FGFP+1A3RCYWlFjBjqWDuwpOBCP6YwVsZsNdBSZRAh5obDbesjO4oSyU112beHcxsLgw4RGfPWm5LkziJZrshUmd2Hq8IptBBwMQQMDT/xDrDJ5cO+LPP/vss1Pk5bedgdqvv/4a/cz72rVrB5Y7KL33mgCTcIPu+tx11112MOr34m6KEEIIIbJwsmaLFi2spxUPMz/k+JG9IPaI4mLJQIAGWTC4FU8EjolurEKSFvAOI/ooS0YJo7zJTuB05UNI4psmcsrAh2gtIg5xjzfc+56oOfYLBBMTMxHRfvUKghU6iBo7EJIITJZzZKIqEW4sN0Ry8UYjcJnwSeTdWy8mleI5JgrvTdOjRw9rncGbjiBFsLMkpDdNWkAA+glXL7Qfgxv866ykgjXEiWOuL8sHMihJNAHXQQS5d+/eti94t9GHYgdzXAvsT25wg+hlEuf1119vatWqZbczCLzooouifnY8294lNYO2d+rUyd6F4Lz0E/z9zCUIIig9gyX6JncKGJAQ9Y4dfDk4f2zZHEEr/AghhBAik4Q4EUCsBYg+rBXOykEUzU0sw6fLbXte2B2I9gJRVKKJ5IEVgsgk0cTjjz8+7jndyh8OonhM9Iu1PcSLBMZaDXJSeUnvt161W9oQiwNiEgGMX/yff/6xYo4VUyif9z02FurBaicIK+wWCCeWuvNaTmI/M0kVewJC0itqEf7cMXjttdesXQbxjTDnzgiw+gc2hthJqdgUnEfZm+a+++6zHneW68PvjGedc/jlQ3Q/drCFYO7YsWP0M3cK7rnnnhRljk2DnQPPM3YY/OdM2MSfDYhRJ4KDIK13gEA0ncGol4svvtgO0LjG3EVwEIFnciVlpFwMULClYEHi7gfCl7xceUmPvzsWv+3YQYhC027YTDh/7FryXCfXz4PSuyU5WWWFASe2HdlMhBBCiOwhTyStM9WyAKK23O6PBZHGpLjMPi6jhHXe7AAxT/QU20QiiNQS8ce/nNVp/CCay0AKQZ1WEJ1nnHGGPdbvwUMibXDHiLsbFfsMMYUq1lLzZZAVgzupDYUQOf5vPoFAgnsi/eSIR+ghxPAXx0K0Lp6wTe9xGSWs82YHWBdYxi4ZIU603S2RmNVp/CBCHxudThb+eOCnlwgXQgghRK6OiAshDm0UEc9cFBEXQuRkFBHPgeuICyGEEEIIIZJHQlwIIYQQQogQkBAXQgghhBAiBCTEhRBCCCGECAEJcSGEEEIIIUJAQlwIIYQQQojcuo64EOLwYGL/lnq4gxBCCJEkiogLIYQQQggRAhLiQgghhBBChICEuBBCCCGEECEgIS6EEEIIIUQISIgLIYQQQggRAlo1RQiRaXR6fqYpVHGNWjQDrBjcSe0nhBC5BEXEhRBCCCGECAEJcSGEEEIIIUJAQlwIIYQQQogQkBAXQgghhBAiBCTEhRBCCCGECAEJcSGEEEIIIUJAQlwIIYQQQogQkBAXQgghhBAiBPRAn3Qyc+ZMs27dulTb69ata44++mgzdepU07BhQ1O7du3ovh07dphJkyaZZs2amRIlSpjJkyfb7Xny5DHFixc3p556qilZsqTv+bZt25YifaFChUzTpk1N2bJl45Zz/fr1pnz58qneZwa//vqrqVKliq0L7N+/3/z0009my5YtplGjRuaoo47yPW7WrFlm5cqV0c+nnXaaqVSpUoo0e/fuNd9//70pUKCAbS/qHAT5nXDCCbZNYN++ffb6cCx558uXz2zatMls3LjR1KlTJ26dguqwc+dOM23aNHPuueemSP/RRx/ZbZwLDhw4YD755BNz1llnRa8leSXqD/QZjv3222/t/y1atIjm6VcfiEQiNj37Tz/99Oj2oHYJSr9nzx7b1gcPHrRlKVKkSNw2EkIIIUTmICGeTqZPn25++eUXK6ZmzJhhOnbsaLf36NHDisiLLrrIdOnSxYwZMyaFaLv88svN+++/b+rXr28uvfRSexwCCZFIfh988IE588wzU51v9erV0fSwa9cuc8kll5gPP/zQtGnTxreM//77r81rwYIFKd5nBuR333332ToB4vWCCy4wFSpUsIODH3/80dx///3mpptuSnXszTffbMX7EUccYT9XrVo1hRDfvn27FaJQuHBhU6xYMfP555+b/PlTd9dvvvnGnH322Wbp0qWmYsWK9npwLGkRlkWLFjVfffWVFcV9+vQxo0aNsvn5Ea8ODGL69u1rr5MXrjfbSpUqZT8jrHv37m0efvhhc9ttt9lty5cvT9gfOnfubFq1amV2795ty86AgLpRB7/6IKLPP/98m3fevHlNtWrV7ADADVhi2wX80lN2xD0DDs5JPRHl5cqVS3OfEEIIIUTakBBPJwg0QOi0bt3aCmjHvHnzzLHHHmv/J5KKeHLCq2XLltF0ZcqUSXEc7xHbK1asiEZDvcSmf/nll83//vc/K8QR/5wPYUVUs3Tp0ubLL7+0n6dMmWK2bt0afd+uXTsbdUV4chyRdaKmRPjXrFljI6aI5COPPNL8888/Nq/Fixebk046yW6Dxx57zApbhB+DAkTms88+a4UpUIcTTzzRtG/f3hx33HHRMiMmiaSvXbs2MPI6cuTIqFCEiy++2ApoBK6XwYMH27TetiJvRO/tt99uBzicmwEOZenatat57rnnzD333JPqnInqwIAgGUaMGGEeeugh8+qrr5pbb701KowT9QfK/d9//9n/OYZrgiDmOvjVhyj733//bfME+iDXm8GWX7sg3v3Sc67+/fubG2+80W6n/7311lvmlltuSaq+QgghhEg/EuJZBAKnbdu25rPPPjPdu3c3mzdvtgIXMR0E6e68804rkJs3b57wHIhVItOIbIQbkU+inYi5hQsX2kg9lpaJEydaAezen3HGGdY6QQQUgUkU9OuvvzZz5861oozoMZF3BP11111n0yDA//jjD5s3whxhPGjQIFsObBfUywlYVzai77H2lCVLltho66pVq8xff/1lyx0boaZO9erVi36uWbOmjU7HCvHq1atb+wV3FxzYe3jNnz/fRpqx4mAXAoQ29hA/IZ6oDpSVQYt3IASIYwcDHQTvO++8Y0aPHm2tLLRzMv2hVq1a9v+nn37aFCxY0O5v0KCBbSu/+gwZMsQOqJy9hOg350aI+7UL7eeX/pFHHklxR4VrjU1KCCGEEFmPhHgW0q1bNzN06FArvIh+nnfeeTYiHQ9sGkSh/SBq64QgYpXo7WWXXWbF4hVXXGGjmAjGXr16WZGF4MQqQRSYCLR7T9QWq4OLgr799tvmxRdfNCeffLKNlCPaEPTYQfhMFBVx2KFDB5svAhVx6rzH+L2d2PVSuXLlVNsYNDDIwNZChJbyYqOg3o5OnTpZ0Yr4p5zvvvuu9TTH0rNnz8B2pMxE8Wkz/OFYXxhAUC8GA16vdrJ1wEvNACRIiFNOBC6Rfiwuw4YNiwrxRP2Bch1//PHWusJ7hLn3jkFsfRDq3kEd1phFixYFtku89A4GDgzInnrqKd82pf68/MBOJIQQQoi0ISGeheD5xQOMHQEBzS3/REIcywGRbcQwIhXw8AIiDCFIdBV7A8Kb/Il0I7ARV7yI3PIKgqg2Ys6JSiwMiFTA+oAQdBCVRYQDEwqJ4mJh8QpUjqXcsZDOiWkHYh/bhAMfNQMKIryOxo0bW+87kWX82kTlvZM7kwHbDC/83S+88IJ5/PHH7XbKzaCEgYy3fRPVAYgUx0bEvXVjgMOAgTRsHzdunD1XMv2BYxnYcDcE+vXrZ9uFAYtffUjrFcV4y+NNskyU/uOPP7bnImLvJt/GQhtiuxFCCCFE5iAhnoVgAyAiivAkspxoAtzvv/9uPeesdIEwI1IMRIsRgbEecQeCDaGFsCQdUVlvpDYWhCWWEKKzgOUEkUYkPtabHjtBknwRagwKHFhdrr/+ejuhFLEOCHbqjnDDm+3AMkNbIPCB9NTbC/XAHvP666/bzxdeeGGKyHKiNsSG46Lb5OMtK+/Zhj/a276J6sDgIB7Yeoi0E8l2AxxsJMOHD49OsI3XH4hYH3PMMdHPrO5CXwiqD30E8ewdXLkBmx8MsILS0w8oMxOQ4/XRu+66ywwcONB3HxF7BhpCCCGESB4J8SwGOwJC0kVk/awmiFsir0SFiRAjtlhZhJcjVqx6QYQj2pjciLjEH4z9Ab/vhg0bbHQZ77B7z+Q/luZzq4NgRfBGqROBiPaWB+FKWVndg4gt5cfughDFfkH9OC/v8VFzfqL5RN6ZbEq5vWkQwKzwQVswYZJJi6+99lqKNC5KHwuClvYeMGCAzYcJrQhMwBOP0GbiJELW274Qrw7YaOLBwIlVbNzgBmjTBx980A6MEvUH/OtYdhjkMJhiP30jqD4MHpgM+sADD9jBE7ajIEuJs/v4pWc5QyxM3FFhToET7V5/ubefOTtSLEEr0QghhBAiGD3QJ4MgQBA5XrCHMDEOEMB4q11UmAmQ2CMQXERhiUSOHTvW/Pbbb+aZZ57xnUgIpI89j1dAcg4EGqtwYOkgkk05WN3liy++SPGeyXwIMKwYRDIRuayigSXGG1WN/dykSRMrAInMM5nSrcABRI2xTBBdR9AxEMDmQDkQ0Nge8BFj3cCSgZBftmyZmTBhgp2M6E1To0YNm4YoMzYcBheIU28aL0Sc3aomtC/HEnnHzkJ9XfQdf3u8FVDi1YHVSxDksSCsGRRgBYoV9nj1EfashpKoP9CeeLQZKNAX6BNcy6D60B9YW5y8GSSw3Vlo/NolKD0rsNAPGQzRF3kxMVQIIYQQWU+eSDwPgxAB4GVGwPNKBJFoBDhCMPahM5mdJh5MoHz00UcTPtRHpJ05c+bY5S0r9hliClX8vyvAiPSxYrD/gFsIIXLa3/zZs2cntG6K+CgiLtLFKaecYu0dfhMcYyGifMcdd8QVz5mVJggmTRKVlggXQgghRE5BHnGRbtzyh8mA5zu70viBzeaqq65K17FCCCGEEFmBIuJCCCGEEEKEgIS4EEIIIYQQISAhLoQQQgghRAhIiAshhBBCCBECEuJCCCGEEEKEgIS4EEIIIYQQIaDlC4UQmcbE/i31cAchhBAiSRQRF0IIIYQQIgQkxIUQQgghhAgBCXEhhBBCCCFCQEJcCCGEEEKIEJAQF0IIIYQQIgQkxIUQQgghhAgBCXEhhBBCCCFCQEJcCCGEEEKIEJAQF0IIIYQQIgQkxIUQQgghhAgBCXEhhBBCCCFCQEJcCCGEEEKIEJAQF0IIIYQQIgQkxIUQQgghhAiB/GGcVAhxeLFr1y77/6JFi8IuihBCiCzG/a13f/tF+pEQF0JkmKVLl9r/e/furdYUQohcwooVK0zz5s3DLsYhjYS4ECLDtGzZ0v4/efJkU7ZsWbXoIcj27dtNq1atzIwZM0yxYsXCLo5II7p+hz6H0jUkEo4Ib9++fdhFOeTJE4lEImEXQghxaLN161ZTsmRJs2XLFlOiRImwiyPSga7hoY2u36GPrmHuRJM1hRBCCCGECAEJcSGEEEIIIUJAQlwIIYQQQogQkBAXQgghhBAiBCTEhRBCCCGECAEJcSFEhilUqJB54IEH7P/i0ETX8NBG1+/QR9cwd6LlC4UQQgghhAgBRcSFEEIIIYQIAQlxIYQQQgghQkBCXAghhBBCiBDIH8ZJhRA5g6VLl5qnn37aLFiwwOzbt880atTI3HDDDaZBgwbZkldmnj83sn//fvPaa6+ZSZMmmb/++stUrFjRdOvWzfTt29fky5cvS/Pas2ePOfHEE83BgwcD83zvvfdM48aN01W33ML8+fPNc889ZxYuXGjy5s1rTjnlFNO/f39To0aNDOX7zjvvmEcffdRMmTLFVKlSJdvPn1vYtWuXeeGFF8z06dPN2rVrbVv36tXL9OzZM0P57tixw7Rt29a0a9fOPPjgg75p+J6SJh7Tpk0zRx99dIbKIrKYiBAiVzJ+/PhIgQIFIvwZ8L4KFiwYee+997I8r8w8f25k+/btkVNPPTVV+/Fq3bp1ZPfu3Vma15w5c3zTe18zZ87M5FofXrz66quRvHnzpmq3YsWKRb744ot05/vbb79FSpcubfNasmRJtp8/t7B+/frIscce69v3e/ToETlw4EC68j148GCkd+/eNp9rrrkmMB1/QxN9B5cvX56BGorsQEJciFzIypUrI0WLFrV/qC+77LLId999Z0XThRdeaLcVKlQo8ueff2ZZXpl5/txKnz59bFtVrVo1MmrUqMi8efMiw4YNi5QpU8ZuHzBgQJbmNXLkSLuvV69ekUWLFvm+du7cmcm1Pnz4+eefI/ny5bNtOHDgwMisWbMi06ZNi7Rv395uo+03b96c5ny5dpUqVYoKsSAhnlXnz02ceeaZtq3q168fGTduXGTu3LmRJ598Mvq37emnn05znnv37o1ceeWV0esXT4gPGjQoev2CvoPkJ3I2EuJC5EJuvPFG+we8a9euqfZ1797d7uvXr1+W5ZWZ58+NLFu2zIoo7ij88ccfKfYxqMmTJ48dzGzatCnL8rrlllvsdRo+fHgm1Sp30aVLF9t+/fv3T7F93759kRYtWth9CK1k2b9/f+SVV16JFClSxB7LdYsnxDP7/LmNb7/91rZRqVKlbGTcywcffGD3lStXzrZnsiCcTzvttBTXL54Q79mzp00zZcqUDNVFhIuEuBC5DG57li1b1v4B//7771PtJ6rDvvLly9u0mZ1XZp4/t/L444/bNurWrVvcSN2bb76ZZXm1a9fObscGIdLG1q1bI/nz57ftt2rVqlT7J0yYYPc1atQo6TybNWsWtXY999xz9u5GkBDPivPnNq6//nrbRjfddJPv/uOOO87uT9biM3r06Og1YSB07733JhTidevWtYJddy4ObbRqihC5jBUrVpiNGzeaokWL2olZ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lipVKjAdbcx3hUeg8yTGeCxbtsyucV2zZs2E1zHZ84v4T9pkKUO+N/FWC3LXpWLFinHbmidiMqGdJ3TyvUrEunXrrOeffDW35tBCQlwIIYQQQogQ0KopQgghhBBChICEuBBCCCGEECEgIS6EEEIIIUQISIgLIYQQQggRAhLiQgghhBBChICEuBBCCCGEECEgIS6EEEIIIUQISIgLIYQQQggRAvnDOKkQQoTNjBkz7BPugsifP7990mD16tXt0+0OFb744gv7f9OmTU2xYsXMocr69evtUwgLFSpkjjnmmFz91FWedDp79mzbJ1u3bh12cYQQmYierCmEyJXweGke554Mp556qnnqqadMixYtTE4nT5489v+5c+eaRo0amUMNBkfXXHONGTlypDl48KDdRj2oz+HCxIkT7eDutNNOSyr9V199Zc444wxzxBFH2Meo50bS2mZCHCooIi6EyNXUqVPHVK5cOdX2vXv3mhUrVpi//vrL/Pjjj6ZNmzZm6tSpplWrVqGUM7cwdOhQM2LECPu+Zs2a5qijjjqsxNdll11mBxljx45N+hgE6JlnnmmKFCliciPpaTMhDhUkxIUQuZobbrjBvoKYPn266datm7UHXH/99ebXX3/N1vLlNoj+wnnnnWfGjRtnDje++eabNB/TsGHDqOUoN5KeNhPiUEGTNYUQIg5YAp555hn7/rfffjPz589Xe2Uh27Zti3rchRDicEcRcSGESIDXjsIEQiKUfqxZs8b8/fffZvfu3aZMmTLW9sIEu1j+/fdfM2fOHFO2bNmoj3v16tXWCsPkxHr16iW0IeCf/vPPP826devshNJKlSolfR3Xrl1rj8VPXqNGDVOhQgXfdK6cTJR0df7nn3/M8uXLo/XLm/f/x3MikYj5448/zIYNG0yVKlVM1apVky7TggULbF02btxoP9MWRIHJH1tQeuuwadMm6y8vX768OeGEE+znRYsW2ban/M5T7+D8XOMDBw7YfCtWrJjUxNKlS5fa+nOeWrVqpcp35cqVZsmSJWbXrl3R+jJPgTaqXbt2uiZrZsX1cXlihznppJOi14L+iU2oWrVqKfIMIjOuT+HChW27Jttm2fn9c2WnjsxrwEYVVMeM9i9xmBMRQohcSMmSJSP8CXzhhRcSpv3mm29sWl6fffZZin0HDx6MDB06NHLcccdF07hX8eLFIzfffHNk165dKY6ZOnWq3d++ffvITz/9FDn11FNTHFe0aNHITTfdFNmzZ49veYYPHx6pVq1aNH2ePHkiXbt2jWzYsCG6be7cuamOmzRpUqRJkyapysn5v/zyy1TpXTk7deoU+fvvvyMdO3a053LH1a5dO/LDDz/YtDNmzIgce+yxKfJt1apVZPXq1ZFk6NatW6py8cqXL1+G6kB69p9//vmRUaNGRQoXLhw9pl+/ftF0XIfWrVunqB+vpk2bRmbOnOlb5u+++87ujy1LmTJlInfccUdk9+7d0bSPP/64b/1uueWWhG0zffp0m/aII47I8uvj8mQfeZ5xxhkpjiFP2jGIzLw+sd8LvzYL4/v3/fffR84666xUfYU2++WXX3yPSU//ErkDCXEhRK4kWSG+d+/eyJlnnmnTIhL+/fffFPuvu+666I9q5cqVI82bN4+ceOKJkWLFikW3X3TRRb5CoGrVqvZHn/fHHHNM5LTTTouULl06etyVV16ZqjwPPPBACsHXokWLSPXq1e3nk046KVCIP/vss9F9hQoVsuKDF+/Zljdv3shrr73mW07EghP+9erVs+cpWLBgtAzjx4+PFChQIFKkSBG7zyv4ECfJ8NBDD9l2Jj8n+Pjcrl27DNXBCT2EGumOPPLISLNmzez1nzZtmk3z8ccfR+tDmgYNGthrUaJECbstf/78kdGjR6fId8GCBVHRSLqTTz45cvrpp9tyuzJefPHF0fTvv/++rQ9txD7Owedhw4ZlWIhn5vVxeZLOidsaNWrYfoawdcdxLWLJ7Otzzz33JGyz7P7+vffee7Yt2U8bc915ub5QtmzZyOLFi1Mck57+JXIPEuJCiFwtxG+44Qb7w+x9TZkyJfLhhx9GBg0alCLSdu+996bIY9asWdGo7ZgxY1Ls27p1a+Syyy6L7t+4cWMqIcCrXLlyKaLsRO969OgR/YH2Cn/ENXmxj0ifN+LK+Z1giRXic+bMiUbiyNtbFqLoRNPd+ebPn+9bzgoVKtgIsGP27NlRQULeRGPXr18f3f/0009Hj122bFnS18UNep588skU29NbByf0eCHwtm3bZrfv2LHDRlOJCDuBeeGFF0bWrVuX4lrcdddd0SjpX3/9Fd3Xt29fu71z586pIq5EOKkHEdfYfTVr1rTHjR07Nuk2SSTEM/P6ePNEXDKAcNB2vXv3jgrKJUuWZPn1iddm2f39o6848XzuueemaE/eM6hxdyi8x6Snf4ncg4S4ECJXC/FkXvyQDxw4MHLgwIEUebz55ptWcBG584Mff5fHzz//7CsE3n333VTHYQlw+7/99tvo9muuucZua9mype/5nnnmGV8h3qtXL7uNSOG+fft8o/4NGza0aS655BLfcr711lupjjvnnHOiEWGEjxfO46KEn3/+eSSjQjy9dfAKPQZYsdx9993RCLBfvtC2bVubBtHkwJrAtgcffDCSFrJKiGfW9fHm+eKLL6bKk+Pq169v9992221Zfn3itVl2f/+cvYi7D7EDLFixYoXtF2effXZ0f3r7l8g9aLKmECJX411HnAlUTL5atWqV/cykrQsvvND07NnTd3JYnz597CsIHr7CZLH9+/ebPXv2+Kbp1KlTqm1HH320KVq0qNm5c6fZsWNHdDvrmMMll1zim9cVV1xhbr311uiDcBzTpk2z/1999dW+k9cKFChgrr32WtOvX7/AZfI6duyYapub7NesWTNTvHjxFPs4z5FHHmknwTF5LqNkRh381iOfMmWK/f/444+PLp0YC5Pq4Msvv4xua9KkiT3PQw89ZH766SfTvn17c/rpp9tJk7ETNbODzL4+9L8rr7wy1XaOo8/fdtttZvLkyeaJJ57I0usTj+z+/rm+wt8EJpL6tbf7jma0f4ncg4S4ECJXE7uOOCL29ddft9sWL15sBUyiVS34weYHd+HChVbI8+I9q4B4842FH/uSJUv65smqDeTL4MDBw4Xc4MGPEiVK2NUwWP3B+2AiV4769esH1sHtY+UJVoFAODl4j2iLxQkuv33e/dx9zQiZUYdixYrZJ1PGwrWCd955x77iwconjttvv918+umndl35zz77zL6A1T8Qxb1797YP4ckOsuL61K1b164g4geiEtyANSuvTzJk1/ePFVm89U+G9PYvkXuQEBdCCA8szUZUjx9pIs8333yzKViwoI3kxYKAGTx4sHnqqafskmh+ETLESpAQ9QqRRCBceAHlCSJWzHijnX5RPIc3z9jy0hbxyOoIcGbUIUhUIraASDbL2cXD2w4s7zdr1izz6quvmlGjRtmnryL2WMrwzTfftK8LLrjAvP/++yZfvnwmK8mK6xNPFLt2dlHmrLw+8cjO75+LsCf6/mVW/xK5BwlxIYTwgYgmT/QbNmyYjY6zRnDXrl1TpHnyySfN3Xffbd+fcsopNgLKGtLcaiZqxvrOfrfp0wOigbWRERys0RyEW4fbQUSfYxHxrDEdBBYFYJ3mtAiN7CAr64CgJkKLxWHAgAFpKheis3///vbFdZkxY4aZOXOmfSIokVAeyX7uuecGWolyMrH9yAvtBawrHmYfy87vn7evMNgK4ocffrCCnTXRuTuSkf4lcgd6sqYQQgTw9NNP2wd1EOnkhzT21vGLL75o/7/uuutsRPSxxx4zl19+uX3oCsL9999/j0bjvLe408vJJ5+cwncaCw9r8d6Od9FQ99CSSZMmBebt9uGLz2lkZR0aN25s///8888D07iHCyGynBWDz7zcXQoGSV26dLFPYeW64yEHd8yhBnUIGvA5H7TzdIfVx7L7++fqyIDLDwQ4D/9q27ZtdLCSnv4lchcS4kIIEef2/BtvvGGFBj+y11xzTYr9LjLm94RCnoaIxcURNFksLVx88cX2/+HDh9sJgl6YkDZw4EDf43r16hU9johtLEwiw0rhJqLlRLKqDi5fBjcjR45MtZ9Id7t27ay4+vDDD+02orlXXXWV3ebOGWtvcsKPqKgXZ1NxAj6nQvnpT7HeatoZK467a5QdfSyozbL7+8ekbeCOx9dff51qPwMBBmnM4XBPOk1P/xK5C1lThBAiDkS4+EHHosIqEfyYupUaiAhOnz7dPPjgg2br1q02Yo2A+fnnn62A51HibvUFhEFGQfi89tprVuicccYZ1hLRokULayN45ZVX7HkZPHhXegAGEIig+fPn2x98RKSbSMhqF/icETmU3ytechJZVQfEFccS5ezbt6/N65xzzrHXjQl/L7/8sr0TwqPIvdYC5gzccccdNhrLoKhNmzZ24h+RUPoIj23H93zppZemOB9WBeCcnIMVOlz0PKeBvx2rCaunYCdBTBOFZtDXuXNnu1JMdvSxoDbL7u8fNqOzzjrLRq+pO9eevw8M0j/55BNrRWLQjmUmo/1L5CLCXj9RCCFy+iPut2zZYp/aR3qevLd27dro0xUrVqzou/Z4pUqV7INCeOALn1lPOHYdY8oQBE8YJA3rLHvZtGmTfVR27Pl4mApPNqxTp06qdcRhzZo19smPQWulxz7wJZly8hjw2CdIeuHJheznyYIZXUc8vXVw61TTnkFs3rzZPoQlKF+euvjrr7+mOIaHzbgHxvi9eKLlhAkTUp2Ldce96Xi0e0bXEc/M6+PNk/W5/erWvXv3yPbt27Pt+gS1WRjfP/pKhw4dfM/JA5ReeeWVTOlfIvegiLgQIleCj5RI1jHHHJMwLcsCsqShi3SNHj3aRqNZjm3p0qX2dvy8efOsp5YJYkTJuP3OEmhbtmwx27ZtM//99180P5aTI1rIkm1BkAfHxq60gBeZ9YZZLm/ChAl2AhyT5ojSN2/e3Pzyyy92XfTYdaOJuBGV4xY5y+7hS2WCHRPbmITKWtOxJCont+DZH7RkHdF6Js/F2jPi4Ty1fnaD9NSB9qOMQcvUAdFe8mNy7vjx482SJUuslQGfMdehR48eqVa0IPI5YsQI60nmOnAMq4dw/YnUcgzXKpb77rvP5ktUFRtDMtFwIsLUgf6Undfn0UcftRHgt956y951qV69uq0X/cyPrLo+QW0WxvePvsJ3j+8gFpXly5fbvtCgQQMb8WZOSWb0L5F7yIMaD7sQQgghhAgfxC7WEoSxV7wKIbIGTdYUQgghhBAiBCTEhRBCCCGECAEJcSGEEEIIIUJAkzWFEEIIkfRERiFE5qHJmkIIIYQQQoSArClCCCGEEEKEgIS4EEIIIYQQISAhLoQQQgghRAhIiAshhBBCCBECEuJCCCGEEEKEgIS4EEIIIYQQISAhLoQQQgghRAhIiAshhBBCCBECEuJCCCGEEEKY7Of/AF0PRt6GXERrAAAAAElFTkSuQmCC", + "text/plain": [ + "
    " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "aa.plot_settings()\n", + "\n", + "importances = pipe.named_steps[\"clf\"].feature_importances_\n", + "order = np.argsort(importances)[-10:]\n", + "color = aa.plot_get_clist(n_colors=2)[0]\n", + "\n", + "fig, ax = plt.subplots(figsize=(7, 5))\n", + "ax.barh(np.arange(len(order)), importances[order], color=color)\n", + "ax.set_yticks(np.arange(len(order)))\n", + "ax.set_yticklabels([feature_names[i] for i in order], fontsize=8)\n", + "ax.set_xlabel(\"Random forest importance\")\n", + "ax.set_title(\"Top CPP features driving the scikit-learn model\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c15-h5", + "metadata": {}, + "source": [ + "## 5. Scoring the next delivery from the same tool\n", + "\n", + "The fitted pipeline is now a predictor for anything that tool sends next. A new FASTA goes\n", + "through exactly the same two calls, and `transform` encodes it with the features chosen at\n", + "`fit` time, so the new matrix is column-compatible by construction." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c16-batch", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:18:15.604469Z", + "iopub.status.busy": "2026-09-18T17:18:15.604405Z", + "iopub.status.idle": "2026-09-18T17:18:15.627065Z", + "shell.execute_reply": "2026-09-18T17:18:15.626784Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "feature matrix: (6, 25)\n", + "DataFrame shape: (6, 26)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
     entryJMD_N_TMD_N-Pattern(C,4,8,12)-TANS770102TMD_C_JMD_C-Pattern(C,2,5,9,12)-OOBM850101JMD_N_TMD_N-Pattern(C,1,5,8)-RICJ880117JMD_N_TMD_N-Pattern(C,5,8)-FASG760105
    1Q153030.9566700.5810000.4690000.221500
    2P168820.5656700.6347500.3456700.491500
    3P044390.8776700.5265000.2960000.229500
    4P080690.5323300.6637500.6050000.664000
    5P279300.4403300.5210000.2466700.442500
    6Q9Y2190.7173300.6022500.3703300.369000
    \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# A second, unlabeled delivery: six proteins the pipeline has never seen.\n", + "df_all = aa.load_dataset(name=\"DOM_GSEC\")\n", + "df_batch2 = df_all[~df_all[\"entry\"].isin(df_seq[\"entry\"])].head(6)\n", + "\n", + "file_batch2 = os.path.join(folder_upstream, \"upstream_batch2.fasta\")\n", + "aa.to_fasta(df_seq=df_batch2[[\"entry\", \"sequence\"]], file_path=file_batch2,\n", + " col_id=\"entry\", col_seq=\"sequence\", sep=\"|\", col_db=None, cols_info=None)\n", + "\n", + "df_new = aa.read_fasta(file_path=file_batch2, col_id=\"entry\", col_seq=\"sequence\",\n", + " sep=\"|\", col_db=None, cols_info=None)\n", + "\n", + "X_new = sft_fitted.transform(X=df_new)\n", + "df_X_new = pd.DataFrame(X_new, columns=feature_names)\n", + "df_X_new.insert(0, \"entry\", df_new[\"entry\"].to_numpy())\n", + "\n", + "print(\"feature matrix:\", X_new.shape)\n", + "aa.display_df(df=df_X_new, n_rows=10, n_cols=5, show_shape=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c17-predict", + "metadata": { + "execution": { + "iopub.execute_input": "2026-09-18T17:18:15.628008Z", + "iopub.status.busy": "2026-09-18T17:18:15.627946Z", + "iopub.status.idle": "2026-09-18T17:18:15.665259Z", + "shell.execute_reply": "2026-09-18T17:18:15.665054Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame shape: (6, 3)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
     entrypredicted_labelp_substrate
    1Q1530310.760000
    2P1688210.690000
    3P0443910.680000
    4P0806900.480000
    5P2793000.390000
    6Q9Y21910.580000
    \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df_pred = pd.DataFrame({\"entry\": df_new[\"entry\"],\n", + " \"predicted_label\": pipe.predict(df_new),\n", + " \"p_substrate\": pipe.predict_proba(df_new)[:, 1].round(3)})\n", + "aa.display_df(df=df_pred, n_rows=10, show_shape=True)" + ] + }, + { + "cell_type": "markdown", + "id": "c18-recap", + "metadata": {}, + "source": [ + "## 6. Recap: the bridge in four lines\n", + "\n", + "Stripped of the narrative, the whole recipe is:\n", + "\n", + "```python\n", + "df_seq = aa.read_fasta(file_path=\"hits.fasta\", cols_info=[\"label\"]) # 1. read\n", + "pipe = Pipeline([(\"sft\", aa.SequenceFeatureTransformer(random_state=0)), # 2. bridge\n", + " (\"scaler\", StandardScaler()),\n", + " (\"clf\", RandomForestClassifier(random_state=0))])\n", + "pipe.fit(df_seq[[\"entry\", \"sequence\"]], df_seq[\"label\"].astype(int)) # 3. fit\n", + "pipe.predict(df_new) # 4. predict\n", + "```\n", + "\n", + "What to change for your own data:\n", + "\n", + "- **Header convention**: `sep`, `col_db` and `cols_info` in `aa.read_fasta` cover the usual\n", + " UniProt-style and tool-specific formats.\n", + "- **Domain boundaries**: if your upstream file has them, keep the `tmd_start` / `tmd_stop`\n", + " columns on the `df_seq` and `aa.SequenceFeature.get_df_parts` will slice real parts\n", + " instead of the sequence-level fallback, which is normally the stronger signal.\n", + "- **Selection budget**: `n_filter`, `max_cor` and `max_overlap` trade feature count against\n", + " redundancy; `split_kws` and `df_scales` set how wide CPP searches, and are the two levers\n", + " that dominate runtime.\n", + "\n", + "Where to go next: the :class:`~aaanalysis.CPP` tutorial for the feature engineering itself,\n", + "the *Embeddings & AlphaFold* tutorial for the embedding and structure bridges, and the\n", + "*P8: Prediction* protocol for the full prediction workflow this recipe feeds." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}