Forge Publication-Ready Protein Plots with R ggplot2

Forge Publication-Ready Protein Plots with R ggplot2

In the relentless pursuit of biological discovery, transforming raw protein data into compelling visual narratives is not merely an artistic endeavor—it is a critical scientific imperative. High-quality visualizations serve as the bedrock for disseminating research findings, anchoring hypotheses, and fueling subsequent investigations. This specialized guide empowers you to master the art and science of generating publication-ready plots for protein data using the unparalleled flexibility of R's ggplot2 package.

We decode complex datasets, translating intricate protein dynamics into clear, impactful figures that resonate with peer reviewers and the broader scientific community. As we forge ahead in the era of data-driven biology, the ability to effectively analyze biological datasets with R for statistics, visualization, and inference becomes paramount. This article activates your potential to engineer truly impactful visualizations, ensuring your research stands out. Prepare to elevate your data presentation from functional to phenomenal, transforming your protein plots into powerful statements of scientific rigor and innovation.

Forge Your Visual Blueprint: Setting Up R and Data for Protein Plotting

Forge Your Visual Blueprint: Setting Up R and Data for Protein Plotting

We initiate our journey into publication-ready protein visualizations by establishing a robust foundation in R. ggplot2, built on the elegant "Grammar of Graphics," empowers us to construct intricate plots layer by layer, offering unparalleled control and flexibility. Before we activate any visual element, we must ensure our data is meticulously prepared. The cardinal rule for ggplot2 is tidy data: each variable forms a column, each observation forms a row, and each type of observational unit forms a table. Protein datasets, often originating from mass spectrometry, ELISA, or western blots, frequently present in a 'wide' format where protein names or conditions occupy individual columns. This structure requires transformation into a 'long' (tidy) format to unlock ggplot2's full potential.

We leverage the tidyverse suite, specifically dplyr for data manipulation and tidyr for reshaping, to engineer our protein data into this optimal structure. The process involves pivoting wider columns into fewer, more descriptive columns, ensuring that attributes like 'Condition' and 'Protein Identifier' become distinct variables rather than embedded within column headers. This meticulous preprocessing step is not a mere formality; it is a critical leverage point that dictates the ease, accuracy, and interpretability of your subsequent visualizations. Incorrect data formatting is a common pitfall that can lead to frustration and erroneous plots. By meticulously structuring our data from the outset, we forge a clear visual blueprint, setting the stage for impactful graphical storytelling and minimizing downstream debugging efforts.

# Ensure ggplot2 and tidyverse are installed and loaded
# install.packages("ggplot2")
# install.packages("dplyr") # For data manipulation
# install.packages("tidyr")  # For tidying data
library(ggplot2)
library(dplyr)
library(tidyr)

# --- Step 1: Create a Sample Protein Dataset ---
# Simulate a dataset for protein expression across different conditions and replicates.
# This data is often in a 'wide' format initially, needing transformation to 'long' format
# for optimal ggplot2 usage (tidy data principles).
set.seed(123) # for reproducibility
protein_data_wide <- data.frame(
  Replicate = 1:5,
  Control_ProteinA = rnorm(5, mean = 100, sd = 15),
  Control_ProteinB = rnorm(5, mean = 50, sd = 10),
  Treated_ProteinA = rnorm(5, mean = 120, sd = 20),
  Treated_ProteinB = rnorm(5, mean = 60, sd = 12)
)

cat("Initial 'wide' protein data structure:\n")
print(protein_data_wide)

# --- Step 2: Transform Data to 'Tidy' Format (Long Format) ---
# ggplot2 operates best on 'tidy' data, where each row is an observation, 
# each column is a variable, and each cell is a single value.
# We'll use pivot_longer from tidyr to transform the data.
protein_data_long <- protein_data_wide %>%
  pivot_longer(
    cols = -Replicate, # All columns except Replicate
    names_to = "Condition_Protein", # New column for original column names
    values_to = "ExpressionLevel" # New column for the values
  ) %>% 
  separate(
    col = Condition_Protein, # Column to split
    into = c("Condition", "Protein"), # New columns to create
    sep = "_", # Separator character
    remove = TRUE # Remove the original column
  ) 

