From bf46a19b7ab175a2b82b249970167c301d6b9451 Mon Sep 17 00:00:00 2001 From: fei1cell Date: Fri, 13 Feb 2026 16:40:30 -0600 Subject: [PATCH] v2 docs: add DTO analysis and update datasets yaml config --- docs/tutorials/DTO_analysis.ipynb | 2384 +++++++++++++++++++++++++++++ tfbpapi/datasets.yaml | 139 ++ 2 files changed, 2523 insertions(+) create mode 100644 docs/tutorials/DTO_analysis.ipynb create mode 100644 tfbpapi/datasets.yaml diff --git a/docs/tutorials/DTO_analysis.ipynb b/docs/tutorials/DTO_analysis.ipynb new file mode 100644 index 0000000..c564c38 --- /dev/null +++ b/docs/tutorials/DTO_analysis.ipynb @@ -0,0 +1,2384 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "48ac26da", + "metadata": {}, + "source": [ + "## DTO Active-Set Analysis\n", + "\n", + "This analysis identifies transcription factor (TF) binding samples that show\n", + "significant dual-threshold overlap (DTO) with both major perturbation datasets,\n", + "providing a high-confidence set of regulatory interactions.\n", + "\n", + "### Workflow\n", + "\n", + "1. **Filter by Hackett-2020-ZEV**: Select binding samples from all datasets\n", + " (Harbison, Rossi, Mahendrawada) where DTO vs. Hackett-2020-ZEV P ≤ 0.01.\n", + "\n", + "2. **Filter by Kemmeren-2014-TFKO**: Apply the same selection against the\n", + " Kemmeren-2014 knockout perturbation dataset (P ≤ 0.01).\n", + "\n", + "3. **Intersect**: Retain only binding samples that are significant in **both**\n", + " comparisons. These form the \"active set\" — samples whose regulatory\n", + " relationships are independently supported by two orthogonal perturbation\n", + " experiments (ZEV-inducible overexpression and gene knockout).\n", + "\n", + "4. **Summarize by regulator**:\n", + " - **Table**: One row per regulator with the count of active samples.\n", + " - **Distribution**: Histogram showing how many regulators have 1, 2, 3, …\n", + " active samples, revealing whether active evidence is concentrated in a\n", + " few TFs or spread broadly.\n", + "\n", + "### Rationale\n", + "\n", + "A binding sample significant in only one perturbation comparison could reflect\n", + "experiment-specific artifacts. Requiring significance in both Hackett (ZEV) and\n", + "Kemmeren (TFKO) leverages two fundamentally different perturbation strategies,\n", + "increasing confidence that the observed TF–target relationships are\n", + "biologically meaningful." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "c59de9d2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VirtualDB(7 repos, 8 datasets, views not yet registered)\n" + ] + } + ], + "source": [ + "from tfbpapi.virtual_db import VirtualDB\n", + "\n", + "# Read configuration from datasets.yaml\n", + "vdb = VirtualDB(\"/home/luegg/code/tfbpapi/tfbpapi/datasets.yaml\")\n", + "print(repr(vdb))" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "3ccc8874", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Registered views:\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 4963.67it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 9799.78it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 12595.51it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 3644.05it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 10280.16it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 11125.47it/s]\n", + "Fetching 1 files: 100%|██████████| 1/1 [00:00<00:00, 10058.28it/s]\n", + "Fetching 30 files: 100%|██████████| 30/30 [00:00<00:00, 146996.64it/s]\n", + "Key 'carbon_source' not found at path 'media.carbon_source' (current keys: ['name'])\n", + "Key 'carbon_source' not found at path 'media.carbon_source' (current keys: ['name'])\n", + "Key 'carbon_source' not found at path 'media.carbon_source' (current keys: ['name'])\n", + "Key 'temperature_celsius' not found at path 'temperature_celsius' (current keys: ['description', 'initial_temperature_celsius', 'temperature_shift_celsius', 'temperature_shift_duration_minutes', 'growth_phase_at_harvest', 'media'])\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " chec_mahendrawada_m2025_af_combined_meta\n", + " chec_mahendrawada_m2025_af_combined_meta_meta\n", + " dto_expanded\n", + " hackett\n", + " hackett_meta\n", + " harbison\n", + " harbison_meta\n", + " kemmeren\n", + " kemmeren_meta\n", + " knockout\n", + " knockout_meta\n", + " overexpression\n", + " overexpression_meta\n", + " rossi_2021_af_combined\n", + " rossi_2021_af_combined_meta\n" + ] + } + ], + "source": [ + "# List all registered views\n", + "print(\"Registered views:\")\n", + "for name in vdb.tables():\n", + " print(f\" {name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "968e3f01", + "metadata": {}, + "source": [ + "## Step1-Obtain the intersection of the binding dataset and Hackett" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "80e34ef0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 910\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", + "
sample_idregulator_symbolregulator_locus_tagconditioncarbon_sourcetemperature_celsiusdto_empirical_pvaluedto_fdrhackett_sample_idperturbation_id_source
016TEC1YBR083WAlphaglucose30.00.00.14176891hackett
1174ZAP1YJL056CYPDglucose30.00.00.005249818hackett
238PHO2YDL106CH2O2Higlucose30.00.00.263801190hackett
3248SOK2YMR016CBUT14glucose30.00.00.0655951098hackett
450NRG1YDR043CH2O2Loglucose30.00.00.167487249hackett
550NRG1YDR043CH2O2Loglucose30.00.00.241330248hackett
6323DIG1YPL049CBUT90glucose30.00.00.0316331598hackett
7281GCR2YNL199CSMunspecified30.00.00.0017011251hackett
886GLN3YER040WSMunspecified30.00.00.099947434hackett
9282GCR2YNL199CYPDglucose30.00.00.0474431251hackett
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol regulator_locus_tag condition carbon_source \\\n", + "0 16 TEC1 YBR083W Alpha glucose \n", + "1 174 ZAP1 YJL056C YPD glucose \n", + "2 38 PHO2 YDL106C H2O2Hi glucose \n", + "3 248 SOK2 YMR016C BUT14 glucose \n", + "4 50 NRG1 YDR043C H2O2Lo glucose \n", + "5 50 NRG1 YDR043C H2O2Lo glucose \n", + "6 323 DIG1 YPL049C BUT90 glucose \n", + "7 281 GCR2 YNL199C SM unspecified \n", + "8 86 GLN3 YER040W SM unspecified \n", + "9 282 GCR2 YNL199C YPD glucose \n", + "\n", + " temperature_celsius dto_empirical_pvalue dto_fdr hackett_sample_id \\\n", + "0 30.0 0.0 0.141768 91 \n", + "1 30.0 0.0 0.005249 818 \n", + "2 30.0 0.0 0.263801 190 \n", + "3 30.0 0.0 0.065595 1098 \n", + "4 30.0 0.0 0.167487 249 \n", + "5 30.0 0.0 0.241330 248 \n", + "6 30.0 0.0 0.031633 1598 \n", + "7 30.0 0.0 0.001701 1251 \n", + "8 30.0 0.0 0.099947 434 \n", + "9 30.0 0.0 0.047443 1251 \n", + "\n", + " perturbation_id_source \n", + "0 hackett \n", + "1 hackett \n", + "2 hackett \n", + "3 hackett \n", + "4 hackett \n", + "5 hackett \n", + "6 hackett \n", + "7 hackett \n", + "8 hackett \n", + "9 hackett " + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Using binding dataset as the main table, JOIN dto_expanded to retrieve DTO statistics\n", + "# Filtering for vs. Hackett perturbation data, pvalue <= 0.01\n", + "harbison_hackett = vdb.query(\"\"\"\n", + " SELECT\n", + " h.sample_id,\n", + " h.regulator_symbol,\n", + " h.regulator_locus_tag,\n", + " h.condition,\n", + " h.carbon_source,\n", + " h.temperature_celsius,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS hackett_sample_id,\n", + " d.perturbation_id_source\n", + " FROM harbison_meta h\n", + " JOIN dto_expanded d\n", + " ON CAST(h.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source = 'harbison'\n", + " WHERE d.perturbation_id_source = 'hackett'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(harbison_hackett)}\")\n", + "harbison_hackett.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "60ae9b90", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 684\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", + "
sample_idregulator_locus_tagcarbon_sourcetemperature_celsiusdto_empirical_pvaluedto_fdrhackett_sample_idperturbation_id_source
0671YOR083Wglucose250.00.0023851393hackett
1550YMR039Cglucose250.00.0239651132hackett
2154YDR216Wglucose250.00.082601304hackett
3392YJL110Cglucose250.00.124665799hackett
4700YOR290Cglucose250.00.0791601454hackett
5372YIL131Cglucose250.00.059436732hackett
6700YOR290Cglucose250.00.0700621453hackett
7372YIL131Cglucose250.00.076569731hackett
8700YOR290Cglucose250.00.0938791452hackett
9372YIL131Cglucose250.00.108097730hackett
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag carbon_source temperature_celsius \\\n", + "0 671 YOR083W glucose 25 \n", + "1 550 YMR039C glucose 25 \n", + "2 154 YDR216W glucose 25 \n", + "3 392 YJL110C glucose 25 \n", + "4 700 YOR290C glucose 25 \n", + "5 372 YIL131C glucose 25 \n", + "6 700 YOR290C glucose 25 \n", + "7 372 YIL131C glucose 25 \n", + "8 700 YOR290C glucose 25 \n", + "9 372 YIL131C glucose 25 \n", + "\n", + " dto_empirical_pvalue dto_fdr hackett_sample_id perturbation_id_source \n", + "0 0.0 0.002385 1393 hackett \n", + "1 0.0 0.023965 1132 hackett \n", + "2 0.0 0.082601 304 hackett \n", + "3 0.0 0.124665 799 hackett \n", + "4 0.0 0.079160 1454 hackett \n", + "5 0.0 0.059436 732 hackett \n", + "6 0.0 0.070062 1453 hackett \n", + "7 0.0 0.076569 731 hackett \n", + "8 0.0 0.093879 1452 hackett \n", + "9 0.0 0.108097 730 hackett " + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rossi_hackett = vdb.query(\"\"\"\n", + " SELECT\n", + " r.sample_id,\n", + " r.regulator_locus_tag,\n", + " r.carbon_source,\n", + " r.temperature_celsius,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS hackett_sample_id,\n", + " d.perturbation_id_source\n", + " FROM (\n", + " SELECT DISTINCT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " carbon_source,\n", + " temperature_celsius\n", + " FROM rossi_2021_af_combined_meta\n", + " ) r\n", + " JOIN dto_expanded d\n", + " ON CAST(r.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source = 'rossi_2021_af_combined'\n", + " WHERE d.perturbation_id_source = 'hackett'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(rossi_hackett)}\")\n", + "rossi_hackett.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "9e8e4616", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 946\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", + "
sample_idregulator_locus_tagregulator_symbolconditiondto_empirical_pvaluedto_fdrhackett_sample_idperturbation_id_source
01YBL005WPDR3standard0.00.00422782hackett
11YBL005WPDR3standard0.00.02529783hackett
21YBL005WPDR3standard0.00.04095684hackett
31YBL005WPDR3standard0.00.03178685hackett
41YBL005WPDR3standard0.00.01850986hackett
51YBL005WPDR3standard0.00.01701487hackett
61YBL005WPDR3standard0.00.01631988hackett
7100YJL056CZAP1standard0.00.000000817hackett
8100YJL056CZAP1standard0.00.000915818hackett
9100YJL056CZAP1standard0.00.000760819hackett
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 YBL005W PDR3 standard \n", + "1 1 YBL005W PDR3 standard \n", + "2 1 YBL005W PDR3 standard \n", + "3 1 YBL005W PDR3 standard \n", + "4 1 YBL005W PDR3 standard \n", + "5 1 YBL005W PDR3 standard \n", + "6 1 YBL005W PDR3 standard \n", + "7 100 YJL056C ZAP1 standard \n", + "8 100 YJL056C ZAP1 standard \n", + "9 100 YJL056C ZAP1 standard \n", + "\n", + " dto_empirical_pvalue dto_fdr hackett_sample_id perturbation_id_source \n", + "0 0.0 0.004227 82 hackett \n", + "1 0.0 0.025297 83 hackett \n", + "2 0.0 0.040956 84 hackett \n", + "3 0.0 0.031786 85 hackett \n", + "4 0.0 0.018509 86 hackett \n", + "5 0.0 0.017014 87 hackett \n", + "6 0.0 0.016319 88 hackett \n", + "7 0.0 0.000000 817 hackett \n", + "8 0.0 0.000915 818 hackett \n", + "9 0.0 0.000760 819 hackett " + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mahendrawada_hackett = vdb.query(\"\"\"\n", + " SELECT\n", + " m.sample_id,\n", + " m.regulator_locus_tag,\n", + " m.regulator_symbol,\n", + " m.condition,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS hackett_sample_id,\n", + " d.perturbation_id_source\n", + " FROM (\n", + " SELECT DISTINCT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " condition\n", + " FROM chec_mahendrawada_m2025_af_combined_meta\n", + " ) m\n", + " JOIN dto_expanded d\n", + " ON CAST(m.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source LIKE '%mahendrawada%af_combined%'\n", + " WHERE d.perturbation_id_source = 'hackett'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(mahendrawada_hackett)}\")\n", + "mahendrawada_hackett.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "a4f7ee52", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total number of records after merging: 2540\n", + "\n", + "Distribution of each dataset:\n", + "source_dataset\n", + "mahendrawada 946\n", + "harbison 910\n", + "rossi 684\n", + "Name: count, dtype: int64\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", + "
sample_idregulator_locus_tagdto_empirical_pvaluedto_fdrhackett_sample_idperturbation_id_sourcesource_dataset
016YBR083W0.00.14176891hackettharbison
1174YJL056C0.00.005249818hackettharbison
238YDL106C0.00.263801190hackettharbison
3248YMR016C0.00.0655951098hackettharbison
450YDR043C0.00.167487249hackettharbison
550YDR043C0.00.241330248hackettharbison
6323YPL049C0.00.0316331598hackettharbison
7281YNL199C0.00.0017011251hackettharbison
886YER040W0.00.099947434hackettharbison
9282YNL199C0.00.0474431251hackettharbison
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag dto_empirical_pvalue dto_fdr \\\n", + "0 16 YBR083W 0.0 0.141768 \n", + "1 174 YJL056C 0.0 0.005249 \n", + "2 38 YDL106C 0.0 0.263801 \n", + "3 248 YMR016C 0.0 0.065595 \n", + "4 50 YDR043C 0.0 0.167487 \n", + "5 50 YDR043C 0.0 0.241330 \n", + "6 323 YPL049C 0.0 0.031633 \n", + "7 281 YNL199C 0.0 0.001701 \n", + "8 86 YER040W 0.0 0.099947 \n", + "9 282 YNL199C 0.0 0.047443 \n", + "\n", + " hackett_sample_id perturbation_id_source source_dataset \n", + "0 91 hackett harbison \n", + "1 818 hackett harbison \n", + "2 190 hackett harbison \n", + "3 1098 hackett harbison \n", + "4 249 hackett harbison \n", + "5 248 hackett harbison \n", + "6 1598 hackett harbison \n", + "7 1251 hackett harbison \n", + "8 434 hackett harbison \n", + "9 1251 hackett harbison " + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "common_cols = [\n", + " \"sample_id\", \"regulator_locus_tag\",\n", + " \"dto_empirical_pvalue\", \"dto_fdr\",\n", + " \"hackett_sample_id\", \"perturbation_id_source\"\n", + "]\n", + "\n", + "harbison_hackett[\"source_dataset\"] = \"harbison\"\n", + "rossi_hackett[\"source_dataset\"] = \"rossi\"\n", + "mahendrawada_hackett[\"source_dataset\"] = \"mahendrawada\"\n", + "\n", + "hackett_all = pd.concat([\n", + " harbison_hackett[common_cols + [\"source_dataset\"]],\n", + " rossi_hackett[common_cols + [\"source_dataset\"]],\n", + " mahendrawada_hackett[common_cols + [\"source_dataset\"]],\n", + "], ignore_index=True)\n", + "\n", + "print(f\"Total number of records after merging: {len(hackett_all)}\")\n", + "print(f\"\\nDistribution of each dataset:\")\n", + "print(hackett_all[\"source_dataset\"].value_counts())\n", + "hackett_all.head(10)" + ] + }, + { + "cell_type": "markdown", + "id": "ae37e5c8", + "metadata": {}, + "source": [ + "## Step2-Obtain the intersection of the binding dataset and kemmern" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "61741f38", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 224\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", + "
sample_idregulator_symbolregulator_locus_tagconditioncarbon_sourcetemperature_celsiusdto_empirical_pvaluedto_fdrkemmeren_sample_idperturbation_id_source
070SUM1YDR310CYPDglucose30.00.00.013631299kemmeren
1151STE12YHR084WAlphaglucose30.00.00.128768622kemmeren
2319MET31YPL038WSMunspecified30.00.00.0212991367kemmeren
370SUM1YDR310CYPDglucose30.00.00.006238299kemmeren
4151STE12YHR084WAlphaglucose30.00.00.117883622kemmeren
5242GAL80YML051WYPDglucose30.00.00.0000001006kemmeren
6319MET31YPL038WSMunspecified30.00.00.0263171367kemmeren
7323DIG1YPL049CBUT90glucose30.00.00.2365021372kemmeren
849NRG1YDR043CH2O2Higlucose30.00.00.230039221kemmeren
9198RGT1YKL038WYPDglucose30.00.00.003463786kemmeren
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_symbol regulator_locus_tag condition carbon_source \\\n", + "0 70 SUM1 YDR310C YPD glucose \n", + "1 151 STE12 YHR084W Alpha glucose \n", + "2 319 MET31 YPL038W SM unspecified \n", + "3 70 SUM1 YDR310C YPD glucose \n", + "4 151 STE12 YHR084W Alpha glucose \n", + "5 242 GAL80 YML051W YPD glucose \n", + "6 319 MET31 YPL038W SM unspecified \n", + "7 323 DIG1 YPL049C BUT90 glucose \n", + "8 49 NRG1 YDR043C H2O2Hi glucose \n", + "9 198 RGT1 YKL038W YPD glucose \n", + "\n", + " temperature_celsius dto_empirical_pvalue dto_fdr kemmeren_sample_id \\\n", + "0 30.0 0.0 0.013631 299 \n", + "1 30.0 0.0 0.128768 622 \n", + "2 30.0 0.0 0.021299 1367 \n", + "3 30.0 0.0 0.006238 299 \n", + "4 30.0 0.0 0.117883 622 \n", + "5 30.0 0.0 0.000000 1006 \n", + "6 30.0 0.0 0.026317 1367 \n", + "7 30.0 0.0 0.236502 1372 \n", + "8 30.0 0.0 0.230039 221 \n", + "9 30.0 0.0 0.003463 786 \n", + "\n", + " perturbation_id_source \n", + "0 kemmeren \n", + "1 kemmeren \n", + "2 kemmeren \n", + "3 kemmeren \n", + "4 kemmeren \n", + "5 kemmeren \n", + "6 kemmeren \n", + "7 kemmeren \n", + "8 kemmeren \n", + "9 kemmeren " + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "harbison_kemmeren = vdb.query(\"\"\"\n", + " SELECT\n", + " h.sample_id,\n", + " h.regulator_symbol,\n", + " h.regulator_locus_tag,\n", + " h.condition,\n", + " h.carbon_source,\n", + " h.temperature_celsius,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS kemmeren_sample_id,\n", + " d.perturbation_id_source\n", + " FROM harbison_meta h\n", + " JOIN dto_expanded d\n", + " ON CAST(h.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source = 'harbison'\n", + " WHERE d.perturbation_id_source = 'kemmeren'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(harbison_kemmeren)}\")\n", + "harbison_kemmeren.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "cd3e4a4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 389\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", + "
sample_idregulator_locus_tagcarbon_sourcetemperature_celsiusdto_empirical_pvaluedto_fdrkemmeren_sample_idperturbation_id_source
0478YLR182Wglucose250.00.084524913kemmeren
1291YGL237Cglucose250.00.010760521kemmeren
2208YEL018Wglucose250.00.218327361kemmeren
3487YLR256Wglucose250.00.000000932kemmeren
4269YGL071Wglucose250.00.174709471kemmeren
532YBR010Wglucose250.00.24030960kemmeren
6470YLR098Cglucose250.00.000020894kemmeren
7467YLR085Cglucose250.00.252903889kemmeren
8639YOL004Wglucose250.00.2153191233kemmeren
921YBL021Cglucose250.00.05686439kemmeren
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag carbon_source temperature_celsius \\\n", + "0 478 YLR182W glucose 25 \n", + "1 291 YGL237C glucose 25 \n", + "2 208 YEL018W glucose 25 \n", + "3 487 YLR256W glucose 25 \n", + "4 269 YGL071W glucose 25 \n", + "5 32 YBR010W glucose 25 \n", + "6 470 YLR098C glucose 25 \n", + "7 467 YLR085C glucose 25 \n", + "8 639 YOL004W glucose 25 \n", + "9 21 YBL021C glucose 25 \n", + "\n", + " dto_empirical_pvalue dto_fdr kemmeren_sample_id perturbation_id_source \n", + "0 0.0 0.084524 913 kemmeren \n", + "1 0.0 0.010760 521 kemmeren \n", + "2 0.0 0.218327 361 kemmeren \n", + "3 0.0 0.000000 932 kemmeren \n", + "4 0.0 0.174709 471 kemmeren \n", + "5 0.0 0.240309 60 kemmeren \n", + "6 0.0 0.000020 894 kemmeren \n", + "7 0.0 0.252903 889 kemmeren \n", + "8 0.0 0.215319 1233 kemmeren \n", + "9 0.0 0.056864 39 kemmeren " + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rossi_kemmeren = vdb.query(\"\"\"\n", + " SELECT\n", + " r.sample_id,\n", + " r.regulator_locus_tag,\n", + " r.carbon_source,\n", + " r.temperature_celsius,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS kemmeren_sample_id,\n", + " d.perturbation_id_source\n", + " FROM (\n", + " SELECT DISTINCT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " carbon_source,\n", + " temperature_celsius\n", + " FROM rossi_2021_af_combined_meta\n", + " ) r\n", + " JOIN dto_expanded d\n", + " ON CAST(r.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source = 'rossi_2021_af_combined'\n", + " WHERE d.perturbation_id_source = 'kemmeren'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(rossi_kemmeren)}\")\n", + "rossi_kemmeren.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "6071cf05", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of records that meet the criteria: 262\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", + "
sample_idregulator_locus_tagregulator_symbolconditiondto_empirical_pvaluedto_fdrkemmeren_sample_idperturbation_id_source
01YBL005WPDR3standard0.00.07766731kemmeren
15YBR083WTEC1standard0.00.06585974kemmeren
2100YJL056CZAP1standard0.00.302421709kemmeren
3101YJL110CGZF3standard0.00.117351723kemmeren
4102YJL176CSWI3standard0.00.127255736kemmeren
59YBR239CERT1standard0.00.147725106kemmeren
6104YJR060WCBF1standard0.00.166077754kemmeren
7106YJR140CHIR3standard0.00.358890772kemmeren
890YHR178WSTB5standard0.00.315486642kemmeren
911YBR289WSNF5standard0.00.133379120kemmeren
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag regulator_symbol condition \\\n", + "0 1 YBL005W PDR3 standard \n", + "1 5 YBR083W TEC1 standard \n", + "2 100 YJL056C ZAP1 standard \n", + "3 101 YJL110C GZF3 standard \n", + "4 102 YJL176C SWI3 standard \n", + "5 9 YBR239C ERT1 standard \n", + "6 104 YJR060W CBF1 standard \n", + "7 106 YJR140C HIR3 standard \n", + "8 90 YHR178W STB5 standard \n", + "9 11 YBR289W SNF5 standard \n", + "\n", + " dto_empirical_pvalue dto_fdr kemmeren_sample_id perturbation_id_source \n", + "0 0.0 0.077667 31 kemmeren \n", + "1 0.0 0.065859 74 kemmeren \n", + "2 0.0 0.302421 709 kemmeren \n", + "3 0.0 0.117351 723 kemmeren \n", + "4 0.0 0.127255 736 kemmeren \n", + "5 0.0 0.147725 106 kemmeren \n", + "6 0.0 0.166077 754 kemmeren \n", + "7 0.0 0.358890 772 kemmeren \n", + "8 0.0 0.315486 642 kemmeren \n", + "9 0.0 0.133379 120 kemmeren " + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mahendrawada_kemmeren = vdb.query(\"\"\"\n", + " SELECT\n", + " m.sample_id,\n", + " m.regulator_locus_tag,\n", + " m.regulator_symbol,\n", + " m.condition,\n", + " d.dto_empirical_pvalue,\n", + " d.dto_fdr,\n", + " d.perturbation_id_id AS kemmeren_sample_id,\n", + " d.perturbation_id_source\n", + " FROM (\n", + " SELECT DISTINCT\n", + " sample_id,\n", + " regulator_locus_tag,\n", + " regulator_symbol,\n", + " condition\n", + " FROM chec_mahendrawada_m2025_af_combined_meta\n", + " ) m\n", + " JOIN dto_expanded d\n", + " ON CAST(m.sample_id AS VARCHAR) = d.binding_id_id\n", + " AND d.binding_id_source LIKE '%mahendrawada%af_combined%'\n", + " WHERE d.perturbation_id_source = 'kemmeren'\n", + " AND d.dto_empirical_pvalue <= 0.01\n", + " ORDER BY d.dto_empirical_pvalue\n", + "\"\"\")\n", + "\n", + "print(f\"Number of records that meet the criteria: {len(mahendrawada_kemmeren)}\")\n", + "mahendrawada_kemmeren.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "274eab77", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total number of records after merging: 875\n", + "\n", + "Distribution among datasets:\n", + "source_dataset\n", + "rossi 389\n", + "mahendrawada 262\n", + "harbison 224\n", + "Name: count, dtype: int64\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", + "
sample_idregulator_locus_tagdto_empirical_pvaluedto_fdrkemmeren_sample_idperturbation_id_sourcesource_dataset
070YDR310C0.00.013631299kemmerenharbison
1151YHR084W0.00.128768622kemmerenharbison
2319YPL038W0.00.0212991367kemmerenharbison
370YDR310C0.00.006238299kemmerenharbison
4151YHR084W0.00.117883622kemmerenharbison
5242YML051W0.00.0000001006kemmerenharbison
6319YPL038W0.00.0263171367kemmerenharbison
7323YPL049C0.00.2365021372kemmerenharbison
849YDR043C0.00.230039221kemmerenharbison
9198YKL038W0.00.003463786kemmerenharbison
\n", + "
" + ], + "text/plain": [ + " sample_id regulator_locus_tag dto_empirical_pvalue dto_fdr \\\n", + "0 70 YDR310C 0.0 0.013631 \n", + "1 151 YHR084W 0.0 0.128768 \n", + "2 319 YPL038W 0.0 0.021299 \n", + "3 70 YDR310C 0.0 0.006238 \n", + "4 151 YHR084W 0.0 0.117883 \n", + "5 242 YML051W 0.0 0.000000 \n", + "6 319 YPL038W 0.0 0.026317 \n", + "7 323 YPL049C 0.0 0.236502 \n", + "8 49 YDR043C 0.0 0.230039 \n", + "9 198 YKL038W 0.0 0.003463 \n", + "\n", + " kemmeren_sample_id perturbation_id_source source_dataset \n", + "0 299 kemmeren harbison \n", + "1 622 kemmeren harbison \n", + "2 1367 kemmeren harbison \n", + "3 299 kemmeren harbison \n", + "4 622 kemmeren harbison \n", + "5 1006 kemmeren harbison \n", + "6 1367 kemmeren harbison \n", + "7 1372 kemmeren harbison \n", + "8 221 kemmeren harbison \n", + "9 786 kemmeren harbison " + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "common_cols = [\n", + " \"sample_id\", \"regulator_locus_tag\",\n", + " \"dto_empirical_pvalue\", \"dto_fdr\",\n", + " \"kemmeren_sample_id\", \"perturbation_id_source\"\n", + "]\n", + "\n", + "harbison_kemmeren[\"source_dataset\"] = \"harbison\"\n", + "rossi_kemmeren[\"source_dataset\"] = \"rossi\"\n", + "mahendrawada_kemmeren[\"source_dataset\"] = \"mahendrawada\"\n", + "\n", + "kemmeren_all = pd.concat([\n", + " harbison_kemmeren[common_cols + [\"source_dataset\"]],\n", + " rossi_kemmeren[common_cols + [\"source_dataset\"]],\n", + " mahendrawada_kemmeren[common_cols + [\"source_dataset\"]],\n", + "], ignore_index=True)\n", + "\n", + "print(f\"Total number of records after merging: {len(kemmeren_all)}\")\n", + "print(f\"\\nDistribution among datasets:\")\n", + "print(kemmeren_all[\"source_dataset\"].value_counts())\n", + "kemmeren_all.head(10)" + ] + }, + { + "cell_type": "markdown", + "id": "adad7e17", + "metadata": {}, + "source": [ + "## Step3-Take the intersection of Hackett and Kemmern" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "437193f8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of significant samples in Hackett: 404\n", + "Number of significant samples in Kemmeren: 472\n", + "Intersection (active set): 278\n", + "\n", + "Distribution among datasets:\n", + "source_dataset\n", + "mahendrawada 104\n", + "harbison 96\n", + "rossi 78\n", + "Name: count, dtype: int64\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", + "
source_datasetsample_idregulator_locus_tag
0harbison16YBR083W
1harbison248YMR016C
2harbison50YDR043C
3harbison323YPL049C
4harbison281YNL199C
5harbison86YER040W
6harbison282YNL199C
7harbison18YBR083W
8harbison7YBL103C
9harbison225YLR176C
\n", + "
" + ], + "text/plain": [ + " source_dataset sample_id regulator_locus_tag\n", + "0 harbison 16 YBR083W\n", + "1 harbison 248 YMR016C\n", + "2 harbison 50 YDR043C\n", + "3 harbison 323 YPL049C\n", + "4 harbison 281 YNL199C\n", + "5 harbison 86 YER040W\n", + "6 harbison 282 YNL199C\n", + "7 harbison 18 YBR083W\n", + "8 harbison 7 YBL103C\n", + "9 harbison 225 YLR176C" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use (source_dataset, sample_id) to uniquely identify a binding sample\n", + "hackett_keys = hackett_all[[\"source_dataset\", \"sample_id\", \"regulator_locus_tag\"]].drop_duplicates()\n", + "kemmeren_keys = kemmeren_all[[\"source_dataset\", \"sample_id\", \"regulator_locus_tag\"]].drop_duplicates()\n", + "\n", + "# Inner join = intersection\n", + "active_set = pd.merge(\n", + " hackett_keys,\n", + " kemmeren_keys,\n", + " on=[\"source_dataset\", \"sample_id\", \"regulator_locus_tag\"],\n", + " how=\"inner\"\n", + ")\n", + "\n", + "print(f\"Number of significant samples in Hackett: {len(hackett_keys)}\")\n", + "print(f\"Number of significant samples in Kemmeren: {len(kemmeren_keys)}\")\n", + "print(f\"Intersection (active set): {len(active_set)}\")\n", + "print(f\"\\nDistribution among datasets:\")\n", + "print(active_set[\"source_dataset\"].value_counts())\n", + "active_set.head(10)" + ] + }, + { + "cell_type": "markdown", + "id": "db6ea6c8", + "metadata": {}, + "source": [ + "## Step4-Statistics" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "6f262b24", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 119 different regulators in the active set.\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", + "
regulator_locus_tagn_active_samplesdatasets
0YEL009C18harbison, mahendrawada, rossi
1YHR206W6harbison, mahendrawada, rossi
2YBL103C6harbison, mahendrawada, rossi
3YPL049C6harbison, mahendrawada, rossi
4YDR043C5harbison, mahendrawada, rossi
5YDL056W5harbison, mahendrawada, rossi
6YHR084W5harbison, mahendrawada, rossi
7YMR037C5harbison, mahendrawada
8YNL068C5harbison, mahendrawada, rossi
9YER040W4harbison, mahendrawada, rossi
10YNL314W4harbison, mahendrawada
11YJR060W4harbison, mahendrawada, rossi
12YIR023W4harbison, mahendrawada
13YOR028C4harbison, rossi
14YPL038W4harbison, mahendrawada, rossi
15YKR099W4harbison, mahendrawada, rossi
16YLR451W4harbison, mahendrawada, rossi
17YOR358W4harbison, mahendrawada, rossi
18YBR083W4harbison, mahendrawada
19YDR310C3harbison, mahendrawada, rossi
\n", + "
" + ], + "text/plain": [ + " regulator_locus_tag n_active_samples datasets\n", + "0 YEL009C 18 harbison, mahendrawada, rossi\n", + "1 YHR206W 6 harbison, mahendrawada, rossi\n", + "2 YBL103C 6 harbison, mahendrawada, rossi\n", + "3 YPL049C 6 harbison, mahendrawada, rossi\n", + "4 YDR043C 5 harbison, mahendrawada, rossi\n", + "5 YDL056W 5 harbison, mahendrawada, rossi\n", + "6 YHR084W 5 harbison, mahendrawada, rossi\n", + "7 YMR037C 5 harbison, mahendrawada\n", + "8 YNL068C 5 harbison, mahendrawada, rossi\n", + "9 YER040W 4 harbison, mahendrawada, rossi\n", + "10 YNL314W 4 harbison, mahendrawada\n", + "11 YJR060W 4 harbison, mahendrawada, rossi\n", + "12 YIR023W 4 harbison, mahendrawada\n", + "13 YOR028C 4 harbison, rossi\n", + "14 YPL038W 4 harbison, mahendrawada, rossi\n", + "15 YKR099W 4 harbison, mahendrawada, rossi\n", + "16 YLR451W 4 harbison, mahendrawada, rossi\n", + "17 YOR358W 4 harbison, mahendrawada, rossi\n", + "18 YBR083W 4 harbison, mahendrawada\n", + "19 YDR310C 3 harbison, mahendrawada, rossi" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "regulator_counts = (\n", + " active_set\n", + " .groupby(\"regulator_locus_tag\")\n", + " .agg(\n", + " n_active_samples=(\"sample_id\", \"nunique\"),\n", + " datasets=(\"source_dataset\", lambda x: \", \".join(sorted(x.unique())))\n", + " )\n", + " .sort_values(\"n_active_samples\", ascending=False)\n", + " .reset_index()\n", + ")\n", + "\n", + "print(f\"There are {len(regulator_counts)} different regulators in the active set.\")\n", + "regulator_counts.head(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "53a236f7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAHpCAYAAACful8UAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAfLRJREFUeJzt3Xd4FNX79/HPJiGFVAKBAIEQem8BIbTQI00QlCJ+KdKUIFUpNoogRSmCFEEFLHQEKdKrIB1CUUA60msSQg+Z5w+e7I8lhSxmWQLv13XtdWXOzJy5Z3Y2yb3nzDkmwzAMAQAAAACAVOdg7wAAAAAAAHhRkXQDAAAAAGAjJN0AAAAAANgISTcAAAAAADZC0g0AAAAAgI2QdAMAAAAAYCMk3QAAAAAA2AhJNwAAAAAANkLSDQAAAACAjZB0A8BTGjBggEwm0zM5VtWqVVW1alXz8vr162UymTRv3rxncvw2bdooV65cz+RYTysmJkbt27eXv7+/TCaTunfvbtd4Tp48KZPJpGnTptk1jrTiWX6eAAB4lki6AUDStGnTZDKZzC9XV1dly5ZNYWFhGjt2rG7cuJEqxzl37pwGDBigiIiIVKkvNT3PsaXEF198oWnTpum9997TTz/9pP/973/P5LgzZszQmDFjnsmx8OJI6583e2nTpo3F7+qkXm3atJH08AvLpLY5dOiQfU8GwEvDyd4BAMDzZNCgQQoKCtL9+/d14cIFrV+/Xt27d9eoUaO0aNEiFS9e3LztJ598or59+1pV/7lz5zRw4EDlypVLJUuWTPF+K1eutOo4TyO52KZMmaK4uDibx/BfrF27VuXLl1f//v2f6XFnzJihAwcOJGhZDwwM1O3bt5UuXbpnGg/Shqf9XfCy69Spk2rWrGlePnHihD777DN17NhRlStXNpfnyZPH/HNAQICGDh2aoK5s2bLZNlgA+P9IugHgEXXq1FGZMmXMy/369dPatWtVv359vfbaazp48KDc3NwkSU5OTnJysu2v0Vu3bil9+vRydna26XGeJC0kjpcuXVLhwoXtHYZZfI8J4EVx8+ZNubu72zWGkJAQhYSEmJd37typzz77TCEhIXr77bcT3cfb2zvJdQDwLNC9HACeoHr16vr000916tQp/fzzz+byxJ5BXbVqlSpVqiQfHx95eHioQIEC+uijjyQ9fA67bNmykqS2bduauzjGP/NbtWpVFS1aVLt27VKVKlWUPn16876PP9Md78GDB/roo4/k7+8vd3d3vfbaa/r3338ttsmVK5e5q+WjHq3zSbEl9kz3zZs31atXL+XIkUMuLi4qUKCAvvrqKxmGYbGdyWRSly5dtHDhQhUtWlQuLi4qUqSIli9fnvgFf8ylS5fUrl07ZcmSRa6uripRooSmT59uXh//fPuJEye0dOlSc+wnT55Mss6pU6eqevXqypw5s1xcXFS4cGFNnDgx0W2XLVum0NBQeXp6ysvLS2XLltWMGTPM13Dp0qU6deqU+bjx1+nxZ7q/+uormUwmnTp1KsEx+vXrJ2dnZ12/ft1ctm3bNr366qvy9vZW+vTpFRoaqs2bN6fomo0bN05FihRR+vTplSFDBpUpU8YcsySdOnVKnTt3VoECBeTm5qaMGTPqzTffTHDN4h+72LRpk7p27So/Pz/5+PioU6dOunfvniIjI9WqVStlyJBBGTJkUO/evS3e//hr8NVXX2n06NEKDAyUm5ubQkNDdeDAgRSdy88//6zg4GC5ubnJ19dXzZs3T3CPJ+Xs2bNq166dsmXLJhcXFwUFBem9997TvXv3zNscP35cb775pnx9fZU+fXqVL19eS5cuTfQ6PH594u+99evXm8viP8d///23qlWrpvTp0yt79uwaMWKExX7Jfd6OHDmiJk2ayN/fX66urgoICFDz5s0VFRWV7Pn+8ccfevPNN5UzZ065uLgoR44c6tGjh27fvp1g20OHDqlp06by8/OTm5ubChQooI8//ti8Pv73299//6233npLGTJkUKVKlSRJsbGx+vzzz5UnTx65uLgoV65c+uijj3T37l2LY+zcuVNhYWHKlCmT3NzcFBQUpHfeecdim1mzZik4ONj8+SpWrJi+/vrrZM8ztd2/f18DBw5Uvnz55OrqqowZM6pSpUpatWrVM40DwIuLlm4ASIH//e9/+uijj7Ry5Up16NAh0W3++usv1a9fX8WLF9egQYPk4uKio0ePmhOlQoUKadCgQQm6QlaoUMFcx9WrV1WnTh01b95cb7/9trJkyZJsXEOGDJHJZFKfPn106dIljRkzRjVr1lRERIS5RT4lUhLbowzD0GuvvaZ169apXbt2KlmypFasWKEPP/xQZ8+e1ejRoy2237Rpk3799Vd17txZnp6eGjt2rJo0aaLT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Gih//vwvXcJtGIbu3LkjNzc3m9TftGlTvfnmmzp+/Lhy585tk2MAgDXoXg4AqeDEiRPat2+fatasmeJ9MmbMqFmzZsnJyUlDhgyx+phnzpzR999/r+rVq1sk3PHCw8NVrVo1fffddzpz5ozV9Ts4OJgT/ZMnT6ZonyNHjmjMmDGqVauWRVIyb948lS1b1iIJLliwoGrUqKE5c+YkW2d0dLRWrVqlt99+25xwS1KrVq3k4eFhsb+Li4v8/f1TFGtiEnum22QyqUuXLlq4cKGKFi0qFxcXFSlSRMuXL09xvQ8ePNBHH30kf39/ubu767XXXtO///6bYLu5c+cqODhYbm5uypQpk95++22dPXvWvL5NmzYaP368Oa741+MmT56sPHnyyMXFRWXLltWOHTtSHKs1fvvtN9WrV0/ZsmWTi4uL8uTJo88//1wPHjxIsO22bdtUt25dZciQQe7u7ipevLi+/vrrZOuPiIiQn5+fqlatan6U4OzZs3rnnXeUJUsW83vxww8/mPdZv369+T5r27at+RpNmzZNVatW1dKlS3Xq1Clzea5cuZKNIX67x2dYdXFxScklStQff/yhevXqKW/evFq9enWCnh7Lli1T5cqV5e7uLk9PT9WrV09//fWXxTbx3f1Pnz6t+vXry8PDQ9mzZzffH/v371f16tXl7u6uwMBAzZgxw2L/adOmyWQyadOmTeratav8/Pzk4+OjTp066d69e4qMjFSrVq2UIUMGZciQQb17905wDeLi4jRmzBgVKVJErq6uypIlizp16qTr169bbJcrVy7Vr19fK1asUJkyZeTm5qZvv/1W0sNeNd27d1eOHDnk4uKivHnzavjw4RbjYjw6XkFK7u3438O//fabNW8LANgMX+cDQCr4888/JUmlS5e2ar+cOXMqNDRU69atU3R0tEVS+STLli3TgwcP1KpVqyS3adWqldatW6fly5dbdFtPqWPHjklSkt2/7927pw0bNmjp0qX6/fffdeTIEbm4uKhq1apydHSU9PAf83379umdd95JsP8rr7yilStX6saNG/L09Ez0GPv371dsbKzKlCljUe7s7KySJUtqz549Vp+XtTZt2qRff/1VnTt3lqenp8aOHasmTZro9OnTT+waL0lDhgyRyWRSnz59dOnSJY0ZM0Y1a9ZURESEubVv2rRpatu2rcqWLauhQ4fq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v/76S5988onWrl2b4IudqKgoSf/3iEJi02U97s6dO6pXr56Cg4M1Z84ci0HtLl++rMjISE2ePFmTJ09OdP9Lly498RjW2Lp1qxYsWKA5c+YoKChIy5cvV4UKFVSzZk398ccfypIliw4cOCAnJycFBwc/sb68efOqVatW6tOnj1q0aKG5c+eaH8OQHo6JIEnVq1dPdP/HE3VXV1f5+flZlHl7eysgICDBFxre3t6J3gOP3y/e3t6SpBw5ciS7/5EjRxQVFaXMmTMnGuvj78Xj3drj69i3b1+Cc0iqDmvubcMwEv1SBwDsgaQbAFJBxowZFRsbm+yzyUk5cOCAHB0dE/2nNDnxU4jt27cvwbRI8fbt2ydJKly48BPrc3R0fOJAcBUrVtS+fft0+vRpLV26VEuXLtXnn3+ujz76SAEBAapXr57q1q2rGjVqmFu8fX195eLiovPnzyeoL74suZHRs2bNarHt4/v/l1HVU+rRxOhRxmMDSz0PnlWskZGRCg0NlZeXlwYNGqQ8efLI1dVVu3fvVp8+fSwGwkopFxcX1a1bV7/99puWL1+u+vXrm9fF1/f222+rdevWie6f1Bc/Tyt+rIby5ctLevil04oVK1SpUiXVqlVL69ev1+TJk1W3bt0ErfhJ6d27t65evaoRI0aoQ4cO+v77783JYfw5/vTTT4kOCPj4yPpJvdfW3APW1PHo/nFxccqcObN++eWXRPd/PJFO7MuxuLg41apVS7179060jvz586co1sTO6/r16wmelwcAeyHpBoBUULBgQUkPRzG35h//06dPa8OGDQoJCbE6Wa9Tp44cHR31008/JTmY2o8//ignJye9+uqrVtX9JDlz5tR7772n9957T7dv39batWvNSfi3334rFxcXRUdHy9nZWQ4ODipWrJh27tyZoJ5t27Ypd+7cyZ570aJF5eTkpJ07d1p0mb13754iIiIsyp5X8S2Y8QzD0NGjR833SmBgoKSHg0093sp5+PBh83op8S7Z9rB+/XpdvXpVv/76q6pUqWIuP3HihMV28d3yDxw48MQvdUwmk3755Rc1bNhQb775ppYtW2YeQd/Pz0+enp568OBBiup5mnVJbfvvv/+aW37ju/vXqFFDwcHBOn36tHkk7pQaPny4rl27pu+++04ZMmTQyJEjJf3ftcqcObNVMyHYQ548ebR69WpVrFjx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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "dist = regulator_counts[\"n_active_samples\"].value_counts().sort_index()\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 5))\n", + "dist.plot(kind=\"bar\", ax=ax)\n", + "ax.set_xlabel(\"Number of active samples per regulator\")\n", + "ax.set_ylabel(\"Number of regulators (TFs)\")\n", + "ax.set_title(\"Distribution of active sample counts across TFs\\n(DTO P≤0.01 in both Hackett & Kemmeren)\")\n", + "ax.set_xticklabels(ax.get_xticklabels(), rotation=0)\n", + "\n", + "for i, (x, y) in enumerate(zip(dist.index, dist.values)):\n", + " ax.text(i, y + 0.5, str(y), ha=\"center\", fontsize=9)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.11.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tfbpapi/datasets.yaml b/tfbpapi/datasets.yaml new file mode 100644 index 0000000..2d3044a --- /dev/null +++ b/tfbpapi/datasets.yaml @@ -0,0 +1,139 @@ +repositories: + BrentLab/harbison_2004: + dataset: + # binding + harbison_2004: + db_name: harbison + sample_id: + field: sample_id + carbon_source: + field: condition + path: media.carbon_source.compound + temperature_celsius: + field: condition + path: temperature_celsius + dtype: numeric + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + + BrentLab/rossi_2021: + # binding + carbon_source: + path: experimental_conditions.media.carbon_source.compound + temperature_celsius: + path: experimental_conditions.temperature_celsius + dataset: + rossi_2021_af_combined: + sample_id: + field: sample_id + regulator_locus_tag: + field: regulator_locus_tag + target_locus_tag: + field: target_locus_tag + + BrentLab/mahendrawada_2025: + temperature_celsius: + path: experimental_conditions.temperature_celsius + dataset: + # binding + chec_mahendrawada_m2025_af_combined_meta: + sample_id: + field: sample_id + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + + + BrentLab/hughes_2006: + dataset: + # perturbation + overexpression: + sample_id: + field: sample_id + carbon_source: + path: experimental_conditions.media.carbon_source.compound + temperature_celsius: + path: experimental_conditions.temperature_celsius + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + # perturbation + knockout: + sample_id: + field: sample_id + carbon_source: + path: experimental_conditions.media.carbon_source.compound + temperature_celsius: + path: experimental_conditions.temperature_celsius + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + + BrentLab/kemmeren_2014: + dataset: + # perturbation + kemmeren_2014: + db_name: kemmeren + sample_id: + field: sample_id + carbon_source: + path: experimental_conditions.media.carbon_source.compound + temperature_celsius: + path: experimental_conditions.temperature_celsius + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + + BrentLab/hackett_2020: + dataset: + # perturbation + hackett_2020: + db_name: hackett + sample_id: + field: sample_id + carbon_source: + path: experimental_conditions.media.carbon_source.compound + temperature_celsius: + path: experimental_conditions.temperature_celsius + dtype: numeric + regulator_locus_tag: + field: regulator_locus_tag + regulator_symbol: + field: regulator_symbol + + BrentLab/yeast_comparative_analysis: + dataset: + dto: + dto_pvalue: + field: dto_empirical_pvalue + dto_fdr: + field: dto_fdr + links: + binding_id: + - [BrentLab/harbison_2004, harbison_2004] + - [BrentLab/rossi_2021, rossi_2021_af_combined] + - [BrentLab/mahendrawada_2025, chec_mahendrawada_m2025_af_combined_meta] + perturbation_id: + - [BrentLab/kemmeren_2014, kemmeren_2014] + - [BrentLab/hackett_2020, hackett_2020] + - [BrentLab/hughes_2006, overexpression] + - [BrentLab/hughes_2006, knockout] + +factor_aliases: + carbon_source: + glucose: [D-glucose, dextrose, glu] + galactose: [D-galactose, gal] + raffinose: [D-raffinose] + +missing_value_labels: + carbon_source: unspecified + +description: + carbon_source: The carbon source provided during growth + temperature_celsius: Growth temperature in degrees Celsius