CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species.
The 40 matches · 5 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
- [1] § Methods › Enhancer models › Motif analysis › Human PBMC motif analysis ↔ Figure_2/DeepBICCN2_analysis.ipynb, lines 582–594 · score 0.89 · crested.tl.modisco.create_pattern_matrix, seqlet_count_log, discard_ic_threshold, pattern_parameter, sim_threshold, crested.tl.modisco.process_patterns
- [2] § Methods › Enhancer models › Motif analysis › Mouse cortex motif analysis ↔ Figure_2/DeepBICCN2_analysis.ipynb, lines 582–594 · score 0.89 · crested.tl.modisco.create_pattern_matrix, seqlet_count_log, discard_ic_threshold, pattern_parameter, sim_threshold, crested.tl.modisco.process_patterns
- [3] § Methods › Enhancer models › Motif analysis › Mouse cortex motif analysis ↔ Figure_3/DeepPBMC_analysis.ipynb, lines 498–513 · score 0.89 · crested.tl.modisco.create_pattern_matrix, seqlet_count_log, discard_ic_threshold, pattern_parameter, sim_threshold, crested.tl.modisco.process_patterns
- [4] § Methods › Enhancer models › Motif analysis › Human PBMC motif analysis ↔ Figure_3/DeepPBMC_analysis.ipynb, lines 498–513 · score 0.89 · crested.tl.modisco.create_pattern_matrix, seqlet_count_log, discard_ic_threshold, pattern_parameter, sim_threshold, crested.tl.modisco.process_patterns
- [5] § Methods › Enhancer models › Motif analysis › Human PBMC motif enrichment analysis using pycisTarget ↔ create_fasta_with_padded_bg_from_bed.sh, lines 1–78 · score 0.86 · padded bg, create fasta, create_cistarget_motif_databases.py, bed, background, arguments
- [6] § A CREsted human PBMC model captures validated TFBSs ↔ Figure_3/chip_seq_analysis.ipynb, lines 1204–1343 · score 0.81 · UniBind, ChIP seq, GATA3, RUNX1, CEBPA, SPI1
- [7] § Methods › Enhancer models › Motif analysis › Zebrafish motif analysis ↔ src/crested/tl/modisco/_tfmodisco.py, lines 1048–1179 · score 0.79 · crested tl modisco, discard_ic_threshold, sim_threshold, trim_ic_threshold, lite, tfmodisco
- [8] § Methods › Enhancer models › Motif analysis › Human PBMC UniBind comparison ↔ Figure_3/chip_seq_analysis.ipynb, lines 1204–1343 · score 0.78 · recursive_seqlets, ChIP seq, Unibind, GATA3, RUNX1, CEBPA
- [9] § Methods › CREsted workflow › Model training › Regression models ↔ src/crested/tl/losses/_poissonmultinomial.py, lines 8–124 · score 0.77 · Poisson loss, multinomial loss, loss function, log transformed, magnitude, truth
- [10] § Methods › Enhancer models › Model training › CREsted cancer cell line peak regression model, DeepCCL ↔ Figure_4/evaluate_deepccl_chrombpnet.ipynb, lines 427–447 · score 0.76 · HepG2, M059J, reverse complement, GM12878, MM001, MM099
- [11] § CREsted identifies high similarity between MES enhancer codes in cancer ↔ Figure_4/topic_scoring.ipynb, lines 316–376 · score 0.74 · melanoma MES, GBM MES, Spearman correlation, biopsy topic, DeepCCL, NPC
- [12] § Methods › Enhancer models › Model evaluation › CREsted predictions against topics derived from the cisTopic analysis of human Gliomas ↔ Figure_4/topic_scoring.ipynb, lines 316–376 · score 0.74 · melanoma MES, Spearman correlation, biopsy topics, DeepCCL, NPC, OPC
- [13] § CREsted provides detailed insights into enhancer codes of mouse cortical cell types ↔ Figure_5/compare_models.ipynb, lines 762–806 · score 0.73 · Micro PVMs, leptomeningeal cell, GABA, VLMC, glut, vascular
- [14] § Methods › Enhancer models › Motif analysis › Human PBMC ChIP–seq comparison ↔ Figure_3/chip_seq_analysis.ipynb, lines 58–121 · score 0.69 · POU2F2, ChIP seq, GATA3, RUNX1, CEBPA, SPI1
- [15] § CREsted identifies high similarity between MES enhancer codes in cancer ↔ Figure_4/chrombpnet_porting.ipynb, lines 26–103 · score 0.67 · ChromBPnet, m059j, ensemble, HepG2, GM12878, MM001
- [16] § Methods › Enhancer models › Model training › CREsted Borzoi transfer learning ↔ src/crested/tl/zoo/_dilated_cnn_decoupled.py, the whole file · a weak match · score 0.66 · Conv1D, Dense layer, CNN, cropping, Softplus, head
- [17] § Methods › Human cancer cell lines › Data processing ↔ Figure_4/topic_scoring.ipynb, lines 258–314 · score 0.66 · HepG2, DeepCCL, MEL, GM12878, MM001, MM099
- [18] § Methods › Enhancer models › Motif analysis › Human PBMC motif enrichment analysis using pycisTarget ↔ create_cistarget_motif_databases.py, lines 154–217 · score 0.66 · cisTarget motif database, create cistarget motif, background padding, bg, command, fasta
- [19] § CREsted identifies high similarity between MES enhancer codes in cancer ↔ Figure_4/enhancer_code_deepccl.ipynb, lines 127–272 · score 0.65 · melanoma cell, RUNX, ATF, CREB, TEAD, MM029
- [20] § Methods › Enhancer models › Model training › CREsted Borzoi transfer learning ↔ src/crested/tl/zoo/_dilated_cnn.py, the whole file · a weak match · score 0.64 · Conv1D, Dense layer, CNN, cropping, Softplus, convolutional
- [21] § Methods › Enhancer models › Model training › CREsted cancer cell line peak regression model, DeepCCL ↔ Figure_4/topic_scoring.ipynb, lines 58–91 · score 0.64 · HepG2, M059J, GM12878, MM001, MM099, MM029
- [22] § CREsted is a software package for efficient enhancer modeling and design ↔ src/crested/tl/losses/_cosinemse_log.py, the whole file · a weak match · score 0.63 · squared error, loss function, dynamically, MSE, cosine, sum
- [23] § Methods › Enhancer models › Model evaluation › Gene locus predictions ↔ src/crested/tl/_tools.py, lines 256–383 · score 0.63 · transcription start site, prediction score, upstream, downstream, window, locus
- [24] § CREsted is a software package for efficient enhancer modeling and design ↔ src/crested/tl/losses/_cosinemse.py, the whole file · a weak match · score 0.63 · squared error, loss function, dynamically, MSE, cosine, Optionally
- [25] § CREsted provides detailed insights into enhancer codes of mouse cortical cell types ↔ Figure_5/compare_models.ipynb, lines 762–806 · score 0.62 · excitatory neuron, SstChodl, oligodendrocytes, microglia, model, cell
- [26] § A CREsted human PBMC model captures validated TFBSs ↔ Figure_3/chip_seq_analysis.ipynb, lines 449–516 · score 0.62 · Precision recall curve, ChIP seq, AR, AP, Figure 3, Thresholding
- [27] § Methods › CREsted workflow › Data preprocessing › Peak-height preprocessing ↔ src/crested/_datasets.py, lines 109–174 · score 0.62 · cut sites, bigWig, consensus peaks, pseudobulk, coverage, tracks
- [28] § Methods › Enhancer models › Model training › gReLU mouse cortex peak regression model ↔ src/crested/_datasets.py, lines 109–174 · score 0.60 · cut site, peak regression, consensus peaks, BigWigs, cortex, mouse
- [29] § CREsted is a software package for efficient enhancer modeling and design ↔ src/crested/pp/_normalization.py, the whole file · a weak match · score 0.60 · low variability, standard deviation, Gini, retrieved, max, peak
- [30] § Methods › Enhancer models › Model training › ChromBPNet cancer cell line model ↔ src/crested/pl/corr/_heatmap.py, lines 182–330 · score 0.59 · Pearson correlation coefficient, ground truth, log transformed, split, heights, trained
- [31] § CREsted-trained models outperform large, pretrained models on cell-type-specific chromatin accessibility predictions ↔ src/crested/_datasets.py, lines 202–336 · score 0.58 · DeepBICCN2, transfer learning, mouse cortex, brain, Borzoi, maps
- [32] § Methods › Enhancer models › Input size benchmark › Comparison of seqlet locations and identified patterns ↔ src/tfmindi/pp/seqlets.py, lines 203–229 · score 0.57 · TF MInDi, recursive_seqlets, tangermeme, pp, scores
- [33] § Methods › Enhancer models › Model evaluation › EBF1 degradation analysis in B cells ↔ src/crested/tl/_tools.py, lines 256–383 · score 0.56 · score gene locus, crested tl, upstream, downstream
- [34] § Methods › CREsted workflow › Model training ↔ src/crested/tl/data/utils/_datawrapper.py, lines 429–472 · score 0.55 · TensorFlow, PyTorch, backends, Keras, CREsted, accessibility
- [35] § Methods › Enhancer models › Model training › ChromBPNet cancer cell line model ↔ src/crested/pl/corr/_violin.py, lines 15–89 · score 0.55 · ground truth, Pearson correlation, log transformed, fine tuned, split, heights
- [36] § CREsted-trained models outperform large, pretrained models on cell-type-specific chromatin accessibility predictions ↔ Figure_5/compare_models.ipynb, lines 69–122 · score 0.54 · double fine tuning, fine tuned Borzoi, frozen, consensus, peaks, model
- [37] § Methods › CREsted workflow › Enhancer code analysis › Pattern to TF matching ↔ src/crested/tl/modisco/_tfmodisco.py, lines 1969–2019 · score 0.53 · TF candidates, correlation threshold, Pearson, kept, cell
- [38] § Methods › CREsted workflow › Data preprocessing › Peak-height preprocessing ↔ src/crested/_io.py, lines 567–613 · score 0.52 · bigWig, boundaries, cut, coverage, chromosome, Optionally
- [39] § Methods › CREsted workflow › Model training ↔ src/crested/tl/_explainer.py, lines 83–214 · score 0.52 · TensorFlow, PyTorch, backends, Keras, sequence, predicting
- [40] § CREsted identifies high similarity between MES enhancer codes in cancer ↔ src/crested/_datasets.py, lines 202–336 · score 0.51 · DeepGlioma, DeepCCL, gliomas, accessibility, human, CREsted
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 1,382 lines · 48 KB · MIT · 4 matches
- # %%
- import pandas as pd
- import numpy as np
- import keras
- import crested
- import matplotlib.pyplot as plt
- import matplotlib
- %matplotlib inline
- matplotlib.rcParams["pdf.fonttype"] = 42
- matplotlib.rcParams["ps.fonttype"] = 42
- from pathlib import Path
- import anndata
- import h5py
- # %% [markdown]
- # Download the data for the notebooks from the dedicated Zenodo link of the CREsted paper. Then use it below.
- # %%
- DATA_DIR ="../../../crested_data/Figure_3/pbmc/"
- # %% [markdown]
- # # Load hg38 genome
- # %% [markdown]
- # This notebook requires a hg38 fasta file and a hg38 chromosome sizes file. You can download that here for example: https://hgdownload.soe.ucsc.edu/downloads.html
- # Once downloaded, we load them to the notebook.
- # %%
- genome_dir = "../../../human/genome/"
- genome_fasta = f"{genome_dir}hg38.fa"
- genome_chrom_sizes = f"{genome_dir}hg38.chrom.sizes"
- # %%
- genome = crested.Genome(genome_fasta, genome_chrom_sizes)
- crested.register_genome(genome)
- # %% [markdown]
- # # Load DeepPBMC
- # %%
- bigwigs_folder = f"{DATA_DIR}bw/"
- regions_file = f"{DATA_DIR}consensus_regions.bed"
- # %%
- adata = anndata.read_h5ad(f"{DATA_DIR}pbmc_filtered.h5ad")
- adata.obs_names = pd.Index([
- 'Bcell', 'CD14_monocyte', 'CD16_monocyte',
- 'CD4_Tcell', 'Cytotoxic_T_cell',
- 'Dendritic_cell', 'Natural_killer_cell'
- ])
- # %%
- model_path =f"{DATA_DIR}DeepPBMC.keras"
- model = keras.models.load_model(
- model_path, compile=False
- )
- # %% [markdown]
- # # Analysis on modisco top regions
- # %% [markdown]
- # First we define all relevant files, patterns and directories and add them to the tf_dict dictionary.
- # %%
- tf_dict = {}
- tf_list = ['PAX5','EBF1','POU2F2', 'RUNX1', 'GATA3', 'ETS1', 'CEBPA', 'SPI1']
- ct_list= ['Bcell','Bcell','Bcell','CD4_Tcell','CD4_Tcell','CD4_Tcell', 'CD14_monocyte', 'CD14_monocyte']
- chip_bed_files = [
- f'{DATA_DIR}/chip/pax5/pax5_CHIP.bed',
- f"{DATA_DIR}/chip/ebf1/ebf1_CHIP.bed",
- f"{DATA_DIR}/chip/pou2f2/pou2f2_chip.bed",
- f"{DATA_DIR}/chip/runx1/runx1_CHIP.bed",
- f"{DATA_DIR}/chip/gata3_2/gata3_CHIP.bed",
- f"{DATA_DIR}/chip/ets1/ets1_CHIP.bed",
- f"{DATA_DIR}/chip/cebpa/cebpa_CHIP.bed",
- f"{DATA_DIR}/chip/spi1_2/spi1_CHIP.bed",
- ]
- unibind_bed_files = [
- f"{DATA_DIR}/chip/pax5/pax5.bed",
- f"{DATA_DIR}/chip/ebf1/ebf1_unibind.bed",
- None,
- f"{DATA_DIR}/chip/runx1/runx1.bed",
- f"{DATA_DIR}/chip/gata3_2/gata3.bed",
- f"{DATA_DIR}/chip/ets1/ets1_unibind.bed",
- f"{DATA_DIR}/chip/cebpa/cebpa.bed",
- f"{DATA_DIR}/chip/spi1_2/spi1.bed",
- ]
- chip_bw_files = [
- f"{DATA_DIR}chip/pax5/PAX5.bigWig",
- f"{DATA_DIR}chip/ebf1/EBF1.bigWig",
- f"{DATA_DIR}chip/pou2f2/pou2f2.bigWig",
- f"{DATA_DIR}chip/runx1/runx1.bw",
- f"{DATA_DIR}chip/gata3_2/gata3.bw",
- f"{DATA_DIR}chip/ets1/ets1.bw",
- f"{DATA_DIR}chip/cebpa/cebpa.bw",
- f"{DATA_DIR}chip/spi1_2/spi1.bw",
- ]
- # Modisco pattern numbers
- pattern_nrs = [[5, 6, 9, 12, 13, 19, 23],
- [3,24],
- [2],
- [2, 4, 19, 22, 25],
- [7], #[3,7,27]
- [0],
- [0, 4],
- [2]]
- for i, tf in enumerate(tf_list):
- tf_dict[tf]={}
- tf_dict[tf]['cell_type']=ct_list[i]
- tf_dict[tf]['chip_bed']=chip_bed_files[i]
- tf_dict[tf]['chip_bw']=chip_bw_files[i]
- tf_dict[tf]['unibind_bed']=unibind_bed_files[i]
- tf_dict[tf]['pattern_indices']=pattern_nrs[i]
- top_n = 1000
- contribution_dir=f"{DATA_DIR}modisco/"
- modisco_regions=f"{DATA_DIR}modisco_regions.csv"
- # %% [markdown]
- # ## Load the patterns from the modisco files
- # %%
- def recursively_load_h5_data(h5_group):
- """
- Recursively load all data from an HDF5 group or dataset into Python objects.
- """
- if isinstance(h5_group, h5py.Group):
- # If it's a group, recurse into its items
- return {key: recursively_load_h5_data(h5_group[key]) for key in h5_group.keys()}
- elif isinstance(h5_group, h5py.Dataset):
- # If it's a dataset, load its value into memory
- return h5_group[()]
- else:
- # Unknown type, return as-is
- return h5_group
- for tf in tf_dict:
- ppms = []
- patterns = []
- h5_file = contribution_dir+'/'+tf_dict[tf]['cell_type']+'_modisco_results.h5'
- with h5py.File(h5_file) as hdf5_results:
- for metacluster_name in ['pos_patterns']:
- pattern_idx = 0
- for i in range(len(list(hdf5_results[metacluster_name]))):
- p = "pattern_" + str(i)
- pattern = hdf5_results[metacluster_name][p]
- pattern_data = recursively_load_h5_data(pattern)
- patterns.append(pattern_data)
- tf_dict[tf]['patterns']=patterns
- # %% [markdown]
- # ## Load the regions used for modisco
- # %%
- for tf in tf_dict:
- file_path = modisco_regions
- region_df = pd.read_csv(file_path)
- region_df['Class name'] = region_df['Class name'].str.lstrip('Merged__')
- region_df['Class name'] = region_df['Class name'].replace('B_cell','Bcell')
- region_df['Class name'] = region_df['Class name'].replace('CD4_T_cell','CD4_Tcell')
- region_df = region_df.loc[region_df['Class name']==tf_dict[tf]['cell_type']]
- region_df = region_df.head(top_n)
- region_df
- tf_dict[tf]['region_df']=region_df
- # %%
- tf_dict['EBF1']['region_df']
- # %% [markdown]
- # ## Load the ChIP Bed files
- # %%
- # Define the base column names for a standard BED file
- base_column_names = ["chrom", "start", "end", "name", "score", "strand", "signal_value", "p-val", "-log10(q-value)", "peak-summit"]
- chip_df_list=[]
- for tf in tf_dict:
- bed_file_path = tf_dict[tf]['chip_bed']
- bed_preview = pd.read_csv(bed_file_path, sep="\t", header=None, nrows=1)
- # Extend the column names list to match the number of columns in the file
- extra_columns = [f"extra_col_{i}" for i in range(len(bed_preview.columns) - len(base_column_names))]
- column_names = base_column_names + extra_columns
- # Read the BED file into a DataFrame with the dynamically generated column names
- chip_all_df = pd.read_csv(bed_file_path, sep="\t", header=None, names=column_names)
- chip_df_list.append(chip_all_df)
- # %% [markdown]
- # ## Load the UniBind Bed files
- # %%
- import pandas as pd
- # Define the base column names for a standard BED file
- base_column_names = ["chrom", "start", "end", "name", "score", "strand", "signal_value", "p-val", "-log10(q-value)", "peak-summit"]
- unibind_df_list=[]
- for tf in tf_dict:
- # Read the BED file to determine the number of columns
- bed_file_path = tf_dict[tf]['unibind_bed']
- if bed_file_path:
- bed_preview = pd.read_csv(bed_file_path, sep="\t", header=None, nrows=1)
- # Extend the column names list to match the number of columns in the file
- extra_columns = [f"extra_col_{i}" for i in range(len(bed_preview.columns) - len(base_column_names))]
- column_names = base_column_names + extra_columns
- # Read the BED file into a DataFrame with the dynamically generated column names
- unibind_df = pd.read_csv(bed_file_path, sep="\t", header=None, names=column_names)
- unibind_df_list.append(unibind_df)
- else:
- unibind_df_list.append(None)
- # %% [markdown]
- # ## Calculate overlapping Unibind hits in the top regions used in modisco
- # %%
- for i,tf in enumerate(tf_dict):
- possible_hits = []
- regions_to_process = tf_dict[tf]['region_df'].head(top_n) if top_n else tf_dict[tf]['region_df']
- unibind_df = unibind_df_list[i]
- if unibind_df is not None:
- for _, region in regions_to_process.iterrows():
- overlaps = unibind_df[
- (unibind_df["chrom"] == region["chr"]) &
- (unibind_df["start"] < region["end"] - 557) &
- (unibind_df["end"] > region["start"] + 557)
- ]
- for _, overlap in overlaps.iterrows():
- possible_hits.append({
- "region": region['region'],
- "region_chr": region["chr"],
- "region_start": region["start"],
- "region_end": region["end"],
- "chip_chr": overlap["chrom"],
- "chip_start": int(overlap["start"]),
- "chip_end": int(overlap["end"]),
- "chip_name": overlap["name"],
- "chip_score": overlap["score"],
- "chip_strand": overlap["strand"],
- "status": "Possible Hit"
- })
- possible_hits_df_unibind = pd.DataFrame(possible_hits)
- tf_dict[tf]['unibind_hits_df'] = possible_hits_df_unibind
- else:
- tf_dict[tf]['unibind_hits_df'] = None
- # %%
- tf_dict['RUNX1']['unibind_hits_df']
- # %% [markdown]
- # ## Retrieve seqlet positions inside top modisco regions
- # %%
- for tf in tf_dict:
- seqlet_starts=[]
- seqlet_chroms=[]
- seqlet_ends=[]
- seqlet_regions=[]
- seqlet_attributions=[]
- pattern_nrs= tf_dict[tf]['pattern_indices']
- patterns = tf_dict[tf]['patterns']
- region_df = tf_dict[tf]['region_df']
- for pattern_nr in pattern_nrs:
- pattern = patterns[pattern_nr]
- for i in range(pattern['seqlets']['n_seqlets'][0]):
- region = pattern['seqlets']['example_idx'][i]
- if region<top_n:
- region_id = region_df.iloc[region]['region']
- region_chr = region_df.iloc[region]['chr']
- region_start = region_df.iloc[region]['start']
- region_end = region_df.iloc[region]['end']
- seqlet_start = region_start+pattern['seqlets']['start'][i]+557
- seqlet_end = seqlet_start+30
- seqlet_chrom = region_chr
- average_contrib=pattern['seqlets']['contrib_scores'][i]
- average_contrib = average_contrib[average_contrib!=0]
- average_contrib = np.mean(average_contrib)
- seqlet_starts.append(seqlet_start)
- seqlet_ends.append(seqlet_end)
- seqlet_chroms.append(seqlet_chrom)
- seqlet_regions.append(region_id)
- seqlet_attributions.append(average_contrib)
- seqlet_df= pd.DataFrame({
- 'region': seqlet_regions,
- 'chr':seqlet_chroms,
- 'start':seqlet_starts,
- 'end':seqlet_ends,
- 'average_contrib':seqlet_attributions
- })
- tf_dict[tf]['seqlet_df']=seqlet_df
- # %%
- tf='POU2F2'
- tf_dict[tf]['seqlet_df']
- # %% [markdown]
- # ## Calculate precision and recall of Seqlets inside ChIP-seq peaks
- # %%
- def calculate_precision_recall(seqlet_df, chip_all_df, region_df, top_n=None):
- """
- Calculate precision and recall for overlap between seqlet_df and chip_all_df, with region_df for context.
- Parameters:
- - seqlet_df (pd.DataFrame): DataFrame with seqlets.
- - chip_all_df (pd.DataFrame): DataFrame with ChIP-seq data.
- - region_df (pd.DataFrame): DataFrame with regions for possible hits.
- - top_n (int, optional): Limit the number of regions processed from region_df.
- Returns:
- - results_df (pd.DataFrame): DataFrame with detailed overlap information.
- - precision (float): Precision value.
- - recall (float): Recall value.
- """
- # Initialize results and counts
- results = []
- true_positives = 0
- false_positives = 0
- false_negatives = 0
- # Step 1: True Positives and False Positives
- for _, seqlet in seqlet_df.iterrows():
- overlaps = chip_all_df[
- (chip_all_df["chrom"] == seqlet["chr"]) &
- (chip_all_df["start"] < seqlet["end"] + 5) &
- (chip_all_df["end"] > seqlet["start"] - 5)
- ]
- if not overlaps.empty:
- for _, overlap in overlaps.iterrows():
- results.append({
- "region": seqlet['region'],
- "seqlet_chr": seqlet["chr"],
- "seqlet_start": seqlet["start"],
- "seqlet_end": seqlet["end"],
- "seqlet_average_contrib": seqlet["average_contrib"],
- "chip_chr": overlap["chrom"],
- "chip_start": int(overlap["start"]),
- "chip_end": int(overlap["end"]),
- "chip_name": overlap["name"],
- "chip_score": overlap["score"],
- "chip_strand": overlap["strand"],
- "status": "True Positive"
- })
- true_positives += 1
- else:
- results.append({
- "region": seqlet['region'],
- "seqlet_chr": seqlet["chr"],
- "seqlet_start": seqlet["start"],
- "seqlet_end": seqlet["end"],
- "seqlet_average_contrib": seqlet["average_contrib"],
- "chip_chr": np.nan,
- "chip_start": np.nan,
- "chip_end": np.nan,
- "chip_name": np.nan,
- "chip_score": np.nan,
- "chip_strand": np.nan,
- "status": "False Positive"
- })
- false_positives += 1
- # Step 2: Possible Hits (region_df vs chip_all_df)
- possible_hits = []
- regions_to_process = region_df.head(top_n) if top_n else region_df
- for _, region in regions_to_process.iterrows():
- overlaps = chip_all_df[
- (chip_all_df["chrom"] == region["chr"]) &
- (chip_all_df["start"] < region["end"] - 557) &
- (chip_all_df["end"] > region["start"] + 557)
- ]
- for _, overlap in overlaps.iterrows():
- possible_hits.append({
- "region": region['region'],
- "region_chr": region["chr"],
- "region_start": region["start"],
- "region_end": region["end"],
- "chip_chr": overlap["chrom"],
- "chip_start": int(overlap["start"]),
- "chip_end": int(overlap["end"]),
- "chip_name": overlap["name"],
- "chip_score": overlap["score"],
- "chip_strand": overlap["strand"],
- "status": "Possible Hit"
- })
- possible_hits_df = pd.DataFrame(possible_hits)
- # Step 3: False Negatives (possible hits not in seqlet_df)
- for _, hit in possible_hits_df.iterrows():
- matches = seqlet_df[
- (seqlet_df["chr"] == hit["chip_chr"]) &
- (seqlet_df["start"] < hit["chip_end"] + 5) &
- (seqlet_df["end"] > hit["chip_start"] - 5)
- ]
- if matches.empty:
- results.append({
- "region": hit['region'],
- "seqlet_chr": np.nan,
- "seqlet_start": np.nan,
- "seqlet_end": np.nan,
- "seqlet_average_contrib": np.nan,
- "chip_chr": hit["chip_chr"],
- "chip_start": hit["chip_start"],
- "chip_end": hit["chip_end"],
- "chip_name": hit["chip_name"],
- "chip_score": hit["chip_score"],
- "chip_strand": hit["chip_strand"],
- "status": "False Negative"
- })
- false_negatives += 1
- # Convert results to DataFrame
- results_df = pd.DataFrame(results)
- # Calculate precision and recall
- precision = true_positives / (true_positives + false_positives) if true_positives + false_positives > 0 else 0
- recall = true_positives / (true_positives + false_negatives) if true_positives + false_negatives > 0 else 0
- return results_df, precision, recall, true_positives, false_positives, false_negatives, possible_hits_df
- # %%
- # Assuming seqlet_df, chip_all_df, and region_df are defined
- for i, tf in enumerate(tf_dict):
- print(tf)
- results_df, precision, recall, tp, fp, fn, possible_hits_df = calculate_precision_recall(tf_dict[tf]['seqlet_df'], chip_df_list[i], tf_dict[tf]['region_df'], top_n)
- tf_dict[tf]['results_df']=results_df
- tf_dict[tf]['chip_hits_df']=possible_hits_df
- # Display results
- print(f"Precision: {precision:.3f}")
- print(f"Recall: {recall:.3f}")
- # %% [markdown]
- # ### Fig. 3e
- # %%
- from tqdm import tqdm
- def calculate_precision_recall_by_threshold(seqlet_df, chip_all_df, region_df, thresholds, top_n=None):
- """
- Calculate precision and recall for varying thresholds on 'average_contrib' in seqlet_df.
- Parameters:
- - seqlet_df (pd.DataFrame): DataFrame with seqlets.
- - chip_all_df (pd.DataFrame): DataFrame with ChIP-seq data.
- - region_df (pd.DataFrame): DataFrame with regions for possible hits.
- - thresholds (list or np.array): Thresholds for filtering seqlet_df by 'average_contrib'.
- - top_n (int, optional): Limit the number of regions processed from region_df.
- Returns:
- - precision_list (list): List of precision values for each threshold.
- - recall_list (list): List of recall values for each threshold.
- - thresholds (list): List of thresholds used.
- - avg_precision (float): Average precision across thresholds.
- - avg_recall (float): Average recall across thresholds.
- """
- precision_list = []
- recall_list = []
- for threshold in thresholds:
- # Filter seqlet_df by current threshold
- filtered_seqlet_df = seqlet_df.loc[seqlet_df['average_contrib'] > threshold].reset_index(drop=True)
- # Calculate precision and recall using the previous function
- _, precision, recall, _, _, _, _ = calculate_precision_recall(filtered_seqlet_df, chip_all_df, region_df, top_n)
- # Store results
- precision_list.append(precision)
- recall_list.append(recall)
- # Calculate average precision and recall
- avg_precision = np.mean(precision_list)
- avg_recall = np.mean(recall_list)
- return precision_list, recall_list, thresholds, avg_precision, avg_recall
- plt.figure(figsize=(6, 6))
- for i, tf in tqdm(enumerate(tf_list), total=len(tf_dict)):
- # Define thresholds
- thresholds = np.linspace(np.min(tf_dict[tf]['seqlet_df']['average_contrib'].values),
- np.max(tf_dict[tf]['seqlet_df']['average_contrib'].values) * 0.95, 5)
- # Calculate precision, recall, and averages
- precision_list, recall_list, thresholds, avg_precision, avg_recall = calculate_precision_recall_by_threshold(
- tf_dict[tf]['seqlet_df'], chip_df_list[i], tf_dict[tf]['region_df'], thresholds
- )
- # Plot all PR curves on the same figure
- plt.plot(recall_list, precision_list, marker='o', label=f'{tf} (AP={avg_precision:.3f}, AR={avg_recall:.3f})')
- # Formatting
- plt.title("Precision-Recall Curves by Thresholding on 'average_contrib'")
- plt.xlabel("Recall")
- plt.ylabel("Precision")
- plt.grid(True, linestyle='--', alpha=0.7)
- plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
- plt.xlim([0, 1])
- plt.ylim([0, 1])
- #plt.savefig('paperfigs/all_TFs_PR.pdf', bbox_inches='tight')
- plt.show()
- # %%
- tf_dict[tf]['chip_hits_df'] # Dataframe for ChIP-seq peaks inside top modisco regions
- # %% [markdown]
- # ## Closer look at example regions
- # %%
- tf='POU2F2'
- region_index = 3
- region_df = tf_dict[tf]['region_df']
- results_df = tf_dict[tf]['results_df']
- region_id = region_df.iloc[region_index]['region']
- region_data = results_df[results_df['region'] == region_id]
- chrom=region_id.split(':')[0]
- start=int(region_id.split(':')[1].split('-')[0])
- end=int(region_id.split(':')[1].split('-')[1])
- # Extract non-NaN seqlet coordinates
- seqlet_coords = region_data.dropna(subset=["seqlet_chr"]).apply(
- lambda row: (int(row["seqlet_start"]-start), int(row["seqlet_end"]-start)), axis=1
- ).tolist() if len(region_data.dropna(subset=["seqlet_chr"]))>0 else None
- # Extract non-NaN ChIP coordinates
- chip_coords = region_data.dropna(subset=["chip_chr"]).apply(
- lambda row: (int(row["chip_start"]-start), int(row["chip_end"]-start)), axis=1
- ).tolist() if len(region_data.dropna(subset=["chip_chr"]))>0 else None
- seqlet_coords
- chip_coords
- region_data
- # %%
- adata.obs_names
- # %%
- classes_of_interest = [tf_dict[tf]['cell_type']]
- sequence = genome.fetch(chrom, start, end).upper()
- class_idx = list(adata.obs_names.get_indexer(classes_of_interest))
- scores, one_hot_encoded_sequences = crested.tl.contribution_scores(
- sequence,
- target_idx=class_idx,
- model=model,
- )
- # %%
- # Highlight identified seqlets
- crested.pl.patterns.contribution_scores(
- scores,
- one_hot_encoded_sequences,
- sequence_labels=[''],
- class_labels=classes_of_interest,
- zoom_n_bases=500,
- title="Test set region",
- height=3.5,
- highlight_positions=seqlet_coords
- )
- # %%
- # Highlight ChIP peaks
- crested.pl.patterns.contribution_scores(
- scores,
- one_hot_encoded_sequences,
- sequence_labels=[''],
- class_labels=classes_of_interest,
- zoom_n_bases=500,
- title="Test set region",
- height=3.5,
- highlight_positions=chip_coords
- )
- # %%
- # Plot ChIP BigWigs
- for chip_bw in chip_bw_files:
- filename = chip_bw.split('/')[-1].split('.')[0].upper() # Extract and format filename
- # Read BigWig region
- vals, _ = crested.utils.read_bigwig_region(chip_bw, (chrom, start + 807, end - 807))
- # Plot
- plt.figure(figsize=(50, 2))
- plt.plot(np.arange(0, 500), vals)
- # Add filename as text in the top-left corner
- plt.text(0.02, 0.95, filename, transform=plt.gca().transAxes, fontsize=18, fontweight='bold',
- verticalalignment='top',horizontalalignment='left', bbox=dict(facecolor='white', alpha=0.7, edgecolor='black'))
- plt.show()
- # %%
- # Plot all possible seqlets identified by tangermeme
- from tangermeme.seqlet import recursive_seqlets
- from crested.utils import one_hot_encode_sequence
- oh_seq = one_hot_encode_sequence(sequence)[0].T
- X_attr = scores[0,0].T
- X_attr=X_attr*oh_seq
- X_attr = np.expand_dims(np.sum(X_attr, axis=0),axis=0)
- seqlets = recursive_seqlets(X_attr, threshold=0.05)
- highlight_positions=[]
- for i in range(len(seqlets)):
- start_ = seqlets.iloc[i]['start']
- end_ = seqlets.iloc[i]['end']
- highlight_positions.append((start_,end_))
- crested.pl.patterns.contribution_scores(
- scores,
- one_hot_encoded_sequences,
- sequence_labels=[''],
- class_labels=classes_of_interest,
- zoom_n_bases=500,
- title="Test set region",
- height=3.5,
- highlight_positions=highlight_positions
- )
- seqlets
- # %%
- # Plot region predictions
- sequence = genome.fetch(chrom, start, end).upper()
- prediction = crested.tl.predict(sequence, model)
- crested.pl.bar.prediction(prediction, classes=list(adata.obs_names))
- # %%
- # Plot scATAC profile
- bigwigs_folder = f"{DATA_DIR}/bw/"
- vals = np.zeros(len(list(adata.obs_names)))
- for i, ct in enumerate(list(adata.obs_names)):
- print(ct)
- values = crested.utils.read_bigwig_region(bigwigs_folder+ct+'.bw', (chrom,start+557,end-557))
- bw_values=values[0]
- midpoints=values[1]
- vals[i] = np.sum(bw_values)*adata.obsm['weights'][i]
- plt.figure(figsize=(20,3))
- plt.bar(list(adata.obs_names), vals)
- plt.title('ATAC profile')
- plt.show()
- # %% [markdown]
- # ## Recall of UniBind hits with seqlet calling
- # %%
- def get_row_with_overlap(df, input_start, input_end, overlap=0.5):
- """
- Retrieves the row with at least 30% overlap with the given input range.
- Parameters:
- df (pd.DataFrame): Input DataFrame with 'start' and 'end' columns.
- input_start (int): Start of the input range.
- input_end (int): End of the input range.
- overlap (float): Minimum overlap fraction required.
- Returns:
- pd.DataFrame: Row(s) with at least the specified overlap, sorted by overlap.
- """
- def overlap_fraction(row):
- # Calculate overlap
- overlap_start = max(row['start'], input_start)
- overlap_end = min(row['end'], input_end)
- overlap_length = max(0, overlap_end - overlap_start + 1) # Ensure non-negative
- row_length = row['end'] - row['start'] + 1
- return overlap_length / row_length
- # Calculate overlap for each row and add as a new column
- df['overlap'] = df.apply(overlap_fraction, axis=1)
- # Filter rows with at least the specified overlap
- overlap_rows = df[df['overlap'] >= overlap]
- # Sort the filtered rows by the overlap column
- overlap_rows = overlap_rows.sort_values(by='overlap', ascending=False)
- return overlap_rows
- # %%
- from tqdm import tqdm
- for tf in tf_dict:
- print(tf)
- filename=contribution_dir+'/'+tf_dict[tf]['cell_type']+'_contrib.npz'
- scores = np.load(filename)['arr_0']
- scores = np.transpose(scores,(0,2,1))
- oh_seqs=np.load(contribution_dir+'/'+tf_dict[tf]['cell_type']+'_oh.npz')['arr_0']
- oh_seqs = np.transpose(oh_seqs,(0,2,1))
- seqlet_starts=[]
- seqlet_ends = []
- p_values=[]
- attributions=[]
- attributions_exact=[]
- attributions_exact_padded=[]
- if tf_dict[tf]['unibind_hits_df'] is None:
- tf_dict[tf]['unibind_recall_df'] = None
- continue
- for i in tqdm(range(top_n)):
- oh_seq = oh_seqs[i].T
- X_attr = scores[i].T
- X_attr=X_attr*oh_seq
- X_attr = np.expand_dims(np.sum(X_attr, axis=0),axis=0)
- seqlets = recursive_seqlets(X_attr, threshold=0.05)
- region_id=tf_dict[tf]['region_df'].iloc[i]['region']
- if region_id in tf_dict[tf]['unibind_hits_df']['region'].values:
- rows = tf_dict[tf]['unibind_hits_df'].loc[tf_dict[tf]['unibind_hits_df']['region']==region_id]
- if len(rows)==1:
- row=rows
- chrom= row['region_chr']
- start=int(row['region_start'].iloc[0])
- end=int(row['region_end'].iloc[0])
- tf_start=int(row['chip_start'].iloc[0]-start)
- tf_end = int(row['chip_end'].iloc[0]-start)
- overlap_rows = get_row_with_overlap(seqlets, tf_start, tf_end)
- if len(overlap_rows)>0:
- r = overlap_rows.iloc[0]
- seq_start=start+int(r['start'])
- seq_end = start+int(r['end'])
- seqlet_starts.append(seq_start)
- seqlet_ends.append(seq_end)
- p_values.append(r['p-value'])
- attributions.append(r['attribution'])
- attribution = (np.mean(X_attr[0,int(r['start']):int(r['end'])]))
- padding=(30-(int(r['end'])-int(r['start'])))//2
- attribution_padded = (np.mean(X_attr[0,int(r['start'])-padding:int(r['end'])+padding]))
- attributions_exact.append(attribution)
- attributions_exact_padded.append(attribution_padded)
- else:
- seqlet_starts.append(np.nan)
- seqlet_ends.append(np.nan)
- p_values.append(np.nan)
- attributions.append(np.nan)
- attributions_exact.append(np.nan)
- attributions_exact_padded.append(np.nan)
- else:
- for i in range(len(rows)):
- chrom= rows['region_chr']
- start=int(rows['region_start'].iloc[i])
- end=int(rows['region_end'].iloc[i])
- tf_start=int(rows['chip_start'].iloc[i]-start)
- tf_end = int(rows['chip_end'].iloc[i]-start)
- overlap_rows = get_row_with_overlap(seqlets, tf_start, tf_end)
- if len(overlap_rows)>0:
- r = overlap_rows.iloc[0]
- seq_start=start+int(r['start'])
- seq_end = start+int(r['end'])
- seqlet_starts.append(seq_start)
- seqlet_ends.append(seq_end)
- p_values.append(r['p-value'])
- attributions.append(r['attribution'])
- attribution = (np.mean(X_attr[0,int(r['start']):int(r['end'])]))
- padding=(30-(int(r['end'])-int(r['start'])))//2
- attribution_padded = (np.mean(X_attr[0,int(r['start'])-padding:int(r['end'])+padding]))
- attributions_exact.append(attribution)
- attributions_exact_padded.append(attribution_padded)
- else:
- seqlet_starts.append(np.nan)
- seqlet_ends.append(np.nan)
- p_values.append(np.nan)
- attributions.append(np.nan)
- attributions_exact.append(np.nan)
- attributions_exact_padded.append(np.nan)
- df_final = tf_dict[tf]['unibind_hits_df'].head(top_n).copy()
- df_final.loc[:,'seqlet_start'] = seqlet_starts
- df_final.loc[:,'seqlet_end'] = seqlet_ends
- df_final.loc[:,'seqlet_p_val']= p_values
- df_final.loc[:,'seqlet_attribution'] = attributions
- df_final.loc[:,'chip_attribution'] = attributions_exact
- df_final.loc[:,'chip_attribution_padded'] = attributions_exact_padded
- tf_dict[tf]['unibind_recall_df']=df_final
- tf_dict['PAX5']['unibind_recall_df']
- # %%
- recalls ={}
- for tf in tf_dict:
- if tf_dict[tf]['unibind_recall_df'] is not None:
- print(tf)
- # Determine true positives (non-NaN in seqlet_start)
- true_positives = tf_dict[tf]['unibind_recall_df']['seqlet_start'].notna().sum()
- # Determine false negatives (NaN in seqlet_start)
- false_negatives = tf_dict[tf]['unibind_recall_df']['seqlet_start'].isna().sum()
- # Calculate recall
- recall = true_positives / (true_positives + false_negatives)
- recalls[tf] = {}
- recalls[tf]['recall']=recall
- recalls[tf]['total']=true_positives+false_negatives
- # Output the results
- print(f"True Positives (TP): {true_positives}")
- print(f"False Negatives (FN): {false_negatives}")
- print(f"Recall: {recall:.2f}")
- else:
- print('No unibind bed file provided for '+tf)
- # %% [markdown]
- # ### Fig. 3f
- # %%
- # Sorting by recall values
- sorted_factors = sorted(recalls, key=lambda x: recalls[x]['recall'], reverse=True)
- sorted_recalls = [recalls[factor]['recall'] for factor in sorted_factors]
- sorted_totals = [recalls[factor]['total'] for factor in sorted_factors]
- # Generate new x-axis labels with (n = total)
- x_labels = [f"{factor}\n(n = {total})" for factor, total in zip(sorted_factors, sorted_totals)]
- # Plotting
- fig, ax = plt.subplots(figsize=(8, 5))
- bars = ax.bar(x_labels, sorted_recalls, color='royalblue', edgecolor='black', linewidth=1.2)
- # Aesthetics
- ax.set_ylabel('Recall', fontsize=14)
- ax.set_xlabel('Transcription Factors', fontsize=14)
- ax.set_title('Recall Scores of Identified Seqlets in Unibind Sites', fontsize=16)
- ax.set_ylim(0, 1.05)
- ax.grid(axis='y', linestyle='--', alpha=0.7)
- # Adding value labels
- for bar in bars:
- yval = bar.get_height()
- ax.text(bar.get_x() + bar.get_width()/2, yval + 0.02, f'{yval:.3f}',
- ha='center', va='bottom', fontsize=12, color='black')
- # Remove top and right borders
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- # Show the plot
- plt.xticks(rotation=45, fontsize=12, ha='right', va='top')
- plt.yticks(fontsize=12)
- #plt.savefig('paperfigs/unibind_recall.pdf', bbox_inches='tight')
- plt.show()
- # %% [markdown]
- # ## Check ChIP and ATAC peak heights in identified seqlets
- # %%
- for tf in tf_dict:
- tf_dict[tf]['atac_bw_file']="/home/VIB.LOCAL/niklas.kempynck/nkemp/pbmc/bw/"+tf_dict[tf]['cell_type']+".bw"
- # %%
- import pandas as pd
- import matplotlib.pyplot as plt
- from crested.utils import read_bigwig_region # Ensure you have the required module
- def plot_average_peak_height(bigwig_file, region_df, padding=0, seqlet_len=30, cols=None,max_carrot=None, title='', save_path=None):
- """
- Plot a histogram of average peak heights and a heatmap carrot plot for all peak values.
- Parameters:
- - bigwig_file (str): Path to the BigWig file.
- - region_df (pd.DataFrame): DataFrame with columns ['region', 'chr', 'start', 'end'].
- - padding (int): Number of bases to add as flanking around the regions.
- - seqlet_len (int): Expected sequence length for normalization.
- - cols (list): Column names for chrom, start, end. Defaults to ['chr', 'start', 'end'].
- """
- # Setup columns
- if cols:
- chrom_col = cols[0]
- start_col = cols[1]
- end_col = cols[2]
- else:
- chrom_col = 'chr'
- start_col = 'start'
- end_col = 'end'
- # Initialize peak values
- window_length = seqlet_len + 2 * padding
- peaks = np.zeros((len(region_df), window_length))
- avg_peak_heights = []
- for i, row in region_df.iterrows():
- chrom = row[chrom_col]
- start = int(row[start_col])
- end = int(row[end_col])
- seq_len = end - start
- # Normalize to seqlet_len
- if seq_len > seqlet_len:
- diff = (seq_len - seqlet_len) // 2
- start = start + diff
- end = end - diff
- elif seq_len < seqlet_len:
- diff = (seqlet_len - seq_len) // 2
- start = start - diff
- end = end + diff
- seq_len = end-start
- if seq_len<seqlet_len:
- end=end+1
- # Read the BigWig region with flanking
- values, _ = read_bigwig_region(bigwig_file, (chrom, start - padding, end + padding))
- # Handle peak values
- #if len(values) >= window_length:
- peaks[i] = values[:window_length]
- avg_peak_heights.append(np.mean(values))
- #else:
- # peaks[i] = np.pad(values, (0, window_length - len(values)), 'constant', constant_values=0)
- # avg_peak_heights.append(np.mean(values) if len(values) > 0 else 0)
- avg_peaks = np.mean(peaks,axis=0)
- num_figs= 3 if max_carrot else 2
- # Plot histogram of average peak heights
- fig, axes = plt.subplots(1, num_figs , figsize=(num_figs*4, 6))
- x_start = -window_length/2
- x_end = window_length/2
- # Histogram
- # Plot
- axes[0].plot(np.arange(x_start,x_end),avg_peaks)
- axes[0].set_title("Average Peak Height in Regions "+title)
- axes[0].set_xlabel("Position")
- axes[0].set_ylabel("Average Peak Height")
- axes[0].grid(axis='y', linestyle='--', alpha=0.7)
- peak_max_values = np.max(peaks, axis=1) # Find max value for each row
- sorted_indices = np.argsort(peak_max_values)[::-1] # Sort indices in descending order
- sorted_peaks = peaks[sorted_indices] # Reorder rows based on sorted indices
- # Heatmap carrot plot
- im = axes[1].imshow(
- sorted_peaks,
- aspect='auto',
- cmap='coolwarm',
- extent=[-window_length // 2, window_length // 2, 0, len(region_df)]
- )
- axes[1].set_title("Carrot Plot of Peak Values "+(title))
- axes[1].set_xlabel("Position (relative to center)")
- axes[1].set_ylabel("Region Index")
- fig.colorbar(im, ax=axes[1], orientation='vertical', label='Peak Value')
- if max_carrot:
- peaks_carrot = np.clip(peaks, 0, max_carrot)
- peak_max_values = np.max(peaks_carrot, axis=1) # Find max value for each row
- sorted_indices = np.argsort(peak_max_values)[::-1] # Sort indices in descending order
- sorted_peaks = peaks_carrot[sorted_indices] # Reorder rows based on sorted indices
- # Heatmap carrot plot
- im = axes[2].imshow(
- sorted_peaks,
- aspect='auto',
- cmap='coolwarm',
- extent=[-window_length // 2, window_length // 2, 0, len(region_df)]
- )
- axes[2].set_title("Carrot Plot of Peak Values - Clipped "+(title))
- axes[2].set_xlabel("Position (relative to center)")
- axes[2].set_ylabel("Region Index")
- fig.colorbar(im, ax=axes[2], orientation='vertical', label='Peak Value')
- plt.tight_layout()
- if save_path:
- plt.savefig(save_path, bbox_inches='tight')
- plt.show()
- return peaks
- # %%
- tf = 'CEBPA'
- bw_file =tf_dict[tf]['chip_bw']
- seqlet_df = tf_dict[tf]['seqlet_df']
- results_df = tf_dict[tf]['results_df']
- possible_hits_df = tf_dict[tf]['chip_hits_df']
- possible_hits_df_unibind = tf_dict[tf]['unibind_hits_df']
- peaks=plot_average_peak_height(bw_file, seqlet_df, padding=50, max_carrot=5, title='(All seqlets)')#, save_path='paperfigs/'+tf+'_all_seqlets.pdf')
- results_df_tp = results_df.loc[results_df['status']=='True Positive'].reset_index()
- _=plot_average_peak_height(bw_file, results_df_tp, padding=50, cols=['seqlet_chr','seqlet_start','seqlet_end'],max_carrot=5,title='(True positives)')
- results_df_fp = results_df.loc[results_df['status']=='False Positive'].reset_index()
- _=plot_average_peak_height(bw_file, results_df_fp, padding=50, cols=['seqlet_chr','seqlet_start','seqlet_end'],max_carrot=0.12, title='(False positives)')#, save_path='paperfigs/'+tf+'_FP.pdf')
- results_df_fn = results_df.loc[results_df['status']=='False Negative'].reset_index()
- _=plot_average_peak_height(bw_file, results_df_fn, padding=50, cols=['chip_chr','chip_start','chip_end'],max_carrot=5,title='(False negatives)')
- _=plot_average_peak_height(bw_file, possible_hits_df, padding=50, cols=['chip_chr','chip_start','chip_end'],max_carrot=5,title='(Chip sites)')
- _=plot_average_peak_height(bw_file, possible_hits_df_unibind, padding=50, cols=['chip_chr','chip_start','chip_end'],max_carrot=5,title='(Unibind sites)')
- # %%
- results_df
- # %%
- import numpy as np
- import matplotlib.pyplot as plt
- def plot_combined_average_peak_heights(bigwig_file, subset_dict, padding=0, seqlet_len=30, cols_dict=None, title='', save_path=None):
- """
- Plot a combined average peak height plot for multiple subsets.
- Parameters:
- - bigwig_file (str): Path to the BigWig file.
- - subset_dict (dict): Dictionary where keys are subset names and values are DataFrames with ['chr', 'start', 'end'].
- - padding (int): Number of bases to add as flanking around the regions.
- - seqlet_len (int): Expected sequence length for normalization.
- - cols (list): Column names for chrom, start, end. Defaults to ['chr', 'start', 'end'].
- - title (str): Title of the plot.
- """
- window_length = seqlet_len + 2 * padding
- x_positions = np.arange(-window_length / 2, window_length / 2)
- plt.figure(figsize=(2, 4))
- max_peak = 0
- for subset_name, region_df in subset_dict.items():
- if region_df is None:
- continue
- if cols_dict:
- cols = cols_dict[subset_name]
- num_instances = len(region_df) # Get the number of instances
- label = f"{subset_name} (n={num_instances})" # Modify the label
- # Extract peak values
- peaks = np.zeros((len(region_df), window_length))
- for i, row in region_df.iterrows():
- chrom = row[cols[0]] if cols else row['chr']
- start = int(row[cols[1]]) if cols else int(row['start'])
- end = int(row[cols[2]]) if cols else int(row['end'])
- seq_len = end - start
- diff=seq_len
- # Normalize to seqlet_len
- if seq_len > seqlet_len:
- diff = (seq_len - seqlet_len) // 2
- start += diff
- end -= diff
- elif seq_len < seqlet_len:
- diff = (seqlet_len - seq_len) // 2
- start -= diff
- end += diff
- if diff<seq_len:
- end=end+1
- # Read BigWig region
- values, _ = read_bigwig_region(bigwig_file, (chrom, start - padding, end + padding))
- peaks[i] = values[:window_length]
- avg_peaks = np.mean(peaks, axis=0)
- if np.max(avg_peaks)>max_peak:
- max_peak = np.max(avg_peaks)
- # Plot
- plt.plot(x_positions, avg_peaks, label=label)
- plt.xlabel("Position (relative to center)")
- plt.ylabel("Average Peak Height")
- plt.title(title)
- plt.ylim([0,max_peak+0.05*max_peak])
- plt.legend(loc='center left', bbox_to_anchor=(1, 0.7))
- if save_path:
- plt.savefig(save_path, bbox_inches='tight')
- plt.show()
- # %% [markdown]
- # ### Fig. 3g
- # %%
- for tf in tf_list:
- print(tf)
- bw_file =tf_dict[tf]['chip_bw']
- seqlet_df = tf_dict[tf]['seqlet_df']
- results_df = tf_dict[tf]['results_df']
- possible_hits_df = tf_dict[tf]['chip_hits_df']
- possible_hits_df_unibind = tf_dict[tf]['unibind_hits_df']
- subset_dict = {
- "All Seqlets": seqlet_df,
- "True Positives": results_df.loc[results_df['status'] == 'True Positive'].reset_index(),
- "False Positives": results_df.loc[results_df['status'] == 'False Positive'].reset_index(),
- "False Negatives": results_df.loc[results_df['status'] == 'False Negative'].reset_index(),
- "Chip Sites": possible_hits_df,
- "Unibind": possible_hits_df_unibind
- }
- cols_dict = {
- "All Seqlets": ['chr', 'start', 'end'],
- "True Positives": ['seqlet_chr','seqlet_start','seqlet_end'],
- "False Positives": ['seqlet_chr','seqlet_start','seqlet_end'],
- "False Negatives": ['chip_chr','chip_start','chip_end'],
- "Chip Sites": ['chip_chr','chip_start','chip_end'],
- "Unibind": ['chip_chr','chip_start','chip_end']
- }
- plot_combined_average_peak_heights(bw_file, subset_dict, padding=200, cols_dict=cols_dict)# title="Average Peak for "+tf, save_path='paperfigs/'+tf+'_peaks.pdf')
- # %% [markdown]
- # # Recall of seqlets on UniBind hits inside all consensus peaks
- # %%
- peak_bed=f"{DATA_DIR}consensus_regions.bed"
- peak_df = pd.read_csv(peak_bed, sep="\t", header=None, names=column_names)
- peak_df
- # %% [markdown]
- # ## Calculate overlapping ChIP peaks and consensus regions
- # %%
- from tqdm import tqdm
- ct_list= ['Bcell',
- 'Bcell',
- 'CD4_Tcell',
- 'CD4_Tcell',
- 'CD4_Tcell',
- 'CD14_monocyte',
- 'CD14_monocyte'
- ]
- chip_bed_files = [
- f"{DATA_DIR}/chip/pax5/pax5.bed",
- f"{DATA_DIR}/chip/ebf1/ebf1_unibind.bed",
- f"{DATA_DIR}/chip/runx1/runx1.bed",
- f"{DATA_DIR}/chip/gata3_2/gata3.bed",
- f"{DATA_DIR}/chip/ets1/ets1_unibind.bed",
- f"{DATA_DIR}/chip/cebpa/cebpa.bed",
- f"{DATA_DIR}/chip/spi1_2/spi1.bed",
- ]
- unibind_dfs = []
- for chip_bed_file in tqdm(chip_bed_files):
- print(chip_bed_file)
- column_names = ["chrom", "start", "end", "name", "score", "strand"]
- # Read the unibind bed files
- chip_df = pd.read_csv(chip_bed_file, sep="\t", header=None, names=column_names)
- # Merge the two DataFrames on overlapping intervals
- merged_df = pd.merge(
- chip_df,
- peak_df,
- on="chrom",
- suffixes=("_chip", "_peak")
- )
- # Filter for overlaps
- subset_chip_df = merged_df[
- (merged_df["start_chip"] >= merged_df["start_peak"]) &
- (merged_df["end_chip"] <= merged_df["end_peak"])
- ]
- sorted_chip_df = subset_chip_df.sort_values(by="score_chip", ascending=False)
- unibind_dfs.append(sorted_chip_df)
- # %% [markdown]
- # ## Calculate contribution scores for all overlapping regions
- # WARNING: TAKES A LONG TIME
- #
- # just load the results
- # %%
- #out_dirs = [
- # f'{DATA_DIR}chip/pax5/',
- # f"{DATA_DIR}chip/ebf1/",
- # f"{DATA_DIR}chip/runx1/",
- # f"{DATA_DIR}chip/gata3_2/",
- # f"{DATA_DIR}chip/ets1/",
- # f"{DATA_DIR}chip/cebpa/",
- # f"{DATA_DIR}chip/spi1_2/",
- #]
- #for a, sorted_chip_df in enumerate(unibind_dfs):
- # top_n=len(sorted_chip_df)
- #
- # sequences=[]
- # for i in range(top_n):
- # row= sorted_chip_df.iloc[i]
- #
- # chrom= row['chrom']
- # start=int(row['start_peak']-807)
- # end=int(row['end_peak']+807)
- # sequence = genome.fetch(chrom, start, end).upper()
- # sequences.append(sequence)
- # scores, one_hot_encoded_sequences = evaluators[2].calculate_contribution_scores_sequence(sequences, ct_list[a], method='expected_integrated_grad', disable_tqdm=False)
- # scores = np.squeeze(scores, axis=1)
- # scores = np.transpose(scores,(0,2,1))
- # print(scores.shape)
- # one_hot_encoded_sequences = np.transpose(one_hot_encoded_sequences,(0,2,1))
- # np.savez(out_dirs[a]+"/"+ct_list[a]+"_contrib.npz",scores)
- # np.savez(out_dirs[a]+"/"+ct_list[a]+"_oh.npz", one_hot_encoded_sequences)
- # %%
- out_dirs = [
- f'{DATA_DIR}/chip/pax5/',
- f"{DATA_DIR}/chip/ebf1/",
- f"{DATA_DIR}/chip/runx1/",
- f"{DATA_DIR}/chip/gata3_2/",
- f"{DATA_DIR}/chip/ets1/",
- f"{DATA_DIR}/chip/cebpa/",
- f"{DATA_DIR}/chip/spi1_2/",
- ]
- ct_list= ['Bcell',
- 'Bcell',
- 'CD4_Tcell',
- 'CD4_Tcell',
- 'CD4_Tcell',
- 'CD14_monocyte',
- 'CD14_monocyte'
- ]
- recalls={}
- tfs=['PAX5','EBF1','RUNX1','GATA3','ETS1','CEBPA','SPI']
- a=0
- for ct, outdir, sorted_chip_df, tf in zip(ct_list, out_dirs, unibind_dfs, tfs):
- scores = np.load(outdir+ct+"_contrib.npz")['arr_0']
- scores = np.transpose(scores, (0,2,1))
- one_hot_encoded_sequences = np.load(outdir+ct+"_oh.npz")['arr_0']
- one_hot_encoded_sequences = np.transpose(one_hot_encoded_sequences, (0,2,1))
- print(scores.shape)
- print(sorted_chip_df.shape)
- from tqdm import tqdm
- seqlet_starts=[]
- seqlet_ends = []
- p_values=[]
- attributions=[]
- attributions_exact=[]
- overlaps=[]
- for i in tqdm(range(len(sorted_chip_df))):
- oh_seq = one_hot_encoded_sequences[i].T
- X_attr = scores[i].T
- X_attr=X_attr*oh_seq
- X_attr = np.expand_dims(np.sum(X_attr, axis=0),axis=0)
- seqlets = recursive_seqlets(X_attr, threshold=0.05)
- row=sorted_chip_df.iloc[i]
- chrom= row['chrom']
- start=int(row['start_peak']-807)
- end=int(row['end_peak']+807)
- tf_start=int(row['start_chip']-start)
- tf_end = int(row['end_chip']-start)
- attribution = (np.mean(X_attr[0,tf_start:tf_end]))
- overlap_rows = get_row_with_overlap(seqlets, tf_start, tf_end)
- if len(overlap_rows)>0:
- r = overlap_rows.iloc[0]
- seqlet_starts.append(start+int(r['start']))
- seqlet_ends.append(start+int(r['end']))
- p_values.append(r['p-value'])
- attributions.append(r['attribution'])
- overlaps.append(r['overlap'])
- attributions_exact.append(attribution)
- else:
- seqlet_starts.append(np.nan)
- seqlet_ends.append(np.nan)
- p_values.append(np.nan)
- attributions.append(np.nan)
- overlaps.append(np.nan)
- attributions_exact.append(attribution)
- df_final = sorted_chip_df.copy()
- df_final.loc[:,'seqlet_start'] = seqlet_starts
- df_final.loc[:,'seqlet_end'] = seqlet_ends
- df_final.loc[:,'seqlet_p_val']= p_values
- df_final.loc[:,'seqlet_attribution'] = attributions
- df_final.loc[:,'chip_attribution'] = attributions_exact
- df_final.loc[:,'UniBind-Seqlet overlap fraction'] = overlaps
- true_positives = df_final['seqlet_start'].notna().sum()
- # Determine false negatives (NaN in seqlet_start)
- false_negatives = df_final['seqlet_start'].isna().sum()
- # Calculate recall
- recall = true_positives / (true_positives + false_negatives)
- # Output the results
- print(f"True Positives (TP): {true_positives}")
- print(f"False Negatives (FN): {false_negatives}")
- print(f"Recall: {recall:.2f}")
- recalls[tf]={}
- recalls[tf]['recall']=recall
- recalls[tf]['total']=true_positives+false_negatives
- unibind_dfs[a]=df_final
- a+=1
- # %%
- # Sorting by recall values
- sorted_factors = sorted(recalls, key=lambda x: recalls[x]['recall'], reverse=True)
- sorted_recalls = [recalls[factor]['recall'] for factor in sorted_factors]
- sorted_totals = [recalls[factor]['total'] for factor in sorted_factors]
- # Generate new x-axis labels with (n = total)
- x_labels = [f"{factor}\n(n = {total})" for factor, total in zip(sorted_factors, sorted_totals)]
- # Plotting
- fig, ax = plt.subplots(figsize=(8, 5))
- bars = ax.bar(x_labels, sorted_recalls, color='royalblue', edgecolor='black', linewidth=1.2)
- # Aesthetics
- ax.set_ylabel('Recall', fontsize=14)
- ax.set_xlabel('Transcription Factors', fontsize=14)
- ax.set_title('Recall Scores of Identified Seqlets in Unibind Sites', fontsize=16)
- ax.set_ylim(0, 1.05)
- ax.grid(axis='y', linestyle='--', alpha=0.7)
- # Adding value labels
- for bar in bars:
- yval = bar.get_height()
- ax.text(bar.get_x() + bar.get_width()/2, yval + 0.02, f'{yval:.3f}',
- ha='center', va='bottom', fontsize=12, color='black')
- # Remove top and right borders
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- # Show the plot
- plt.xticks(rotation=45, fontsize=12, ha='right', va='top')
- plt.yticks(fontsize=12)
- #plt.savefig('paperfigs/unibind_recall_ALL.pdf', bbox_inches='tight')
- plt.show()
- # %%
chip_seq_analysis.ipynb at commit 916dee8, under MIT · at the source
Overview
- Laboratory of Computational Biology, VIB Center for AI and Computational Biology (VIB.AI), Leuven, Belgium
- VIB-KU Leuven Center for Brain and Disease Research, Leuven, Belgium
- Department of Human Genetics, KU Leuven, Leuven, Belgium
- Oncode Institute, Hubrecht Institute-KNAW (Royal Netherlands Academy of Arts and Sciences) and University Medical Center Utrecht, Utrecht, the Netherlands
- Department of Neurosciences, KU Leuven, Leuven, Belgium
- Present Address: Illumina Artificial Intelligence Laboratory, Illumina, Foster City, CA USA
- Aligning Science Across Parkinson’s (ASAP) Collaborative Research Network, Chevy Chase, MD USA
Abstract
Sequence-based deep learning models have become the state of the art for analyzing the genomic regulatory code. Particularly for enhancers, these models excel at deciphering sequence grammar that underlies their activity. To enable end-to-end enhancer modeling and design, we developed a software package called CREsted (cis-regulatory element sequence training, explanation and design). It combines preprocessing and analysis of single-cell assay for transposase-accessible chromatin using sequencing data, modeling chromatin accessibility from sequence, sequence design and downstream analysis to decipher enhancer grammar. We demonstrate CREsted’s functionality on a mouse cortex and a human peripheral blood mononuclear cell dataset. Additionally, we use CREsted to compare mesenchymal-like cancer cell states between tumor types, and we investigate different fine-tuning strategies of genomic foundation models within CREsted. Finally, we train a model on a zebrafish development atlas and use this to design and in vivo validate cell-type-specific enhancers. For varying datasets, we demonstrate that CREsted facilitates efficient training and analyses, enabling scrutinization of the enhancer logic and design of synthetic enhancers across tissues and species.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 40 matches between paragraphs and lines of code.
aertslab/CREsted
3940ea94c8a56344c5478119c5e13755e4eb698e, 26 September 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
134 files
- docs/
conf.py , Python, 168 lines - docs/
extensions/ , Python, 32 linestyped_returns.py - docs/
tutorials/ , Jupyter, 457 linesborzoi_atac_finetuning.i pynb - docs/
tutorials/ , Jupyter, 519 linescustom_models.ipynb - docs/
tutorials/ , Jupyter, 554 linesenhancer_code_analysis.i pynb - docs/
tutorials/ , Jupyter, 232 linesmodel_database_example.i pynb - docs/
tutorials/ , Jupyter, 902 linesmodel_training_and_eval. ipynb - docs/
tutorials/ , Jupyter, 42 linesmulti_gpu.ipynb - docs/
tutorials/ , Jupyter, 120 linestopic_classification.ipy nb - scripts/
model_porting/ , Python, 312 linesborzoi_to_crested.py - scripts/
model_porting/ , Python, 255 linesborzoiprime_to_crested.p y - scripts/
model_porting/ , Python, 294 linesenf_to_crested.py - src/
crested/ , Python, 57 lines__init__.py - src/
crested/ , Python, 30 lines_backend.py - src/
crested/ , Python, 3 lines_conf.py - src/
crested/ , Python, 336 lines, 4 matches_datasets.py - src/
crested/ , Python, 269 lines_genome.py - src/
crested/ , Python, 747 lines, 1 match_io.py - src/
crested/ , Python, 57 linespl/ __init__.py - src/
crested/ , Python, 1 linepl/ _old/ __init__.py - src/
crested/ , Python, 3 linespl/ _old/ bar/ __init__.py - src/
crested/ , Python, 136 linespl/ _old/ bar/ _bar.py - src/
crested/ , Python, 3 linespl/ _old/ heatmap/ __init__.py - src/
crested/ , Python, 34 linespl/ _old/ heatmap/ _heatmap.py - src/
crested/ , Python, 3 linespl/ _old/ hist/ __init__.py - src/
crested/ , Python, 32 linespl/ _old/ hist/ _hist.py - src/
crested/ , Python, 74 linespl/ _old/ patterns/ __init__.py - src/
crested/ , Python, 18 linespl/ _old/ patterns/ _contribution_scores.py - src/
crested/ , Python, 32 linespl/ _old/ patterns/ _design.py - src/
crested/ , Python, 119 linespl/ _old/ patterns/ _modisco.py - src/
crested/ , Python, 3 linespl/ _old/ scatter/ __init__.py - src/
crested/ , Python, 18 linespl/ _old/ scatter/ _scatter.py - src/
crested/ , Python, 3 linespl/ _old/ violin/ __init__.py - src/
crested/ , Python, 18 linespl/ _old/ violin/ _violin.py - src/
crested/ , Python, 364 linespl/ _utils.py - src/
crested/ , Python, 6 linespl/ corr/ __init__.py - src/
crested/ , Python, 330 lines, 1 matchpl/ corr/ _heatmap.py - src/
crested/ , Python, 276 linespl/ corr/ _scatter.py - src/
crested/ , Python, 172 lines, 1 matchpl/ corr/ _violin.py - src/
crested/ , Python, 3 linespl/ design/ __init__.py - src/
crested/ , Python, 459 linespl/ design/ _enhancer_design.py - src/
crested/ , Python, 3 linespl/ dist/ __init__.py - src/
crested/ , Python, 172 linespl/ dist/ _histogram.py - src/
crested/ , Python, 3 linespl/ explain/ __init__.py - src/
crested/ , Python, 300 linespl/ explain/ _contribution_scores.py - src/
crested/ , Python, 347 linespl/ explain/ _utils.py - src/
crested/ , Python, 4 linespl/ locus/ __init__.py - src/
crested/ , Python, 242 linespl/ locus/ _locus_scoring.py - src/
crested/ , Python, 218 linespl/ locus/ _track.py - src/
crested/ , Python, 13 linespl/ modisco/ __init__.py - src/
crested/ , Python, 1,513 linespl/ modisco/ _modisco.py - src/
crested/ , Python, 4 linespl/ qc/ __init__.py - src/
crested/ , Python, 306 linespl/ qc/ _filter.py - src/
crested/ , Python, 77 linespl/ qc/ _normalization_weights.p y - src/
crested/ , Python, 5 linespl/ region/ __init__.py - src/
crested/ , Python, 199 linespl/ region/ _bar.py - src/
crested/ , Python, 189 linespl/ region/ _scatter.py - src/
crested/ , Python, 9 linespp/ __init__.py - src/
crested/ , Python, 194 linespp/ _filter.py - src/
crested/ , Python, 143 lines, 1 matchpp/ _normalization.py - src/
crested/ , Python, 126 linespp/ _regions.py - src/
crested/ , Python, 311 linespp/ _split.py - src/
crested/ , Python, 88 linespp/ _utils.py - src/
crested/ , Python, 61 linestl/ __init__.py - src/
crested/ , Python, 266 linestl/ _configs.py - src/
crested/ , Python, 639 linestl/ _crested.py - src/
crested/ , Python, 526 lines, 1 matchtl/ _explainer.py - src/
crested/ , Python, 80 linestl/ _explainer_tf.py - src/
crested/ , Python, 80 linestl/ _explainer_torch.py - src/
crested/ , Python, 34 linestl/ _old.py - src/
crested/ , Python, 731 lines, 2 matchestl/ _tools.py - src/
crested/ , Python, 5 linestl/ data/ __init__.py - src/
crested/ , Python, 262 linestl/ data/ _anndatawrapper.py - src/
crested/ , Python, 1 linetl/ data/ _old/ __init__.py - src/
crested/ , Python, 246 linestl/ data/ _old/ _anndatamodule.py - src/
crested/ , Python, 112 linestl/ data/ _old/ _dataloader.py - src/
crested/ , Python, 259 linestl/ data/ _old/ _dataset.py - src/
crested/ , Python, 4 linestl/ data/ utils/ __init__.py - src/
crested/ , Python, 825 lines, 1 matchtl/ data/ utils/ _datawrapper.py - src/
crested/ , Python, 156 linestl/ data/ utils/ _sequenceloader.py - src/
crested/ , Python, 4 linestl/ design/ __init__.py - src/
crested/ , Python, 496 linestl/ design/ _design.py - src/
crested/ , Python, 199 linestl/ design/ _utils.py - src/
crested/ , Python, 11 linestl/ losses/ __init__.py - src/
crested/ , Python, 88 lines, 1 matchtl/ losses/ _cosinemse.py - src/
crested/ , Python, 111 lines, 1 matchtl/ losses/ _cosinemse_log.py - src/
crested/ , Python, 65 linestl/ losses/ _gini.py - src/
crested/ , Python, 74 linestl/ losses/ _poisson.py - src/
crested/ , Python, 236 lines, 1 matchtl/ losses/ _poissonmultinomial.py - src/
crested/ , Python, 11 linestl/ metrics/ __init__.py - src/
crested/ , Python, 62 linestl/ metrics/ _concordancecorr.py - src/
crested/ , Python, 59 linestl/ metrics/ _pearsoncorr.py - src/
crested/ , Python, 65 linestl/ metrics/ _pearsoncorrlog.py - src/
crested/ , Python, 91 linestl/ metrics/ _spearmancorr.py - src/
crested/ , Python, 52 linestl/ metrics/ _zeropenalty.py - src/
crested/ , Python, 24 linestl/ modisco/ __init__.py - src/
crested/ , Python, 491 linestl/ modisco/ _modisco_utils.py - src/
crested/ , Python, 2,095 lines, 2 matchestl/ modisco/ _tfmodisco.py - src/
crested/ , Python, 15 linestl/ zoo/ __init__.py - src/
crested/ , Python, 117 linestl/ zoo/ _basenji.py - src/
crested/ , Python, 451 linestl/ zoo/ _borzoi.py - src/
crested/ , Python, 144 linestl/ zoo/ _deeptopic_cnn.py - src/
crested/ , Python, 122 linestl/ zoo/ _deeptopic_lstm.py - src/
crested/ , Python, 140 lines, 1 matchtl/ zoo/ _dilated_cnn.py - src/
crested/ , Python, 151 lines, 1 matchtl/ zoo/ _dilated_cnn_decoupled.p y - src/
crested/ , Python, 261 linestl/ zoo/ _enformer.py - src/
crested/ , Python, 136 linestl/ zoo/ _legnet.py - src/
crested/ , Python, 134 linestl/ zoo/ _simple_convnet.py - src/
crested/ , Python, 23 linestl/ zoo/ utils/ __init__.py - src/
crested/ , Python, 640 linestl/ zoo/ utils/ _attention.py - src/
crested/ , Python, 781 linestl/ zoo/ utils/ _layers.py - src/
crested/ , Python, 14 linesutils/ __init__.py - src/
crested/ , Python, 49 linesutils/ _logging.py - src/
crested/ , Python, 96 linesutils/ _model_utils.py - src/
crested/ , Python, 46 linesutils/ _old.py - src/
crested/ , Python, 355 linesutils/ _seq_utils.py - src/
crested/ , Python, 279 linesutils/ _utils.py - tests/
__init__.py , Python, 19 lines - tests/
_utils.py , Python, 206 lines - tests/
conftest.py , Python, 228 lines - tests/
test_dataloader.py , Python, 265 lines - tests/
test_datasets.py , Python, 57 lines - tests/
test_genome.py , Python, 137 lines - tests/
test_io.py , Python, 203 lines - tests/
test_pipeline.py , Python, 103 lines - tests/
test_pl.py , Python, 815 lines - tests/
test_pl_rename.py , Python, 247 lines - tests/
test_pp.py , Python, 289 lines - tests/
test_tl.py , Python, 618 lines - tests/
test_tl_rename.py , Python, 10 lines - tests/
test_utils.py , Python, 110 lines - tests/
test_zoo.py , Python, 45 lines - LICENSE, License, 59 lines
- README.md, Text, 113 lines
aertslab/create_cisTarget_databases
304d5dc1b15e5c923908a50a1ec291c3faaccf9c, 18 June 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
29 files
- bigwigaverageoverbed.py, Python, 245 lines
- cistarget_db.py, Python, 1,677 lines
- clusterbuster.py, Python, 332 lines
- combine_partial_motifs_o
r_tracks_vs_regions_or_g , Python, 278 linesenes_scores_cistarget_db s.py - combine_partial_regions_
or_genes_vs_motifs_or_tr , Python, 255 linesacks_scores_cistarget_db s.py - convert_cistarget_databa
ses_v1_to_v2.py , Python, 179 lines - convert_feather_db_to_sq
lite3_db.py , Python, 215 lines - convert_motifs_or_tracks
_vs_regions_or_genes_sco , Python, 175 linesres_to_rankings_cistarge t_dbs.py - create_cistarget_motif_d
atabases.py , Python, 572 lines, 1 match - create_cistarget_track_d
atabases.py , Python, 430 lines - create_cross_species_mot
ifs_rankings_db.py , Python, 214 lines - create_fasta_with_padded
_bg_from_bed.sh , Shell, 133 lines, 1 match - feather_v1_fbs/
CTable.py , Python, 117 lines - feather_v1_fbs/
CategoryMetadata.py , Python, 62 lines - feather_v1_fbs/
Column.py , Python, 97 lines - feather_v1_fbs/
DateMetadata.py , Python, 34 lines - feather_v1_fbs/
Encoding.py , Python, 14 lines - feather_v1_fbs/
PrimitiveArray.py , Python, 105 lines - feather_v1_fbs/
TimeMetadata.py , Python, 45 lines - feather_v1_fbs/
TimeUnit.py , Python, 10 lines - feather_v1_fbs/
TimestampMetadata.py , Python, 58 lines - feather_v1_fbs/
Type.py , Python, 31 lines - feather_v1_fbs/
TypeMetadata.py , Python, 11 lines - feather_v1_fbs/
__init__.py , Python, 1 line - feather_v1_or_v2.py, Python, 87 lines
- orderstatistics.py, Python, 120 lines
- test_cistarget_db.py, Python, 1,143 lines
- test_orderstatistics.py, Python, 224 lines
- README.md, Text, 689 lines
Zenodo 17791463
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
aertslab/CREsted-paper
916dee8dc9bcd9908049ce4aa48a0dbfefe5b15a, 3 December 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
16 files
- Figure_2/
DeepBICCN2_analysis.ipyn , Jupyter, 683 lines, 2 matchesb - Figure_2/
scoring_utils.py , Python, 428 lines - Figure_2/
validated_enhancers_eval , Jupyter, 603 linesuation.ipynb - Figure_3/
DeepPBMC_analysis.ipynb , Jupyter, 805 lines, 2 matches - Figure_3/
chip_seq_analysis.ipynb , Jupyter, 1,382 lines, 4 matches - Figure_4/
chrombpnet_porting.ipynb , Jupyter, 103 lines, 1 match - Figure_4/
cistopic_utils.py , Python, 381 lines - Figure_4/
cistopic_visualization.i , Jupyter, 99 linespynb - Figure_4/
contribution_comparison. , Jupyter, 254 linesipynb - Figure_4/
enhancer_code_deepccl.ip , Jupyter, 272 lines, 1 matchynb - Figure_4/
evaluate_deepccl_chrombp , Jupyter, 698 lines, 1 matchnet.ipynb - Figure_4/
motif_comparison.ipynb , Jupyter, 163 lines - Figure_4/
topic_scoring.ipynb , Jupyter, 496 lines, 4 matches - Figure_4/
train_deepccl.ipynb , Jupyter, 317 lines - Figure_4/
train_evaluate_deepgliom , Jupyter, 310 linesa.ipynb - Figure_5/
compare_models.ipynb , Jupyter, 1,067 lines, 3 matches - repository limit reached (2,000 files or 30 MB): the rest is at the source (37 files)
Zenodo 17791384
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
Zenodo 17202107
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
38 files
- docs/
conf.py , Python, 140 lines - docs/
extensions/ , Python, 32 linestyped_returns.py - docs/
notebooks/ , Jupyter, 166 lines01_preprocessing_tutoria l.ipynb - docs/
notebooks/ , Jupyter, 223 lines02_analysis_tutorial.ipy nb - docs/
notebooks/ , Jupyter, 80 lines03_region_topic_analysis .ipynb - paper/
plot_figure_1.py , Python, 452 lines - paper/
plot_figure_2.py , Python, 777 lines - paper/
plot_figure_3.py , Python, 1,359 lines - paper/
scripts/ , Python, 137 linesio.py - paper/
scripts/ , Python, 185 linespattern.py - src/
tfmindi/ , Python, 46 lines__init__.py - src/
tfmindi/ , Python, 151 linesbackends/ __init__.py - src/
tfmindi/ , Python, 416 linesdatasets.py - src/
tfmindi/ , Python, 256 linesio.py - src/
tfmindi/ , Python, 33 linespl/ __init__.py - src/
tfmindi/ , Python, 409 linespl/ _utils.py - src/
tfmindi/ , Python, 271 linespl/ contributions.py - src/
tfmindi/ , Python, 137 linespl/ dbd_heatmap.py - src/
tfmindi/ , Python, 314 linespl/ logo.py - src/
tfmindi/ , Python, 244 linespl/ region_topics.py - src/
tfmindi/ , Python, 305 linespl/ tsne.py - src/
tfmindi/ , Python, 5 linespp/ __init__.py - src/
tfmindi/ , Python, 116 linespp/ mappings.py - src/
tfmindi/ , Python, 720 linespp/ seqlets.py - src/
tfmindi/ , Python, 15 linestl/ __init__.py - src/
tfmindi/ , Python, 195 linestl/ cluster.py - src/
tfmindi/ , Python, 384 linestl/ patterns.py - src/
tfmindi/ , Python, 256 linestl/ topic_modeling.py - src/
tfmindi/ , Python, 329 linestypes.py - tests/
conftest.py , Python, 136 lines - tests/
test_datasets.py , Python, 110 lines - tests/
test_io.py , Python, 206 lines - tests/
test_pl.py , Python, 193 lines - tests/
test_pp.py , Python, 675 lines - tests/
test_tl.py , Python, 209 lines - tests/
test_topic_modeling.py , Python, 192 lines - LICENSE, License, 21 lines
- README.md, Text, 125 lines
aertslab/tf-mindi
ca4467f7e020a08fcbbeedffa6f12dc870d78c40, 22 July 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
41 files
- docs/
conf.py , Python, 141 lines - docs/
extensions/ , Python, 32 linestyped_returns.py - docs/
notebooks/ , Jupyter, 203 lines01_preprocessing_tutoria l.ipynb - docs/
notebooks/ , Jupyter, 229 lines02_analysis_tutorial.ipy nb - docs/
notebooks/ , Jupyter, 80 lines03_region_topic_analysis .ipynb - paper/
plot_figure_1.py , Python, 452 lines - paper/
plot_figure_2.py , Python, 777 lines - paper/
plot_figure_3.py , Python, 1,359 lines - paper/
scripts/ , Python, 137 linesio.py - paper/
scripts/ , Python, 185 linespattern.py - src/
tfmindi/ , Python, 50 lines__init__.py - src/
tfmindi/ , Python, 151 linesbackends/ __init__.py - src/
tfmindi/ , Python, 416 linesdatasets.py - src/
tfmindi/ , Python, 354 linesio.py - src/
tfmindi/ , Python, 123 linesmerge.py - src/
tfmindi/ , Python, 33 linespl/ __init__.py - src/
tfmindi/ , Python, 411 linespl/ _utils.py - src/
tfmindi/ , Python, 271 linespl/ contributions.py - src/
tfmindi/ , Python, 137 linespl/ dbd_heatmap.py - src/
tfmindi/ , Python, 314 linespl/ logo.py - src/
tfmindi/ , Python, 244 linespl/ region_topics.py - src/
tfmindi/ , Python, 315 linespl/ tsne.py - src/
tfmindi/ , Python, 5 linespp/ __init__.py - src/
tfmindi/ , Python, 116 linespp/ mappings.py - src/
tfmindi/ , Python, 1,276 lines, 1 matchpp/ seqlets.py - src/
tfmindi/ , Python, 15 linestl/ __init__.py - src/
tfmindi/ , Python, 247 linestl/ cluster.py - src/
tfmindi/ , Python, 601 linestl/ patterns.py - src/
tfmindi/ , Python, 256 linestl/ topic_modeling.py - src/
tfmindi/ , Python, 342 linestypes.py - tests/
conftest.py , Python, 136 lines - tests/
test_datasets.py , Python, 110 lines - tests/
test_io.py , Python, 240 lines - tests/
test_merge.py , Python, 128 lines - tests/
test_pl.py , Python, 214 lines - tests/
test_pp.py , Python, 720 lines - tests/
test_tl.py , Python, 209 lines - tests/
test_topic_modeling.py , Python, 192 lines - tests/
test_types.py , Python, 109 lines - LICENSE, License, 59 lines
- README.md, Text, 137 lines
Code availability
The CREsted package is available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 7 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 251 scripts, each with its path and the digest of its content;
- 40 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
Datasets cited
- geo:GSE229169, at NCBI GEO; found in “Data availability”
- sra:SRX15782182, at NCBI SRA; found in the text, “Authentication of newly generated cell line data”
Data availability
Analysis data required for reproducing the findings in this paper is available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Publisher: n/a → Nature Portfolio
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 15 authors, 4 keywords, 11 MeSH terms, 7 funders, 93 references, 42 RRIDs.
Cite
This paper
Kempynck, N., De Winter, S., Blaauw, C. H., Konstantakos, V., Ekşi, E. C., Dieltiens, S., Abaffyová, D., Bercier, V., Taskiran, I. I., Hulselmans, G., Spanier, K., Christiaens, V., Van Den Bosch, L., Mahieu, L., & Aerts, S. (2026). CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species. Nature methods, 23(5), 946-959. https://
BibTeX
@article{kempynck2026cre
author = {Kempynck, Niklas and De Winter, Seppe and Blaauw, Casper H and Konstantakos, Vasileios and Ekşi, Eren Can and Dieltiens, Sam and Abaffyová, Darina and Bercier, Valérie and Taskiran, Ibrahim I and Hulselmans, Gert and Spanier, Katina and Christiaens, Valerie and Van Den Bosch, Ludo and Mahieu, Lukas and Aerts, Stein},
title = {{CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species}},
journal = {Nature methods},
year = {2026},
month = apr,
volume = {23},
number = {5},
pages = {946--959},
publisher = {Nature Portfolio},
issn = {1548-7091},
doi = {10.1038/
url = {https://
pmid = {41927920},
pmcid = {PMC13167471}
}
RIS
TY - JOUR
AU - Kempynck, Niklas
AU - De Winter, Seppe
AU - Blaauw, Casper H
AU - Konstantakos, Vasileios
AU - Ekşi, Eren Can
AU - Dieltiens, Sam
AU - Abaffyová, Darina
AU - Bercier, Valérie
AU - Taskiran, Ibrahim I
AU - Hulselmans, Gert
AU - Spanier, Katina
AU - Christiaens, Valerie
AU - Van Den Bosch, Ludo
AU - Mahieu, Lukas
AU - Aerts, Stein
TI - CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species
T2 - Nature methods
J2 - Nat Methods
PY - 2026
DA - 2026/
VL - 23
IS - 5
SP - 946
EP - 959
SN - 1548-7091
PB - Nature Portfolio
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species",
"container-title": "Nature methods",
"author": [
{
"family": "Kempynck",
"given": "Niklas"
},
{
"family": "De Winter",
"given": "Seppe"
},
{
"family": "Blaauw",
"given": "Casper H"
},
{
"family": "Konstantakos",
"given": "Vasileios"
},
{
"family": "Ekşi",
"given": "Eren Can"
},
{
"family": "Dieltiens",
"given": "Sam"
},
{
"family": "Abaffyová",
"given": "Darina"
},
{
"family": "Bercier",
"given": "Valérie"
},
{
"family": "Taskiran",
"given": "Ibrahim I"
},
{
"family": "Hulselmans",
"given": "Gert"
},
{
"family": "Spanier",
"given": "Katina"
},
{
"family": "Christiaens",
"given": "Valerie"
},
{
"family": "Van Den Bosch",
"given": "Ludo"
},
{
"family": "Mahieu",
"given": "Lukas"
},
{
"family": "Aerts",
"given": "Stein"
}
],
"container-title-short":
"volume": "23",
"issue": "5",
"page": "946-959",
"DOI": "10.1038/
"PMID": "41927920",
"PMCID": "PMC13167471",
"ISSN": "1548-7091",
"publisher": "Nature Portfolio",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
2
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s42003-026-10462-y [code]
- SpaDC enables sequence-based integrative analysis and regulatory inference of spatial chromatin accessibility data.Journal: Communications biologyIn common: pysam, Biopython, BEDTools, 8 other tools, genetics / omics, mouse, 12 references
- [2] doi:10.1038/s41467-026-75700-7 [code]
- Gene regulatory innovations from transposable elements in primate cerebellum development.Journal: Nature communicationsIn common: pysam, Biopython, BEDTools, 9 other tools, genetics / omics, mouse, 8 references
- [3] doi:10.1186/s13059-026-04177-w [code]
- Genomic sequence evolution underlying human neocortical interareal diversification.Journal: Genome biologyIn common: pysam, BEDTools, UMAP, 10 other tools, genetics / omics, mouse, 6 references
- [4] doi:10.1016/j.xcrm.2026.102766 [code]
- A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.Journal: Cell reports. MedicineIn common: pysam, Keras, UMAP, 12 other tools, genetics / omics, 3 references
- [5] doi:10.1093/bioinformatics/btag652 [code]
- mmVelo: a deep generative model for estimating cell state-dependent dynamics across multiple modalities.Journal: Bioinformatics (Oxford, England)In common: pysam, BEDTools, UMAP, 11 other tools, genetics / omics, mouse, 3 references
- [6] doi:10.1016/j.celrep.2026.117110 [code]
- Single-nucleus multiome analysis in the human prefrontal cortex identifies gene expression and cis-regulatory elements associated with aging.Journal: Cell reportsIn common: pysam, BEDTools, anndata, 8 other tools, genetics / omics, 6 references
- [7] doi:10.64898/2026.03.30.714220 [code]
- An integrated single cell and spatial omics atlas of human prenatal developmentJournal: bioRxiv (preprint)In common: CuPy, Biopython, UMAP, 12 other tools, 2 references
- [8] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: CuPy, Biopython, Keras, 12 other tools, zebrafish, mouse
- [9] doi:10.1016/j.celrep.2026.117073 [code]
- Single-cell epigenomics uncovers heterochromatin instability and transcription factor dysfunction during mouse brain aging.Journal: Cell reportsIn common: pysam, BEDTools, anndata, 6 other tools, genetics / omics, mouse, 7 references
- [10] doi:10.1038/s44320-026-00208-7 [code]
- Interpretable deep generative ensemble learning for single-cell omics with Hydra.Journal: Molecular systems biologyIn common: pysam, Keras, UMAP, 12 other tools, 1 reference
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 7 repositories of the authors' code, each at its verified commit and with its license, 251 scripts, and 40 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:4fb57ad48e9b1dce…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