cat("\nTransformed 'tidy' protein data structure (long format):\n")
print(head(protein_data_long))

# --- Step 3: Ensure Data Types are Correct ---
# Convert 'Condition' and 'Protein' to factors for proper categorical plotting.
protein_data_long <- protein_data_long %>%
  mutate(
    Condition = factor(Condition, levels = c("Control", "Treated")),
    Protein = factor(Protein)
  )

cat("\nData structure after factor conversion:\n")
str(protein_data_long)

# Now 'protein_data_long' is ready for ggplot2 plotting.
Activate Insight: Crafting Essential Protein Data Visualizations

Activate Insight: Crafting Essential Protein Data Visualizations

With our data meticulously prepared, we activate insight by crafting core visualizations that reveal the underlying dynamics of protein data. ggplot2 provides a powerful array of geom functions, each tailored to highlight specific aspects of your biological data. We prioritize three fundamental plot types for protein analysis: box plots, violin plots, and bar plots with error bars, complemented by scatter plots for relationships.

  • Box Plots: These are indispensable for rapidly comparing the distribution of protein expression levels across different experimental conditions or protein types. They efficiently summarize key statistical properties: median, quartiles, and potential outliers. We engineer our plots by mapping categorical variables (e.g., 'Condition', 'Protein') to the x-axis and quantitative measures (e.g., 'ExpressionLevel') to the y-axis, employing geom_boxplot().
  • Violin Plots: Building on box plots, violin plots enrich our visual narrative by illustrating the density distribution of data points. They are particularly effective when we need to convey not just the summary statistics but also the shape and spread of the data, especially when comparing groups with varying sample sizes. Combining geom_violin() with an embedded geom_boxplot() provides a comprehensive view.
  • Bar Plots with Error Bars: When the objective is to highlight mean or median comparisons between discrete groups, bar plots, augmented with clearly defined error bars (e.g., standard error of the mean or standard deviation), are the go-to choice. We use stat_summary() to compute and plot these aggregates, ensuring that statistical variability is transparently presented.

Each plot type serves a unique purpose, allowing us to decode different facets of our protein data, from overall distributions to specific mean differences. Mastering these foundational visualizations empowers us to activate a deeper understanding of our experimental results, paving the way for more sophisticated analyses.

# Load necessary libraries (already loaded in previous step, but good practice for standalone blocks)
library(ggplot2)
library(dplyr)

# Assume 'protein_data_long' is already prepared from the previous step
# If not, run the previous code block to generate it.

# --- Step 1: Visualize Protein Expression Distribution using Box Plots ---
# Box plots are excellent for comparing distributions and identifying outliers across categories.
# We map 'Condition' to the x-axis, 'ExpressionLevel' to the y-axis, 
# and use 'Protein' for coloring to distinguish individual proteins.
plot_boxplot <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Protein)) +
  geom_boxplot(position = position_dodge(width = 0.8)) + # Dodge boxes to prevent overlap
  labs(
    title = "Protein Expression Levels by Condition",
    x = "Treatment Condition",
    y = "Expression Level (Arbitrary Units)",
    fill = "Protein Type"
  ) +
  theme_minimal() + # Start with a clean theme
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"), # Center and bold title
    axis.title = element_text(face = "bold"), # Bold axis titles
    legend.title = element_text(face = "bold") # Bold legend title
  )

cat("Generating Box Plot for Protein Expression...\n")
print(plot_boxplot)

# --- Step 2: Visualize Protein Expression Density using Violin Plots ---
# Violin plots are similar to box plots but also show the density distribution of data.
plot_violin <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Protein)) +
  geom_violin(trim = FALSE, position = position_dodge(width = 0.8), alpha = 0.7) + # trim=FALSE shows full range
  geom_boxplot(width = 0.1, position = position_dodge(width = 0.8), outlier.shape = NA) + # Add small boxplot inside
  labs(
    title = "Protein Expression Density by Condition",
    x = "Treatment Condition",
    y = "Expression Level (Arbitrary Units)",
    fill = "Protein Type"
  ) +
  theme_minimal() + 
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.title = element_text(face = "bold"),
    legend.title = element_text(face = "bold")
  )

