A Python package for visualizing nanopore sequencing signals at specific reference positions. This tool enables researchers to plot and compare signal patterns from POD5 files aligned to reference genomes via BAM files.
- Signal Visualization: Plot nanopore signals from POD5 files at specific reference positions
- Multi-condition Comparison: Overlay multiple conditions/samples for direct comparison
- Statistical Analysis: Calculate and visualize statistics across positions
- GMM Fitting: Fit Gaussian Mixture Models for advanced analysis
- CurrentView
git clone https://github.com/genometechlab/currentview.git
cd currentview
pip install . # or: pip install -e . for a development installOptional extra: pip install ".[speed]" adds numba, which JIT-compiles the
built-in statistics functions. CurrentView falls back to plain NumPy without it.
numpy>=1.20.0matplotlib>=3.5.0plotly>=5.14.0kaleido>=0.2.0nbformat>=5.10.4pysam>=0.22.0scikit-learn<2.0,>=1.4umap-learn>=0.5.11pod5>=0.3.23dash>=2.14.0dash-bootstrap-components>=1.5.0scipy>=1.10.0pandas>=1.5.0
CurrentView is implemented and tested with Python 3.12.8.
from currentview import CurrentView, PlotStyle
# Create visualizer for a 9-base window with statistics
cv = CurrentView(K=9, stats=['mean', 'std', 'median'])
# Add signals aligned to a reference position
cv.add_condition(
bam_path="sample1.bam",
pod5_path="sample1.pod5",
contig="chr1",
target_position=100,
label="IVT"
)
# Add another condition for comparison
cv.add_condition(
bam_path="sample2.bam",
pod5_path="sample2.pod5",
contig="chr1",
target_position=100,
label="Canonical",
color="red"
)
# Display the signal plot
cv.show_signals()
# Display the stats plot
cv.show_stats()
# Or display both
cv.show()Implementation examples are provided under example folder
The main class for visualization.
CurrentView(
K: int = 9,
kmer: Optional[List[Union[str, int]]] = None,
stats: Optional[List[Union[str, Callable]]] = None,
signal_processing_fn: Optional[callable] = None,
stats_distribution_kind: Literal["kde", "histogram", "both"] = "kde",
signals_plot_style: Optional[PlotStyle] = None,
stats_plot_style: Optional[PlotStyle] = None,
color_palette: Optional[Union[str, ColorPalette]] = None,
title: Optional[str] = None,
backend: Literal["plotly", "matplotlib"] = "plotly",
verbosity: VerbosityLevel = VerbosityLevel.SILENT,
logger: Optional[logging.Logger] = None
)Parameters:
K: Window size (will be made odd if even). Default: 9kmer: Optional custom k-mer labels for x-axis. Should be an iterable with sizeKstats: List of statistics to include. Supports'mean','median','std','variance','min','max','duration','skewness','kurtosis', and user-defined callablessignal_processing_fn: Optional callable for custom signal processingstats_distribution_kind: How stats distributions are drawn —'kde','histogram', or'both'signals_plot_style: PlotStyle object for signal visualization customizationstats_plot_style: PlotStyle object for stats visualization customizationcolor_palette: Color palette name (string) or ColorPalette instancetitle: Plot titlebackend:'plotly'for interactive figures,'matplotlib'for lightweight static outputverbosity: Logging level (0-4 or VerbosityLevel enum):- 0 = SILENT: No output
- 1 = ERROR: Only errors
- 2 = WARNING: Errors and warnings
- 3 = INFO: Errors, warnings, and info
- 4 = DEBUG: Everything including debug messages
logger: Optional custom logger instance
Add and process a new condition from BAM and POD5 files.
cv.add_condition(
bam_path: Union[str, Path],
pod5_path: Union[str, Path],
contig: str,
target_position: int,
*,
molecule_type: str = "RNA",
matched_query_base: Optional[str] = None,
read_ids: Optional[Union[Set[str], List[str]]] = None,
max_reads: Optional[int] = None,
exclude_reads_with_indels: bool = False,
label: Optional[str] = None,
color: Optional[str] = None,
alpha: Optional[float] = None,
line_width: Optional[float] = None,
line_style: Optional[str] = None
) -> CurrentViewParameters:
bam_path: Path to BAM alignment file (required)pod5_path: Path to POD5 signal file (required)contig: Chromosome/contig name, e.g., "chr1" (required)target_position: 0-based reference position (required). This matches the coordinatespysamand the BAM format use internally. If you are reading positions off a 1-based source such as IGV, a VCF, or a GFF, subtract 1.molecule_type: Type of molecule, "RNA" or "DNA" (default: "RNA")matched_query_base: Expected base at target position for validation (default: None)ignore_non_primaries: Skip secondary and supplementary alignments (default: True)read_ids: Specific read IDs to include (default: None - all aligned reads)max_reads: Maximum number of reads to process (default: None - no limit)exclude_reads_with_indels: Skip reads with insertions/deletions (default: False)label: Condition name (default:{contig}:{target_position})color: Line color (default: auto-assigned from palette)alpha: Line transparency 0-1 (default: auto-calculated based on read count)line_width: Line thickness (default: from style)line_style: Line style:"solid","dash","dot","dashdot"(default: from style)
Update visualization parameters of an existing condition.
cv.update_condition(
label: str,
*,
color: Optional[str] = None,
alpha: Optional[float] = None,
line_width: Optional[float] = None,
line_style: Optional[str] = None
) -> CurrentView# Display both signals and stats plots
cv.show()
# Display only the signals plot
cv.show_signals()
# Display only the stats plot
cv.show_stats()# Save both plots (adds _signals and _stats suffixes)
cv.save(path="output.png", format='png', scale=1)
# Save only signals plot
cv.save_signals(path="signals.png", format='png', scale=1)
# Save only stats plot
cv.save_stats(path="stats.png", format='png', scale=1)# Highlight a position in the window
cv.highlight_position(window_idx=4, color='red', alpha=0.2)
# Highlight the center position
cv.highlight_center(color='red', alpha=0.2)
# Remove all highlights
cv.clear_highlights()
# Add text annotation
cv.add_annotation(window_idx=4, text="SNP", y_position=150)
# Remove annotations
cv.clear_annotations()
# Set plot title
cv.set_title("Signal comparison at chr1:1000000")
# Set y-axis limits
cv.set_ylim(bottom=50, top=200)
# Get/print summary
summary = cv.get_summary()
cv.print_summary()
# Remove a condition
cv.remove_condition("Control")
# Clear all conditions
cv.clear()
# Get condition names
names = cv.get_condition_names()
# Get specific condition
cond = cv.get_condition("Control")
# Change verbosity
cv.set_verbosity(3) # Set to INFO level
# Update styles
cv.set_signals_style(new_style)
cv.set_stats_style(new_style)Both fit_gmms() and fit_umap() take an offsets_window tuple that selects
which bases their per-read statistics are computed over. Offsets are signed and
inclusive, relative to the target base:
offsets_window |
Meaning (K=9) |
|---|---|
| omitted | the full K-base window — the default for both methods |
(-4, 4) |
the whole 9-base window (same as the default at K=9) |
(0, 0) |
the target base only |
(-1, 1) |
the target base plus one on each side |
(-4, -1) |
the four bases immediately upstream of the target |
Offsets must satisfy start <= end and lie within [-(K-1)/2, (K-1)/2].
umap_handler = cv.fit_umap(
stats=['median', 'std'], # stats used as features at each position
offsets_window=(-4, 4), # optional; defaults to the full K-base window
n_neighbors=10,
min_dist=0.1,
)
# plot UMAP scatters
umap_viz = umap_handler.visualize(style=PlotStyle(...))
umap_viz.save("umap.png")The statistics requested here are computed on demand, so they do not have to
match the stats given to the CurrentView constructor.
Fit and visualize Gaussian Mixture Models:
from currentview import GMMConfig, PreprocessConfig, PlotStyle
# Fit GMMs and get the handler back
gmm_handler = cv.fit_gmms(
stat1='mean',
stat2='std',
offsets_window=(0, 0), # optional; defaults to the full K-base window
gmm_config=GMMConfig(n_components='auto'),
preprocess_config=PreprocessConfig(enable_standardize=True),
)
# Plot from the handler
gmm_viz = gmm_handler.visualize()
# ...or fit and plot in one call
gmm_viz = cv.plot_gmms(
stat1='mean',
stat2='std',
offsets_window=(-4, 4),
gmm_style=PlotStyle(),
gmm_config=GMMConfig(),
preprocess_config=PreprocessConfig(),
)
# Kolmogorov–Smirnov test (per-feature marginals, Bonferroni corrected)
ks_result = gmm_handler.ks_test(
label_p='Control', # label of the first condition
label_q='Treatment', # label of the second condition
correction='bonferroni',
mode='auto',
drop_nonfinite=True,
verbose=True,
)
# Jensen–Shannon divergence between the two fitted GMMs
js_result = gmm_handler.js_divergence(
label_p='Control',
label_q='Treatment',
n_samples=20000,
base=2,
random_state=None, # set an int for reproducible sampling
verbose=True,
)stat1 and stat2 must be two different statistics.
The appearance of plots can be customized using the PlotStyle class:
from currentview.utils.plotly_utils import PlotStyle
from currentview.utils.color_utils import ColorPalette
# Create custom style
style = PlotStyle(
width=1200, # pixels
height=800, # pixels
line_width=2.0,
line_style="solid", # "solid", "dash", "dot", "dashdot"
opacity_mode='auto', # 'auto' or 'fixed'
fixed_opacity=0.8,
fill_opacity=0.3,
# ... more options
)
cv = CurrentView(
K=9,
signals_plot_style=style,
stats_plot_style=style,
color_palette="colorblind" # or ColorPalette instance
)A complete guide to PlotStyle can be found in plotstyle_guide.md.
Implementation examples are provided under example folder
Example 1: Basic Single Condition
from currentview import CurrentView
cv = CurrentView(K=9, verbosity=3)
cv.add_condition(
bam_path="sample.bam",
pod5_path="sample.pod5",
contig="chr1",
target_position=100
)
cv.set_title("Nanopore Signals at chr1:1000000")
cv.show_signals()Example 2: Comparing Multiple Conditions
from currentview import CurrentView
from currentview.utils.plotly_utils import PlotStyle
style = PlotStyle(width=1400, height=800)
cv = CurrentView(K=9, signals_plot_style=style)
conditions = [
("control.bam", "control.pod5", "Control", "blue"),
("treated.bam", "treated.pod5", "Treatment", "red"),
("knockout.bam", "knockout.pod5", "Knockout", "green"),
]
for bam, pod5, label, color in conditions:
cv.add_condition(
bam_path=bam,
pod5_path=pod5,
contig="chr1",
target_position=100,
label=label,
color=color,
max_reads=50
)
cv.highlight_center(color='yellow', alpha=0.3)
cv.add_annotation(window_idx=4, text="Target")
cv.set_title("Signal Comparison at chr1:1000000")
cv.save("comparison.png")Example 3: With Statistics
cv = CurrentView(
K=9,
stats=['mean', 'median', 'std', 'skewness']
)
cv.add_condition(
bam_path="sample.bam",
pod5_path="sample.pod5",
contig="chr1",
target_position=100,
label="Sample"
)
# View signals
cv.show_signals()
# View statistics
cv.show_stats()
# Print summary
cv.print_summary()Example 4: Filtering Specific Reads
target_reads = ["read_001", "read_002", "read_003"]
cv = CurrentView(K=11)
cv.add_condition(
bam_path="sample.bam",
pod5_path="sample.pod5",
contig="chr2",
target_position=5000000,
read_ids=target_reads,
exclude_reads_with_indels=True,
label="Selected Reads"
)
cv.print_summary()
cv.show()Example 5: Method Chaining
(CurrentView(K=9, stats=['mean'])
.add_condition("sample.bam", "sample.pod5", "chr1", 12345, label="Sample")
.highlight_center(color='red')
.set_title("My Analysis")
.show())Processing BAM and POD5 files can be computationally expensive. For better performance:
-
Limit reads for large datasets:
cv.add_condition(..., max_reads=100)
-
Filter out reads with indels:
cv.add_condition(..., exclude_reads_with_indels=True)
-
Use appropriate verbosity:
cv = CurrentView(K=9, verbosity=0) # Silent for production cv.set_verbosity(4) # Debug for troubleshooting
-
Adjust alpha for overlapping signals:
style = PlotStyle(opacity_mode='auto') # Auto-adjusts based on read count cv.add_condition(..., alpha=0.5) # Or set manually
-
Use contrasting colors:
cv = CurrentView(color_palette="colorblind")
-
Limit window size for clarity: K=9 or K=11 work well for most cases
-
No reads found at position:
- Verify correct contig name (e.g., "chr1" vs "1")
- Check the position is correct and 0-based — subtract 1 from a coordinate read off IGV, a VCF, or a GFF
- Increase verbosity to see detailed logs; CurrentView reports how many reads it skipped and why (missing basecaller tags, base mismatch, indels in window)
-
Every read is skipped with a "missing basecaller tags" warning:
- CurrentView needs the move table to map signal to bases. The BAM must carry
the
mv,nsandtstags, e.g. from Dorado run with--emit-moves.
- CurrentView needs the move table to map signal to bases. The BAM must carry
the
-
Memory issues with large files:
- Use
max_readsparameter - Filter reads by specific read IDs
- Use
-
Overlapping signals hard to see:
- Adjust alpha transparency
- Reduce number of reads
- Use different colors
- Increase figure size
-
Label "already exists" error:
- Each condition needs a unique label
- Use
remove_condition()first, or specify a unique label
Currentview is also available as a web application. After completing the installation, activate your environment and run:
currentview-app
Once the application has initialized, it will automatically open the Currentview web interface in your default browser.
On the landing page, users can configure general visualization parameters, including the k-mer window size and the statistics to be displayed. This functionality is largely equivalent to the CurrentView component of the Python API.
On the subsequent page, the interface is organized into three panels:
-
Add Condition Panel: This panel allows users to define condition-specific parameters, including the path to the BAM file, the POD5 directory, the target genomic position, and the maximum number of reads. It also provides controls for condition-level visualization settings such as color opacity, line style, and line width. This panel is equivalent to the
add_conditionmethod of the Python API. -
Conditions Panel: This panel displays all previously added conditions in the order they were added. Visualization attributes for each condition can be modified directly within this panel. This panel is equivalent to the
update_conditionmethod of the Python API. -
Visualization Panel: This panel presents the signal plots and, if requested, the associated statistical summaries.
This project is licensed under the MIT License - see the LICENSE file for details.
If you use this tool in your research, please cite:
Pooria Daneshvar Kakhaki, Neda Ghohabi Esfahani, Stuart Akeson, Miten Jain, CurrentView: A tool for visualization and comparison of nanopore ionic current signals, Bioinformatics, 2026;, btag161, https://doi.org/10.1093/bioinformatics/btag161
https://academic.oup.com/bioinformatics/advance-article/doi/10.1093/bioinformatics/btag161/8651103



