Skip to content

Commit 1a517fe

Browse files
Add charging visualization functions and shorten_trna_names() utility
Add shorten_trna_names() to extract reusable tRNA label shortening. Improve plot_charging_diffs() with source_col faceting, label shortening, and configurable label_col. Improve plot_abundance_charging() with label shortening, source_col faceting, and lfcSE error bars. Add new plot_charging_ratios() for box+jitter plots of raw charging ratios. Replace deprecated geom_errorbarh() with geom_errorbar(orientation="y"). Use vars(.data[[col]]) for programmatic faceting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9452d1a commit 1a517fe

File tree

11 files changed

+456
-11
lines changed

11 files changed

+456
-11
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export(plot_abundance_charging)
3434
export(plot_arc_diagram)
3535
export(plot_bcerror_profile)
3636
export(plot_charging_diffs)
37+
export(plot_charging_ratios)
3738
export(plot_chord_or)
3839
export(plot_chord_ror)
3940
export(plot_identity_panel)
@@ -59,6 +60,7 @@ export(read_pipeline_config)
5960
export(read_pipeline_results)
6061
export(read_sprinzl_coords)
6162
export(run_deseq)
63+
export(shorten_trna_names)
6264
export(structure_html)
6365
export(structure_organisms)
6466
export(structure_to_png)

NEWS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# clover 0.0.0.9000
22

3+
* `plot_abundance_charging()` gains `shorten`, `source_col`, and `error_bars` parameters. Labels are auto-shortened via `shorten_trna_names()`, an optional faceting column separates host and phage tRNAs, and horizontal error bars show `lfcSE` on significant points when present.
4+
5+
* `plot_charging_diffs()` gains `source_col`, `label_col`, and `shorten` parameters. Labels are auto-shortened via `shorten_trna_names()` and an optional faceting column separates host and phage tRNAs.
6+
7+
* `plot_charging_ratios()` creates a box-and-jitter plot of raw charging ratios grouped by condition, with optional faceting and auto-shortened tRNA labels.
8+
9+
* `shorten_trna_names()` extracts the tRNA label-shortening logic into a reusable utility function, stripping source prefixes, the `tRNA-` prefix, and gene-copy suffixes.
10+
311
* `compute_bcerror_delta()` computes per-position differences in base-calling error rates between two conditions from a summarized bcerror tibble.
412

513
* `prep_mod_heatmap()` prepares bcerror delta data for `plot_mod_heatmap()` by joining Sprinzl coordinates, annotating known modifications, and shortening tRNA labels.