cat("\nGenerating Violin Plot for Protein Expression...\n")
print(plot_violin)

# --- Step 3: Visualize Mean Protein Levels with Error Bars (Bar Plot) ---
# Bar plots with error bars (e.g., standard error of the mean) are common for showing central tendency.
plot_bar_error <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Protein)) +
  stat_summary(fun = "mean", geom = "bar", position = position_dodge(width = 0.8), alpha = 0.8) + # Bars for mean
  stat_summary(fun.data = "mean_se", geom = "errorbar", position = position_dodge(width = 0.8), width = 0.2) + # Error bars for SE
  labs(
    title = "Mean Protein Expression with Standard Error",
    x = "Treatment Condition",
    y = "Mean Expression Level",
    fill = "Protein Type"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.title = element_text(face = "bold"),
    legend.title = element_text(face = "bold")
  )

cat("\nGenerating Bar Plot with Error Bars...\n")
print(plot_bar_error)

Decode Visual Narratives: Enhancing Aesthetics for Publication

Raw visualizations, however informative, rarely meet the stringent demands of scientific publication. We must decode visual narratives by meticulously refining aesthetics, transforming functional plots into compelling scientific arguments. This critical phase involves customizing every element to enhance clarity, impact, and adherence to journal guidelines. The foundation is laid by selecting a professional theme, such as theme_classic() or theme_bw(), which strips away extraneous visual noise, providing a clean canvas.

We then engineer precise control over textual elements using the theme() function. This allows us to adjust font sizes, styles (bold, italic), and alignment for titles, subtitles, axis labels, and legend text, ensuring legibility and hierarchy of information. A common error is neglecting consistent font sizes across all figures within an article; we must maintain uniformity to project scientific rigor. Furthermore, labs() is indispensable for crafting descriptive and succinct titles, axis labels, and legend titles, often incorporating mathematical expressions or Greek letters using R's expression() function for scientific accuracy.

Color selection is a powerful leverage point. We move beyond default palettes to implement color-blind friendly schemes (e.g., using `RColorBrewer` palettes like 'Set1' or 'Dark2' or manually defining hexadecimal codes) ensuring our message is accessible to all readers. Over-saturated or clashing colors can distract and detract from the data's message. Finally, for complex datasets involving multiple comparisons or variables, faceting with facet_wrap() or facet_grid() allows us to generate multiple small plots, each focusing on a subset of the data (e.g., individual proteins or conditions). This technique is invaluable for managing visual complexity and presenting nuanced comparisons, making our narrative clear and digestible for the scientific audience.

# Load necessary libraries
library(ggplot2)
library(dplyr)

# Assume 'protein_data_long' is already prepared from the previous steps.

# --- Step 1: Apply a Professional Theme and Customize Labels ---
# We'll enhance the box plot from the previous section.
plot_enhanced_theme <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Protein)) +
  geom_boxplot(position = position_dodge(width = 0.8), alpha = 0.7) +
  labs(
    title = "Comparative Protein Expression Across Conditions",
    subtitle = "Analyzing two key proteins under control and treated states",
    x = "Experimental Condition",
    y = expression(paste("Protein Expression Level (", italic("log"[2]), " Fold Change)")), # Example with expression
    fill = "Protein Identity"
  ) +
  theme_classic() + # A clean, publication-ready theme
  theme(
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5), # Larger, bold, centered title
    plot.subtitle = element_text(size = 12, hjust = 0.5), # Centered subtitle
    axis.title.x = element_text(size = 14, face = "bold", margin = margin(t = 10)), # Custom margin for axis title
    axis.title.y = element_text(size = 14, face = "bold", margin = margin(r = 10)),
    axis.text.x = element_text(size = 12, color = "black"), # Custom axis text size and color
    axis.text.y = element_text(size = 12, color = "black"),
    legend.title = element_text(size = 12, face = "bold"),
    legend.text = element_text(size = 10),
    legend.position = "right", # Position legend on the right
    panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5) # Add a border around the plot area
  )

cat("Generating Plot with Enhanced Theme and Custom Labels...\n")
print(plot_enhanced_theme)

