@@ -413,3 +413,127 @@ def marker_profiles_split(
413413 if show :
414414 return None
415415 return axs
416+
417+
418+ def marker_profiles (
419+ adata : AnnData ,
420+ marker_column : str ,
421+ plot_nan : bool = False ,
422+ error_type : str = 'std' ,
423+ xticklabels : bool = False ,
424+ ylabel : str = 'Abundance' ,
425+ replicate_column : str | None = None ,
426+ show : bool = True ,
427+ save : bool | str | None = None ,
428+ ) -> plt .Axes | None :
429+ """Plot mean profiles with error bands for each marker annotation.
430+
431+ Creates a single plot showing the mean profile for each marker category
432+ with shaded error regions (standard deviation or standard error).
433+
434+ This function assumes that ``adata.var`` is sorted by Replicate (if present)
435+ and Fraction/Pulldown, so that related measurements are adjacent on the x-axis.
436+
437+ Parameters
438+ ----------
439+ adata
440+ AnnData object with proteins in `.var` and samples/fractions in `.obs`.
441+ marker_column
442+ Column name in ``adata.obs`` containing marker annotations.
443+ plot_nan
444+ If ``True``, NaN entries in the marker column are included;
445+ otherwise they are skipped.
446+ error_type
447+ Type of error to display: ``'std'`` for standard deviation or
448+ ``'sem'`` for standard error of the mean. Default is ``'std'``.
449+ xticklabels
450+ If ``True``, label x-ticks with ``adata.var_names``.
451+ ylabel
452+ Label for the y-axis. Default is ``'Abundance'``.
453+ replicate_column
454+ Column name in ``adata.var`` indicating replicate groups. If provided,
455+ vertical dashed lines are drawn at replicate boundaries (after the last
456+ instance of each replicate).
457+ show
458+ If ``True`` (default) the plot is shown and the function returns ``None``.
459+ save
460+ If ``True`` or a ``str``, save the figure. A string is appended to the default filename.
461+ Infer the filetype if ending on ``{'.pdf', '.png', '.svg'}``.
462+
463+ Returns
464+ -------
465+ Returns the Axes if ``show`` is ``False``, otherwise ``None``.
466+
467+ See Also
468+ --------
469+ marker_profiles_split : Plot individual profiles in separate subplots.
470+ """
471+ if error_type not in ['std' , 'sem' ]:
472+ raise ValueError ("error_type must be 'std' or 'sem'" )
473+
474+ # Prepare data
475+ marker_series , categories , palette , replicate_boundaries = _prepare_marker_profile_data (
476+ adata , marker_column , plot_nan , replicate_column
477+ )
478+
479+ # Create figure
480+ fig , ax = plt .subplots (figsize = (10 , 6 ))
481+
482+ n_proteins = adata .n_vars
483+
484+ # Plot each category
485+ for category in categories :
486+ # Get samples in this category
487+ if pd .isna (category ):
488+ mask = marker_series .isna ()
489+ category_name = "NaN"
490+ color = "gray"
491+ else :
492+ mask = marker_series == category
493+ category_name = str (category )
494+ color = palette .get (category , "gray" )
495+
496+ # Get data for this category (each row is a sample/fraction profile)
497+ category_data = adata [mask , :].X
498+
499+ if category_data .shape [0 ] == 0 :
500+ continue
501+
502+ # Calculate mean and error
503+ mean_profile = np .mean (category_data , axis = 0 )
504+ if error_type == 'std' :
505+ error = np .std (category_data , axis = 0 )
506+ else : # 'sem'
507+ error = np .std (category_data , axis = 0 ) / np .sqrt (category_data .shape [0 ])
508+
509+ # Plot mean line with shaded error region
510+ x = np .arange (n_proteins )
511+ ax .plot (x , mean_profile , color = color , linewidth = 2 , label = category_name )
512+ ax .fill_between (
513+ x ,
514+ mean_profile - error ,
515+ mean_profile + error ,
516+ color = color ,
517+ alpha = 0.2 ,
518+ )
519+
520+ # Add vertical dashed lines at replicate boundaries
521+ if replicate_boundaries :
522+ for boundary in replicate_boundaries :
523+ ax .axvline (boundary , color = "gray" , linestyle = "--" , linewidth = 1 , alpha = 0.7 )
524+
525+ ax .set_ylabel (ylabel )
526+ ax .set_xlim (- 0.5 , n_proteins - 0.5 )
527+ ax .legend ()
528+
529+ # Set x-tick labels if requested
530+ if xticklabels :
531+ ax .set_xticks (range (n_proteins ))
532+ ax .set_xticklabels (adata .var_names , rotation = 90 , ha = "center" )
533+
534+ plt .tight_layout ()
535+
536+ _utils .savefig_or_show ("marker_profiles" , show = show , save = save )
537+ if show :
538+ return None
539+ return ax
0 commit comments