R/plots.R

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,7 @@ prep_mod_heatmap <- function(
185185
if (shorten_labels) {
186186
result <- dplyr::mutate(
187187
result,
188-
trna_label = sub("^tRNA-", "", .data[[ref_col]]) |>
189-
sub("-\\d+-\\d+$", "", x = _)
188+
trna_label = shorten_trna_names(.data[[ref_col]], strip_prefix = "^$")
190189
)
191190
}
192191

@@ -524,7 +523,8 @@ plot_volcano <- function(
524523
#' [ggrepel::geom_text_repel()].
525524
#'
526525
#' @param deseq_res A tibble from [tidy_deseq_results()] with at least
527-
#' `ref`, `log2FoldChange`, and `padj` columns.
526+
#' `ref`, `log2FoldChange`, and `padj` columns. When `error_bars` is
527+
#' `TRUE`, an `lfcSE` column is also expected.
528528
#' @param charging_diffs A tibble from [compute_charging_diffs()] with
529529
#' at least `ref` and `diff` columns.
530530
#' @param lab_col Column name (string) used for point labels. Default
@@ -537,6 +537,14 @@ plot_volcano <- function(
537537
#' `2`.
538538
#' @param label_size Numeric size for [ggrepel::geom_text_repel()].
539539
#' Default `3`.
540+
#' @param shorten Logical; if `TRUE` (default), shorten tRNA names in
541+
#' point labels via [shorten_trna_names()].
542+
#' @param source_col Optional column name (string) for faceting, e.g.,
543+
#' `"source"` to separate host and phage tRNAs. When provided, the
544+
#' column must exist in `deseq_res`. Default `NULL`.
545+
#' @param error_bars Logical; if `TRUE` (default), draw horizontal
546+
#' error bars (`lfcSE`) on significant points. Requires an `lfcSE`
547+
#' column in `deseq_res`.
540548
#'
541549
#' @return A ggplot object.
542550
#'
@@ -561,12 +569,24 @@ plot_abundance_charging <- function(
561569
padj_cutoff = 0.05,
562570
max_overlaps = 20,
563571
point_size = 2,
564-
label_size = 3
572+
label_size = 3,
573+
shorten = TRUE,
574+
source_col = NULL,
575+
error_bars = TRUE
565576
) {
566577
rlang::check_installed("ggrepel", reason = "to label significant points.")
567578

568579
data <- dplyr::inner_join(deseq_res, charging_diffs, by = "ref")
569580

581+
if (shorten) {
582+
data <- dplyr::mutate(
583+
data,
584+
.plot_label = shorten_trna_names(.data[[lab_col]])
585+
)
586+
} else {
587+
data <- dplyr::mutate(data, .plot_label = .data[[lab_col]])
588+
}
589+
570590
data <- dplyr::mutate(
571591
data,
572592
significant = !is.na(.data$padj) & .data$padj < padj_cutoff,
@@ -600,15 +620,34 @@ plot_abundance_charging <- function(
600620

601621
p <- ggplot(data, aes(x = log2FoldChange, y = diff)) +
602622
geom_hline(yintercept = 0, linetype = "dashed", color = "grey40") +
603-
geom_vline(xintercept = 0, linetype = "dashed", color = "grey40") +
623+
geom_vline(xintercept = 0, linetype = "dashed", color = "grey40")
624+
625+
# Error bars on significant points
626+
has_lfcse <- "lfcSE" %in% names(data)
627+
if (error_bars && has_lfcse) {
628+
p <- p +
629+
geom_errorbar(
630+
data = function(x) dplyr::filter(x, .data$significant),
631+
aes(
632+
xmin = log2FoldChange - .data$lfcSE,
633+
xmax = log2FoldChange + .data$lfcSE
634+
),
635+
orientation = "y",
636+
linewidth = 0.3,
637+
alpha = 0.4,
638+
color = "grey40"
639+
)
640+
}
641+
642+
p <- p +
604643
geom_point(
605644
aes(color = quadrant),
606645
size = point_size,
607646
alpha = 0.7
608647
) +
609648
ggrepel::geom_text_repel(
610649
data = function(x) dplyr::filter(x, .data$significant),
611-
aes(label = .data[[lab_col]]),
650+
aes(label = .data$.plot_label),
612651
size = label_size,
613652
max.overlaps = max_overlaps
614653
) +
@@ -625,6 +664,11 @@ plot_abundance_charging <- function(
625664
theme(legend.position = "bottom") +
626665
theme_markdown_axes()
627666

667+
if (!is.null(source_col)) {
668+
p <- p +
669+
facet_wrap(vars(.data[[source_col]]), scales = "free")
670+
}
671+
628672
p
629673
}
630674

@@ -638,6 +682,12 @@ plot_abundance_charging <- function(
638682
#' `ref` (factor), `diff`, and `se_diff` columns.
639683
#' @param point_size Numeric size for [ggplot2::geom_point()]. Default
640684
#' `2.5`.
685+
#' @param source_col Optional column name (string) for faceting, e.g.,
686+
#' `"source"` to separate host and phage tRNAs. Default `NULL`.
687+
#' @param label_col Column name (string) to use for y-axis labels.
688+
#' Default `"ref"`.
689+
#' @param shorten Logical; if `TRUE` (default), shorten tRNA names on
690+
#' the y-axis via [shorten_trna_names()].
641691
#'
642692
#' @return A ggplot object.
643693
#'
@@ -650,8 +700,29 @@ plot_abundance_charging <- function(
650700
#' se_diff = rep(0.03, 5)
651701
#' )
652702
#' plot_charging_diffs(df)
653-
plot_charging_diffs <- function(data, point_size = 2.5) {
654-
ggplot(data, aes(x = diff, y = ref)) +
703+
plot_charging_diffs <- function(
704+
data,
705+
point_size = 2.5,
706+
source_col = NULL,
707+
label_col = "ref",
708+
shorten = TRUE
709+
) {
710+
if (shorten) {
711+
data <- dplyr::mutate(
712+
data,
713+
.plot_label = shorten_trna_names(.data[[label_col]])
714+
)
715+
} else {
716+
data <- dplyr::mutate(
717+
data,
718+
.plot_label = .data[[label_col]]
719+
)
720+
}
721+
722+
p <- ggplot(
723+
data,
724+
aes(x = diff, y = stats::reorder(.data$.plot_label, diff))
725+
) +
655726
geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") +
656727
geom_point(size = point_size) +
657728
geom_linerange(aes(xmin = diff - se_diff, xmax = diff + se_diff)) +
@@ -660,6 +731,91 @@ plot_charging_diffs <- function(data, point_size = 2.5) {
660731
y = ""
661732
) +
662733
cowplot::theme_minimal_vgrid()
734+
735+
if (!is.null(source_col)) {
736+
p <- p +
737+
facet_wrap(vars(.data[[source_col]]), scales = "free_y")
738+
}
739+
740+
p
741+
}
742+
743+
#' Plot per-tRNA charging ratios.
744+
#'
745+
#' Create a box-and-jitter plot of raw charging ratios grouped by
746+
#' condition. Expects the tibble stored in
747+
#' `metadata(se)$charging_ratios`.
748+
#'
749+
#' @param data A tibble with at least `ref`, `charging_ratio`, and the
750+
#' column named by `group_col`.
751+
#' @param group_col Column name (string) for the x-axis grouping
752+
#' variable (e.g., `"condition"`). Default `"condition"`.
753+
#' @param facet_col Optional column name (string) for faceting (e.g.,
754+
#' `"strain"`). Default `NULL`.
755+
#' @param shorten Logical; if `TRUE` (default), shorten tRNA names on
756+
#' the y-axis via [shorten_trna_names()].
757+
#' @param point_size Numeric size for [ggplot2::geom_jitter()]. Default
758+
#' `0.8`.
759+
#' @param point_alpha Numeric alpha for jittered points. Default `0.4`.
760+
#'
761+
#' @return A ggplot object.
762+
#'
763+
#' @export
764+
#'
765+
#' @examples
766+
#' df <- tibble::tibble(
767+
#' ref = rep(paste0("tRNA-Ala-AGC-", 1:3, "-1"), each = 6),
768+
#' condition = rep(c("ctl", "inf"), each = 3, times = 3),
769+
#' charging_ratio = runif(18, 0.3, 0.9)
770+
#' )
771+
#' plot_charging_ratios(df)
772+
plot_charging_ratios <- function(
773+
data,
774+
group_col = "condition",
775+
facet_col = NULL,
776+
shorten = TRUE,
777+
point_size = 0.8,
778+
point_alpha = 0.4
779+
) {
780+
if (shorten) {
781+
data <- dplyr::mutate(
782+
data,
783+
.plot_label = shorten_trna_names(.data$ref)
784+
)
785+
} else {
786+
data <- dplyr::mutate(data, .plot_label = .data$ref)
787+
}
788+
789+
p <- ggplot(
790+
data,
791+
aes(x = .data[[group_col]], y = charging_ratio)
792+
) +
793+
geom_boxplot(outlier.shape = NA) +
794+
geom_jitter(
795+
width = 0.2,
796+
size = point_size,
797+
alpha = point_alpha
798+
) +
799+
facet_wrap(~.plot_label, scales = "free_y") +
800+
labs(
801+
x = NULL,
802+
y = "Charging ratio"
803+
) +
804+
cowplot::theme_cowplot() +
805+
theme(
806+
strip.text = element_text(size = rel(0.7)),
807+
axis.text.x = element_text(angle = 45, hjust = 1)
808+
)
809+
810+
if (!is.null(facet_col)) {
811+
p <- p +
812+
facet_wrap(
813+
vars(.data$.plot_label, .data[[facet_col]]),
814+
scales = "free_y"
815+
)
816+
}
817+
818+
p
663819
}
664820

665821
#' Plot per-position base-calling error profiles.

R/utils.R

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,27 @@ dna_to_rna_anticodon <- function(x) {
3232
paste0(parts[, 2], chartr("T", "U", parts[, 3]), parts[, 4])
3333
)
3434
}
35+
36+
#' Shorten tRNA name strings for display.
37+
#'
38+
#' Strips source prefixes (e.g., `host-`, `phage-`), the `tRNA-` prefix,
39+
#' and the gene-copy suffix (`-\d+-\d+`) to produce compact labels
40+
#' suitable for plot axes.
41+
#'
42+
#' @param x Character vector of tRNA name strings.
43+
#' @param strip_prefix Regex pattern for source prefixes to remove.
44+
#' Default `"^(host|phage)-"`.
45+
#'
46+
#' @return Character vector of shortened names (e.g.,
47+
#' `"host-tRNA-Ser-CGT-1-1"` becomes `"Ser-CGT"`).
48+
#'
49+
#' @export
50+
#' @examples
51+
#' shorten_trna_names("host-tRNA-Ser-CGT-1-1")
52+
#' shorten_trna_names(c("phage-tRNA-Glu-TTC-2-1", "tRNA-Ala-AGC-3-1"))
53+
shorten_trna_names <- function(x, strip_prefix = "^(host|phage)-") {
54+
x |>
55+
sub(strip_prefix, "", x = _) |>
56+
sub("^tRNA-", "", x = _) |>
57+
sub("-\\d+-\\d+$", "", x = _)
58+
}

man/plot_abundance_charging.Rd

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/plot_charging_diffs.Rd

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)