# --- Step 2: Implement Color-Blind Friendly Palettes and Custom Scales ---
# Utilize RColorBrewer or manual scales for accessible and impactful colors.
# Let's create a custom palette for our proteins.
custom_colors <- c("ProteinA" = "#1f78b4", "ProteinB" = "#e31a1c") # Example: blue and red

plot_custom_colors <- plot_enhanced_theme +
  scale_fill_manual(values = custom_colors) # Apply custom fill colors

cat("\nGenerating Plot with Custom, Color-Blind Friendly Colors...\n")
print(plot_custom_colors)

# --- Step 3: Utilize Faceting for Multi-Panel Comparisons (e.g., separating by protein) ---
plot_faceted <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Condition)) +
  geom_violin(trim = FALSE, alpha = 0.7) +
  geom_boxplot(width = 0.1, outlier.shape = NA) +
  labs(
    title = "Protein Expression by Condition (Faceted by Protein Type)",
    x = "Treatment Condition",
    y = "Expression Level (Arbitrary Units)",
    fill = "Condition"
  ) +
  theme_bw() + # Another clean theme for comparison
  facet_wrap(~ Protein, scales = "free_y") + # Create separate panels for each protein
  theme(
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    axis.title = element_text(size = 14, face = "bold"),
    strip.text = element_text(size = 12, face = "bold"), # Customize facet labels
    legend.position = "none" # Remove redundant legend when facetting by fill variable
  ) +
  scale_fill_brewer(palette = "Set1") # Using RColorBrewer for conditions

cat("\nGenerating Faceted Plot...\n")
print(plot_faceted)
Engineer Impact: Advanced Plotting and Export Strategies

Engineer Impact: Advanced Plotting and Export Strategies

Our final phase involves engineering maximum impact from our visualizations and strategically exporting them for publication. This demands attention to statistical annotation, multi-panel arrangement, and meticulous file export. First, we infuse our plots with quantitative rigor by adding statistical annotations. While specialized packages like ggpubr streamline adding p-values, we can also leverage geom_text() and geom_segment() directly within ggplot2 to manually place significance markers (e.g., p-values, fold changes, ANOVA results) directly onto our plots. This direct integration ensures that the statistical inference is immediately visible and contextualized within the visual data, enhancing the narrative's strength. A common error is cluttering the plot with too many annotations; prioritize key findings for clarity.

For research articles, presenting multiple related plots within a single figure panel is often necessary. The patchwork package revolutionizes this process, allowing us to seamlessly combine diverse ggplot2 objects with intuitive operators (+, |, / for concatenation and arrangement). This bypasses the cumbersome manual layout adjustments common in image editing software, ensuring consistent alignment and spacing. We can engineer complex multi-panel layouts with ease, ensuring each sub-plot contributes cohesively to the overarching scientific message.

Finally, the definitive step is exporting our publication-ready plots. The ggsave() function provides robust control over output format, dimensions, and resolution. For print publications, we prioritize vector formats like PDF or EPS, which are infinitely scalable without pixelation. When raster formats like TIFF or PNG are required (e.g., for online submission or specific journal requirements), we command high resolution (typically 300-600 DPI) and appropriate dimensions to prevent degradation upon printing or display. We also consider file compression (e.g., 'lzw' for TIFF) to manage file size without compromising visual integrity. Adhering to journal-specific guidelines for file type, resolution, and dimensions is paramount to ensure seamless submission and peer review.

# Load necessary libraries
library(ggplot2)
library(dplyr)
library(patchwork) # For combining plots
# For statistical annotations, if ggpubr is not desired, one can manually add text.
# install.packages("ggpubr") # Optional, for easy statistical annotation
# library(ggpubr)

# Assume 'protein_data_long' is already prepared.

# --- Step 1: Add Statistical Annotations Manually (e.g., p-values, fold changes) ---
# For simplicity, we'll manually add text to a plot. In real scenarios, 
# p-values would come from statistical tests (e.g., t-test, ANOVA).

# Let's create a simple plot to annotate
plot_to_annotate <- ggplot(protein_data_long, aes(x = Condition, y = ExpressionLevel, fill = Condition)) +
  geom_boxplot(alpha = 0.7) +
  labs(
    title = "Protein A Expression with Manual Annotation",
    x = "Condition",
    y = "Expression Level"
  ) +
  theme_classic() +
  theme(legend.position = "none")

