{ "cells": [ { "cell_type": "markdown", "id": "a644b163", "metadata": {}, "source": [ "# BBBC021\n", "\n", "MCF-7 breast cancer cells treated with 113 small molecules {cite:p}`Caie_2010`,\n", "and the standard mechanism-of-action benchmark {cite:p}`Ljosa_2013`.\n", "{func}`~mantispy.ds.bbbc021` returns the subset the benchmark annotates: 38 compounds and DMSO, 103 treatments\n", "across 12 mechanisms." ] }, { "cell_type": "code", "execution_count": 1, "id": "c2a812af", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T00:57:25.606051Z", "iopub.status.busy": "2026-10-21T00:57:25.605665Z", "iopub.status.idle": "2026-10-21T00:57:29.249373Z", "shell.execute_reply": "2026-10-21T00:57:29.248106Z" } }, "outputs": [ { "data": { "text/plain": [ "AnnData object with n_obs × n_vars = 632 × 467\n", " obs: 'Metadata_Plate', 'Metadata_Well', 'Metadata_Compound', 'Metadata_Concentration', 'Metadata_MOA', 'Metadata_CellCount', 'Metadata_SiteCount', 'Metadata_Row', 'Metadata_Col', 'Metadata_Control', 'Metadata_Control_Type', 'Metadata_ConcentrationUnit', 'Metadata_Perturbation', 'Metadata_Perturbation_Type'\n", " var: 'object', 'feature_group', 'feature', 'channel', 'scale', 'angle', 'gray_levels', 'radial_bin', 'params', 'is_feature', 'feature_kind'\n", " uns: 'mantispy'\n", " layers: None (.X)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "import mantispy as mt\n", "\n", "adata = mt.ds.bbbc021()\n", "adata" ] }, { "cell_type": "markdown", "id": "40e584da", "metadata": {}, "source": [ "## Source\n", "\n", "| file | from | holds |\n", "| --- | --- | --- |\n", "| `ljosa_2013.csv` | Cell Painting Gallery, `cpg0010-caie-drugresponse/broad-az/workspace/backend` | the well-level CellProfiler profiles |\n", "| `BBBC021_v1_image.csv` | Broad Bioimage Benchmark Collection {cite:p}`Ljosa_2012` | compound and concentration per plate and well |\n", "| `BBBC021_v1_moa.csv` | Broad Bioimage Benchmark Collection | the mechanism per compound and concentration |\n", "| 632 × `Image.csv` | Cell Painting Gallery, `workspace/analysis///analysis/-` | one row per field of view, with its cell count |\n", "\n", "Every file is pinned by sha256 in the registry.\n", "\n", "## Columns mantispy adds\n", "\n", "| column | how |\n", "| --- | --- |\n", "| `Metadata_Compound`, `Metadata_Concentration` | `BBBC021_v1_image.csv`, joined on plate and well |\n", "| `Metadata_MOA` | `BBBC021_v1_moa.csv`, joined on compound and concentration |\n", "| `Metadata_Control` | `Metadata_Compound == \"DMSO\"` |\n", "| `Metadata_Perturbation` | compound and concentration, `\"@\"`: what replicate wells share |\n", "| `Metadata_CellCount` | `Count_Cells` of the well's `Image.csv`, summed over its fields |\n", "| `Metadata_SiteCount` | the fields of that `Image.csv` that contributed cells |\n", "\n", "The `Image.csv` files are from the same CellProfiler run as the profiles, which the profiles themselves show.\n", "CellProfiler numbers the cells of each field from 1, so a field of $n$ cells contributes object numbers $1, \\dots, n$,\n", "and a well's mean `Cells_Number_Object_Number` is fixed by its per-field counts $n_s$:\n", "\n", "$$\\frac{\\sum_s n_s (n_s + 1) / 2}{\\sum_s n_s}$$" ] }, { "cell_type": "code", "execution_count": 2, "id": "6ca1ec6f", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T00:57:29.253512Z", "iopub.status.busy": "2026-10-21T00:57:29.253295Z", "iopub.status.idle": "2026-10-21T00:57:44.575746Z", "shell.execute_reply": "2026-10-21T00:57:44.574400Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "607 of 632 wells agree exactly\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", "
platewellfrom Image.csvin the profilesdifference
89Week1_22401E07133.151679132.958785-0.192894
116Week10_40115D05110.201258110.4447240.243466
124Week10_40119C0631.00956930.860577-0.148992
143Week2_24121G0341.04629640.919505-0.126792
145Week2_24141B0296.47802996.5864360.108407
\n", "
" ], "text/plain": [ " plate well from Image.csv in the profiles difference\n", "89 Week1_22401 E07 133.151679 132.958785 -0.192894\n", "116 Week10_40115 D05 110.201258 110.444724 0.243466\n", "124 Week10_40119 C06 31.009569 30.860577 -0.148992\n", "143 Week2_24121 G03 41.046296 40.919505 -0.126792\n", "145 Week2_24141 B02 96.478029 96.586436 0.108407" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cache = mt.settings.cache_dir / \"mantispy\"\n", "profiles = pd.read_csv(cache / \"ljosa_2013__analysis_batch_stable__ljosa_2013.csv\").set_index(\n", " [\"Image_Metadata_Plate\", \"Image_Metadata_Well\"]\n", ")[\"Cells_Number_Object_Number\"]\n", "\n", "checked = []\n", "for path in sorted(cache.glob(\"cpg0010-caie-drugresponse/**/Image.csv\")):\n", " n = pd.read_csv(path, usecols=[\"Count_Cells\"])[\"Count_Cells\"].to_numpy()\n", " plate, well = path.parent.name.rsplit(\"-\", 1)\n", " checked.append((plate, well, (n * (n + 1) / 2).sum() / n.sum(), profiles[(plate, well)]))\n", "checked = pd.DataFrame(checked, columns=[\"plate\", \"well\", \"from Image.csv\", \"in the profiles\"])\n", "agree = np.isclose(checked[\"from Image.csv\"], checked[\"in the profiles\"])\n", "print(f\"{agree.sum()} of {len(checked)} wells agree exactly\")\n", "checked.assign(difference=checked[\"in the profiles\"] - checked[\"from Image.csv\"])[~agree].head()" ] }, { "cell_type": "markdown", "id": "7579bf3d", "metadata": {}, "source": [ "In the other 25 wells the two differ by at most 1.5.\n", "Those wells' profiles averaged over nearly, but not exactly, the cells CellProfiler counted.\n", "For `Week2_24121` well G03, the analysis run's `Cells.csv` holds exactly the 324 cells its `Image.csv` counts, and\n", "their mean object number is the 41.05 the counts give, against 40.92 in the profile.\n", "`Metadata_CellCount` is CellProfiler's count either way.\n", "\n", "## What it looks like" ] }, { "cell_type": "markdown", "id": "df05e13c", "metadata": {}, "source": [ "Cell count is the first thing many mechanisms change.\n", "Microtubule stabilizers and destabilizers, DNA replication inhibitors and Eg5 inhibitors all stop cells dividing, and\n", "leave a third to two fifths of the cells a DMSO well holds; the kinase inhibitors leave more than DMSO.\n", "A profile's distance from the controls has to be read beside this, which is what {func}`~mantispy.tl.cytotoxicity`\n", "does." ] }, { "cell_type": "code", "execution_count": 3, "id": "66735f24", "metadata": { "execution": { "iopub.execute_input": "2026-10-21T00:57:44.579609Z", "iopub.status.busy": "2026-10-21T00:57:44.579423Z", "iopub.status.idle": "2026-10-21T00:57:45.831081Z", "shell.execute_reply": "2026-10-21T00:57:45.829904Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "mt.pl.cell_counts(adata, groupby=\"Metadata_MOA\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.15" } }, "nbformat": 4, "nbformat_minor": 5 }