diff --git a/ProtPeptigram/logger.py b/ProtPeptigram/logger.py index c560b42..eeadadc 100644 --- a/ProtPeptigram/logger.py +++ b/ProtPeptigram/logger.py @@ -55,7 +55,7 @@ def configure_logging(level: str = "info", log_to_file: bool = False) -> logging logger.setLevel(log_level) return logger - +#add console logging def save_console_log(output_dir,file_name: str = "prot-peptigram.log"): """ Saves console logs to a specified file. diff --git a/ProtPeptigram/runner.py b/ProtPeptigram/runner.py index 89a3270..990a5cb 100644 --- a/ProtPeptigram/runner.py +++ b/ProtPeptigram/runner.py @@ -133,7 +133,7 @@ def run_pipeline( specific_proteins = None if protein_list and os.path.exists(protein_list): specific_proteins = read_protein_list(protein_list) - console.print(f"Using {len(specific_proteins)} proteins from provided list.", style="bold") + console.log(f"Using {len(specific_proteins)} proteins from provided list.", style="bold") # 1. Initialize the data processor processor = PeptideDataProcessor() @@ -180,6 +180,7 @@ def run_pipeline( try: fig, _ = viz.plot_peptigram( [prot], + groups = unique_samples, group_by='Sample', color_by='protein', figsize=(14, 12), diff --git a/ProtPeptigram/viz.py b/ProtPeptigram/viz.py index 06a12e2..4ec6442 100644 --- a/ProtPeptigram/viz.py +++ b/ProtPeptigram/viz.py @@ -191,14 +191,17 @@ def plot_peptigram( peptide_groups.columns = ['Peptide', 'Protein', 'Start', 'End', 'Mean_Intensity', 'Std_Intensity', 'Count', 'Groups'] - # Determine groups to plot if groups is None: all_groups = [] for g in peptide_groups['Groups']: all_groups.extend(g) groups = sorted(list(set(all_groups))) - + + # groups = sorted(list({g for group_list in peptide_groups['Groups'] for g in group_list})) + # print(f"Groups to plot: {groups}") + # print(f"Proteins to plot: {peptide_groups}") + # Configure styling for publication-quality plt.style.use('default') plt.rcParams['font.family'] = 'sans-serif' @@ -480,119 +483,121 @@ def plot_peptigram( lambda x: group in x)] if group_peptides.empty: - ax.set_visible(False) - continue - - # group_peptides['Length'] = group_peptides['End'] - group_peptides['Start'] - group_peptides.loc[:, 'Length'] = group_peptides['End'] - \ - group_peptides['Start'] - - # Calculate maximum height needed - max_height = self._calculate_plot_height(group_peptides, xlim) - - # Initialize space tracking array - spaces = np.zeros((max_height, int(xlim[1] - xlim[0] + 1))) - - # Sort peptides by start position and length - group_peptides = group_peptides.sort_values( - ['Start', 'End'], ascending=[True, False]) - - # Plot each peptide - for idx, peptide in group_peptides.iterrows(): - start = int(peptide['Start']) - end = int(peptide['End']) - protein_id = peptide['Protein'] - - # Get base color for this protein - base_color = protein_to_color[protein_id] - final_color = base_color - - # Apply coloring based on selection - if color_by_protein_and_intensity: - # Get intensity value and normalize using per-protein normalization - intensity_val = peptide['Mean_Intensity'] - - # Get normalization for this protein - if protein_id in protein_intensity_norms: - norm = protein_intensity_norms[protein_id] - intensity_normalized = norm(intensity_val) - else: - # Fallback to global normalization - intensity_normalized = plt.Normalize( - min_intensity_val, max_intensity_val)(intensity_val) - - # Get the appropriate colormap for this protein - intensity_cmap = intensity_cmap_dict[protein_id] - - # Get color from the protein's intensity colormap - if intensity_normalized < 0: - intensity_normalized = 0 - elif intensity_normalized > 1: - intensity_normalized = 1 - - final_color = intensity_cmap(intensity_normalized) - - elif color_by == 'intensity': - intensity_val = peptide['Mean_Intensity'] - - # Use per-protein normalization and colormap - if protein_id in protein_intensity_norms: - norm = protein_intensity_norms[protein_id] - intensity_normalized = norm(intensity_val) - else: - # Fallback to global normalization - intensity_normalized = plt.Normalize( - min_intensity_val, max_intensity_val)(intensity_val) - - # Use the protein's assigned colormap - intensity_cmap = intensity_cmap_dict[protein_id] - final_color = intensity_cmap(intensity_normalized) - - elif color_by == 'count': - count_val = peptide['Count'] - count_normalized = plt.Normalize( - 1, group_peptides['Count'].max())(count_val) - final_color = plt.cm.Blues(count_normalized) - - elif color_by == 'length': - length_val = peptide['End'] - peptide['Start'] - length_normalized = plt.Normalize( - group_peptides['Length'].min(), group_peptides['Length'].max())(length_val) - final_color = plt.cm.Greens(length_normalized) - - # Find available space for this peptide - for height in range(max_height): - if start < xlim[0]: - start = xlim[0] - if end > xlim[1]: - end = xlim[1] - - space_start = max(0, start - xlim[0]) - space_end = min(end - xlim[0], xlim[1] - xlim[0]) - - if space_start >= spaces.shape[1] or space_end >= spaces.shape[1]: - continue - - space_needed = spaces[height, space_start:space_end+1] - if np.sum(space_needed) == 0: # Space is available - spaces[height, space_start:space_end+1] = 1 - #peptide visualization - ax.plot( - [start, end], - [-height-0.4, -height-0.4], - linewidth=2.5, - solid_capstyle='round', - color=final_color, - alpha=0.95, - path_effects=[ - path_effects.withStroke( - linewidth=3.0, - foreground=(0, 0, 0, 0.2), - alpha=0.3 - ) - ] - ) - break + #making empty plot if no peptides in group + ax.set_xlim(xlim) + ax.set_ylim(-1, 0) # Keep consistent with non-empty plots (negative y-axis) + max_height = 1 + else: + # group_peptides['Length'] = group_peptides['End'] - group_peptides['Start'] + group_peptides.loc[:, 'Length'] = group_peptides['End'] - \ + group_peptides['Start'] + + # Calculate maximum height needed + max_height = self._calculate_plot_height(group_peptides, xlim) + + # Initialize space tracking array + spaces = np.zeros((max_height, int(xlim[1] - xlim[0] + 1))) + + # Sort peptides by start position and length + group_peptides = group_peptides.sort_values( + ['Start', 'End'], ascending=[True, False]) + + # Plot each peptide + for idx, peptide in group_peptides.iterrows(): + start = int(peptide['Start']) + end = int(peptide['End']) + protein_id = peptide['Protein'] + + # Get base color for this protein + base_color = protein_to_color[protein_id] + final_color = base_color + + # Apply coloring based on selection + if color_by_protein_and_intensity: + # Get intensity value and normalize using per-protein normalization + intensity_val = peptide['Mean_Intensity'] + + # Get normalization for this protein + if protein_id in protein_intensity_norms: + norm = protein_intensity_norms[protein_id] + intensity_normalized = norm(intensity_val) + else: + # Fallback to global normalization + intensity_normalized = plt.Normalize( + min_intensity_val, max_intensity_val)(intensity_val) + + # Get the appropriate colormap for this protein + intensity_cmap = intensity_cmap_dict[protein_id] + + # Get color from the protein's intensity colormap + if intensity_normalized < 0: + intensity_normalized = 0 + elif intensity_normalized > 1: + intensity_normalized = 1 + + final_color = intensity_cmap(intensity_normalized) + + elif color_by == 'intensity': + intensity_val = peptide['Mean_Intensity'] + + # Use per-protein normalization and colormap + if protein_id in protein_intensity_norms: + norm = protein_intensity_norms[protein_id] + intensity_normalized = norm(intensity_val) + else: + # Fallback to global normalization + intensity_normalized = plt.Normalize( + min_intensity_val, max_intensity_val)(intensity_val) + + # Use the protein's assigned colormap + intensity_cmap = intensity_cmap_dict[protein_id] + final_color = intensity_cmap(intensity_normalized) + + elif color_by == 'count': + count_val = peptide['Count'] + count_normalized = plt.Normalize( + 1, group_peptides['Count'].max())(count_val) + final_color = plt.cm.Blues(count_normalized) + + elif color_by == 'length': + length_val = peptide['End'] - peptide['Start'] + length_normalized = plt.Normalize( + group_peptides['Length'].min(), group_peptides['Length'].max())(length_val) + final_color = plt.cm.Greens(length_normalized) + + # Find available space for this peptide + for height in range(max_height): + if start < xlim[0]: + start = xlim[0] + if end > xlim[1]: + end = xlim[1] + + space_start = max(0, start - xlim[0]) + space_end = min(end - xlim[0], xlim[1] - xlim[0]) + + if space_start >= spaces.shape[1] or space_end >= spaces.shape[1]: + continue + + space_needed = spaces[height, space_start:space_end+1] + if np.sum(space_needed) == 0: # Space is available + spaces[height, space_start:space_end+1] = 1 + #peptide visualization + ax.plot( + [start, end], + [-height-0.4, -height-0.4], + linewidth=2.5, + solid_capstyle='round', + color=final_color, + alpha=0.95, + path_effects=[ + path_effects.withStroke( + linewidth=3.0, + foreground=(0, 0, 0, 0.2), + alpha=0.3 + ) + ] + ) + break # Set plot limits and labels ax.set_ylim(-max_height, 0) diff --git a/README.md b/README.md index 4254490..5e1f585 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ProtPeptigram provides a comprehensive visualization platform for mapping immunopeptides to their source proteins across different biological samples. This tool can enables to identify peptide coverage patterns, analyze density distributions, and compare peptide presentations between experimental conditions.

- ProtPeptigram Visualization Example + ProtPeptigram Visualization Example

## Features diff --git a/example/prot-peptigram_P60710.png b/example/prot-peptigram_P60710.png new file mode 100644 index 0000000..484fede Binary files /dev/null and b/example/prot-peptigram_P60710.png differ