# Filter for Protein A to simplify example for manual annotation
proteinA_data <- protein_data_long %>% filter(Protein == "ProteinA")

# Manually define annotation data (e.g., p-value from a t-test between Control and Treated)
# In a real scenario, this would be calculated programmatically.
annotation_data_A <- data.frame(
  Condition = c("Control"), 
  ExpressionLevel = c(140), # Y-coordinate for annotation
  label = c("p = 0.002") # Example p-value
)

plot_annotated_A <- ggplot(proteinA_data, aes(x = Condition, y = ExpressionLevel, fill = Condition)) +
  geom_boxplot(alpha = 0.7) +
  labs(
    title = "Protein A Expression with Manual P-value",
    x = "Condition",
    y = "Expression Level"
  ) +
  theme_classic() +
  theme(legend.position = "none") +
  geom_text(data = annotation_data_A, aes(x = 1.5, y = 140, label = label), # Place text between groups
            color = "black", size = 4, fontface = "bold") + 
  # Adding a line for visual connection, mimicking bracket 
  geom_segment(aes(x = 1, xend = 2, y = 135, yend = 135), 
               color = "black", linewidth = 0.5) + 
  geom_segment(aes(x = 1, xend = 1, y = 130, yend = 135), 
               color = "black", linewidth = 0.5) + 
  geom_segment(aes(x = 2, xend = 2, y = 130, yend = 135), 
               color = "black", linewidth = 0.5)

cat("Generating Plot with Manual Statistical Annotation...\n")
print(plot_annotated_A)

# --- Step 2: Combine Multiple Plots using patchwork ---
# Let's combine our enhanced box plot and our faceted violin plot.
# Re-generate the enhanced box plot for Protein B for better comparison.
proteinB_data <- protein_data_long %>% filter(Protein == "ProteinB")
plot_annotated_B <- ggplot(proteinB_data, aes(x = Condition, y = ExpressionLevel, fill = Condition)) +
  geom_boxplot(alpha = 0.7) +
  labs(
    title = "Protein B Expression with Manual P-value",
    x = "Condition",
    y = "Expression Level"
  ) +
  theme_classic() +
  theme(legend.position = "none") +
  geom_text(data = data.frame(Condition = c("Control"), ExpressionLevel = c(85), label = c("p = 0.15")), 
            aes(x = 1.5, y = 85, label = label),
            color = "black", size = 4, fontface = "bold") + 
  geom_segment(aes(x = 1, xend = 2, y = 80, yend = 80), color = "black", linewidth = 0.5) + 
  geom_segment(aes(x = 1, xend = 1, y = 75, yend = 80), color = "black", linewidth = 0.5) + 
  geom_segment(aes(x = 2, xend = 2, y = 75, yend = 80), color = "black", linewidth = 0.5)

combined_plots <- plot_annotated_A + plot_annotated_B + 
  plot_layout(design = "A\n                                B") # Stack plots vertically

# Alternatively, arrange side-by-side
combined_plots_side_by_side <- plot_annotated_A + plot_annotated_B + 
  plot_layout(guides = "collect") & theme(legend.position = "bottom")

cat("\nGenerating Combined Plots using patchwork (stacked and side-by-side)...\n")
print(combined_plots)
print(combined_plots_side_by_side)

# --- Step 3: Export Publication-Ready Plots using ggsave ---
# Exporting the combined plots as high-resolution TIFF and PDF files.

# Define output path (adjust as needed)
output_dir <- getwd() # Current working directory

# Save as TIFF (common for journals)
ggsave(filename = file.path(output_dir, "combined_protein_expression.tiff"),
       plot = combined_plots_side_by_side,
       width = 8, height = 5, units = "in", dpi = 300, # Common dimensions and resolution
       compression = "lzw") # LZW compression for TIFFs

# Save as PDF (vector graphic, scalable)
ggsave(filename = file.path(output_dir, "combined_protein_expression.pdf"),
       plot = combined_plots_side_by_side,
       width = 8, height = 5, units = "in") # DPI not needed for vector formats

