Mercurial > repos > iuc > phyloseq_plot_richness
view phyloseq_plot_bar.R @ 9:10a7732528b2 draft default tip
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/phyloseq commit a5ae2f86b2955290a4c81ab234f02cc51020f970
author | iuc |
---|---|
date | Thu, 13 Mar 2025 09:48:48 +0000 |
parents | d6cbeb48294d |
children |
line wrap: on
line source
#!/usr/bin/env Rscript # Load libraries suppressPackageStartupMessages(library("optparse")) suppressPackageStartupMessages(library("phyloseq")) suppressPackageStartupMessages(library("ggplot2")) suppressPackageStartupMessages(library("dplyr")) # Define options option_list <- list( make_option(c("--input"), action = "store", dest = "input", help = "Input file containing a phyloseq object" ), make_option(c("--x"), action = "store", dest = "x", help = "Variable for x-axis (e.g., 'Sample', 'Phylum')" ), make_option(c("--fill"), action = "store", dest = "fill", default = NULL, help = "Variable for fill color (e.g., 'Genus', 'Order'). Use 'ASV' as argument to show each OTU/ASV." ), make_option(c("--facet"), action = "store", dest = "facet", default = NULL, help = "Facet by variable (optional)" ), make_option(c("--output"), action = "store", dest = "output", help = "Output file (PDF)" ), make_option(c("--topX"), action = "store", dest = "topX", default = NULL, help = "Show only the top X taxa based on abundance (e.g., '10') (optional)" ), make_option(c("--keepOthers"), action = "store_true", dest = "keepOthers", default = FALSE, help = "Keep taxa not in topX and label them as 'Others' (optional)" ), make_option(c("--keepNonAssigned"), action = "store_true", dest = "keepNonAssigned", default = FALSE, help = "Keep taxa labeled as 'Not Assigned' (optional)" ), make_option(c("--normalize"), action = "store_true", dest = "normalize", default = FALSE, help = "Normalize abundances to sum to 100% (optional)" ), make_option(c("--normalize_x"), action = "store_true", dest = "normalize_x", default = FALSE, help = "Normalize x groups to sum up to 100%" ), make_option(c("--width"), action = "store", dest = "width", default = 10, type = "numeric", help = "Width of the output plot in inches" ), make_option(c("--height"), action = "store", dest = "height", default = 8, type = "numeric", help = "Height of the output plot in inches" ), make_option(c("--device"), action = "store", dest = "device", default = "pdf", help = "Output format (e.g., 'pdf', 'png', 'jpg')" ), make_option(c("--nolines"), action = "store_true", dest = "nolines", default = FALSE, help = "Remove borders (lines) around bars (TRUE/FALSE)" ) ) # Parse arguments parser <- OptionParser(usage = "%prog [options] file", option_list = option_list) args <- parse_args(parser, positional_arguments = TRUE) opt <- args$options # Validate required options if (is.null(opt$input) || opt$input == "") { stop("Error: Input file is required.") } if (is.null(opt$output) || opt$output == "") { stop("Error: Output file is required.") } if (is.null(opt$fill) || opt$fill == "") { print(paste("No fill chosen using ASV")) opt$fill <- "ASV" } # Load phyloseq object print(paste("Trying to read:", opt$input)) physeq <- readRDS(opt$input) ## Allow to use OTU as tax group # Extract rownames (taxids) from the tax_table and add them as a new column taxids <- rownames(tax_table(physeq)) # Get the number of columns in the tax_table num_columns <- ncol(tax_table(physeq)) # Add the taxids as a new last column in the tax_table tax_table(physeq) <- cbind(tax_table(physeq), taxid = taxids) # Rename the last column to 'ASV' / OTU does conflict with phyloseq logic colnames(tax_table(physeq))[num_columns + 1] <- "ASV" # Normalize to relative abundances if requested if (opt$normalize) { print("Normalizing abundances to sum to 100%...") physeq <- transform_sample_counts(physeq, function(x) 100 * x / sum(x)) } # Debug: Check available taxonomic ranks tax_ranks <- colnames(tax_table(physeq)) sample_vars <- colnames(sample_data(physeq)) print("Available taxonomic ranks:") print(tax_ranks) print("Available metadata:") print(sample_vars) # Handle missing or unassigned taxa for all ranks if (opt$keepNonAssigned) { # Replace NA or empty values with 'Not Assigned' for all ranks for (rank in tax_ranks) { if (rank %in% tax_ranks) { # replace NA or empty values with 'Not Assigned' tax_table(physeq)[, rank][is.na(tax_table(physeq)[, rank])] <- "Not Assigned" } } } # Filter to top X taxa if requested if (!is.null(opt$topX) && opt$topX != "") { topX <- as.numeric(opt$topX) if (is.na(topX) || topX <= 0) { stop("Error: topX should be a positive number.") } tax_rank <- opt$fill if (!tax_rank %in% colnames(tax_table(physeq))) { stop(paste("Error: Tax rank", tax_rank, "not found in tax_table.")) } physeq_agg <- tax_glom(physeq, taxrank = tax_rank) taxa_abundance <- taxa_sums(physeq_agg) tax_table_agg <- tax_table(physeq_agg) taxa_abundance_by_rank <- tapply(taxa_abundance, tax_table_agg[, tax_rank], sum) top_taxa <- names(sort(taxa_abundance_by_rank, decreasing = TRUE))[1:topX] print("Top taxa:") print(top_taxa) otus_in_top_taxa <- rownames(tax_table_agg)[tax_table_agg[, tax_rank] %in% top_taxa] # Group non-top OTUs as 'Others' if requested if (opt$keepOthers) { # Update the tax_table to assign 'Others' to non-top taxa tax_table(physeq_agg)[, tax_rank][!rownames(tax_table_agg) %in% otus_in_top_taxa] <- "Others" physeq <- physeq_agg } else { physeq <- prune_taxa(otus_in_top_taxa, physeq_agg) } } # normalize x groups if needed if (opt$x %in% sample_vars) { if (opt$normalize_x && !is.null(opt$x) && opt$x != "") { physeq_agg <- merge_samples(physeq, opt$x) physeq <- transform_sample_counts(physeq_agg, function(x) (x / sum(x) * 100)) opt$x <- NULL # set to Null since we do not need x for downstream now opt$facet <- NULL # set to Null since facetting does not work with normalize x warning(paste("normalize x does not work with facetting")) } } else { warning(paste("x", opt$x, "not found in sample data. Skipping normalize_x.")) } # Check if the facet variable is valid and exists facet_var <- NULL if (!is.null(opt$facet) && opt$facet != "") { if (opt$facet %in% sample_vars || opt$facet %in% tax_ranks) { facet_var <- opt$facet # Store facet variable for later } else { warning(paste("Facet variable", opt$facet, "not found in sample data or tax ranks. Skipping faceting.")) } } # Determine if faceting is needed facet_formula <- if (!is.null(facet_var)) as.formula(paste("~", facet_var)) else NULL # Define color based on the `nolines` option plot_color <- ifelse(opt$nolines, NA, "black") # Generate bar plot if (!is.null(opt$x) && opt$x != "") { p <- plot_bar(physeq, x = opt$x, fill = opt$fill ) + facet_wrap(facet_formula, scales = "free_x") + geom_bar( stat = "identity", position = "stack", aes(fill = !!sym(opt$fill)), color = plot_color ) } else { p <- plot_bar(physeq, fill = opt$fill ) + facet_wrap(facet_formula, scales = "free_x") + geom_bar( stat = "identity", position = "stack", aes(fill = !!sym(opt$fill)), color = plot_color ) } # Reorder fill levels to ensure "Not Assigned" and "Others" are at the bottom if they exist fill_values <- unique(p$data[[opt$fill]]) # Get unique fill values new_levels <- setdiff(fill_values, c("Not Assigned", "Others")) # Exclude "Not Assigned" and "Others" if ("Not Assigned" %in% fill_values) { new_levels <- c("Not Assigned", new_levels) # Place "Not Assigned" at the bottom if it exists } if ("Others" %in% fill_values) { new_levels <- c("Others", new_levels) # Place "Others" at the bottom if it exists } # Apply the new levels to the fill variable in the plot data p$data[[opt$fill]] <- factor(p$data[[opt$fill]], levels = new_levels) # Save to output file ggsave( filename = opt$output, plot = p, width = opt$width, height = opt$height, device = opt$device )