cat(paste0("\nPlots saved to: ", output_dir, "/combined_protein_expression.tiff and .pdf\n"))

# Example of saving a single plot
ggsave(filename = file.path(output_dir, "protein_A_annotated.png"),
       plot = plot_annotated_A,
       width = 6, height = 4, units = "in", dpi = 600) # High DPI for PNG

cat(paste0("Single plot saved to: ", output_dir, "/protein_A_annotated.png\n"))

Key Takeaways

Mastering Tidy Data for ggplot2

Forge a strong foundation: Always transform your protein data into a 'tidy' (long) format using tidyr::pivot_longer(). This structure is paramount for ggplot2's Grammar of Graphics, simplifying aesthetic mappings and preventing common plotting errors. Ensure categorical variables are factors.

Core Visualizations and Aesthetic Enhancement

Activate insight with appropriate geoms: Utilize geom_boxplot() and geom_violin() for distributions, and geom_bar() with stat_summary() for mean comparisons with error bars. Decode visual narratives: Elevate plots using professional themes (theme_classic()), customize labels with labs(), control text appearance with theme(), and implement colorblind-friendly palettes (scale_fill_manual(), RColorBrewer). Employ facet_wrap() for multi-variable comparisons.

Advanced Techniques and Publication-Ready Export

Engineer impact: Incorporate statistical annotations (e.g., p-values via geom_text()) to contextualize findings. Combine multiple plots seamlessly using the patchwork package for complex figures. Final step: Export high-quality graphics using ggsave(). Prioritize vector formats (PDF, EPS) for infinite scalability or high-resolution raster formats (TIFF, PNG at 300-600 DPI) per journal specifications, ensuring accurate dimensions and file compression.

FAQ

  • Why is 'tidy' data crucial for ggplot2, especially with protein data?

    Tidy data is the backbone of ggplot2 because it aligns with the 'Grammar of Graphics' philosophy. Each variable (e.g., protein name, condition, expression level) becomes a distinct column, allowing ggplot2 to directly map these variables to aesthetic elements (like x-axis, y-axis, color, fill) without ambiguity. For protein data, which can often be complex with multiple conditions and measurements, transforming it into a tidy format simplifies plotting code, reduces errors, and makes the visualization process significantly more intuitive and reproducible. It ensures that ggplot2 can correctly interpret and render the statistical relationships inherent in your data.

  • How do I choose the right plot type for my protein data?

    Choosing the correct plot type is a strategic decision driven by your research question.

    • Use Box Plots or Violin Plots to visualize the distribution, median, quartiles, and range of protein expression across different groups or conditions. Violin plots additionally reveal density.
    • Employ Bar Plots with Error Bars for direct comparison of mean or median values between discrete categories, clearly showing central tendency and variability.
    • Utilize Scatter Plots to explore relationships or correlations between two continuous protein measurements or a protein measurement and another biological variable.
    • Consider Heatmaps for visualizing expression patterns of many proteins across many samples (e.g., proteomics data), often combined with clustering).

    Each type activates a different facet of your data, so select the one that most effectively communicates your specific biological insight.

  • What are the most common pitfalls when generating publication-ready plots, and how can I avoid them?

    Several pitfalls can compromise plot quality:

    • Lack of Clarity: Over-cluttered plots, small fonts, or ambiguous labels obscure the message. Solution: Prioritize simplicity, use legible font sizes (typically >8pt after scaling), and craft concise, descriptive labels and titles.
    • Poor Aesthetics: Default colors, unreadable backgrounds, or inconsistent themes undermine professionalism. Solution: Adopt clean themes (e.g., theme_classic()), use colorblind-friendly palettes, and ensure consistent styling across all figures.
    • Misleading Statistics: Incorrectly displayed error bars or lack of statistical context can misrepresent findings. Solution: Clearly label error bars (SE, SD, 95% CI) and integrate relevant p-values or significance markers transparently.
    • Suboptimal Export Settings: Low-resolution images or incorrect file formats lead to rejection or poor reproduction. Solution: Use ggsave() with appropriate dpi (300-600 for raster, none for vector), width, and height, adhering strictly to journal guidelines.

    A systematic review of your plots against these points before submission can preempt many issues.