Search code examples
rggplot2

How to add values next to each row in a column in a figure


I want to generate figure like this, where each row should have its value on y axis, I used red arrow to indicate where the values should be, the values stem from the data, so each bar should have a value, I added the values in the fig manually and they look same number, obviously they are not. I'm using synergyfinder package (https://github.com/IanevskiAleksandr/SynergyFinder#readme), it uses ggplot: enter image description here

This is my code:

if (!require("BiocManager", quietly = TRUE))
  install.packages("BiocManager")

BiocManager::install("synergyfinder")
s
a
.libPaths()

library(synergyfinder)
library(readxl)


res <- ReshapeData(
  data = data,
  data_type = "inhibition",
  impute = TRUE,
  impute_method = NULL,
  noise = TRUE,
  seed = 1)


res <- CalculateSynergy(
  data = res,
  method = c("ZIP", "HSA", "Bliss", "Loewe"),
  Emin = NA,
  Emax = NA,
  correct_baseline = "non")


res$drug_pairs




res <- CalculateSensitivity(
  data = res,
  correct_baseline = "non"
)


sensitive_columns <- c(
  "block_id", "drug1", "drug2",
  "ic50_1", "ic50_2",
  "ri_1", "ri_2",
  "css1_ic502", "css2_ic501", "css")
res$drug_pairs[, sensitive_columns]




for (i in c(1, 2)){
  PlotDoseResponseCurve(
    data = res,
    plot_block = 1,
    drug_index = i,
    plot_new = FALSE,
    record_plot = FALSE
  )
}

par(mar = c(4, 8, 4, 6) + 0.1) 

PlotMultiDrugBar(
  data = res,
  plot_block = 1,
  plot_value = c("response", "ZIP_synergy", "Loewe_synergy", "HSA_synergy", "Bliss_synergy"),
  sort_by = "response",
  highlight_label_size = 4
)

This is the head of dput:

 dput(head(data))

structure(list(block_id = c(1, 1, 1, 1, 1, 1), drug1 = c("X", 
"X", "X", "X", "X", "X"), drug2 = c("N", "N", "N", "N", "N", 
"N"), cell_line_name = c("A", "A", "A", "A", "A", 
"A"), conc1 = c(0, 10, 3.333, 1.111, 0.37, 0.123), conc2 = c(0, 
0, 0, 0, 0, 0), response = c(0, 94.7124199185235, 93.1970077742489, 
94.9121253949008, 92.6627816399623, 90.6942562299578), conc_unit = c("μM", 
"μM", "μM", "μM", "μM", "μM")), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))

This is the best that I could've come up with:


install.packages("scales")
library(dplyr)
library(scales)
library(ggplot2)
library(gridExtra)
library(readxl) 


data <- read_xlsx("N+X+syn.xlsx")
head(data)

res <- ReshapeData(
  data = data,
  data_type = "inhibition",
  impute = TRUE,
  impute_method = NULL,
  noise = TRUE,
  seed = 1
)

res <- CalculateSynergy(
  data = res,
  method = c("ZIP", "HSA", "Bliss", "Loewe"),
  Emin = NA,
  Emax = NA,
  correct_baseline = "non"
)

res <- CalculateSensitivity(
  data = res,
  correct_baseline = "non"
)

# Convert to data frame and clean the data
res <- as.data.frame(res)

res_clean <- res %>%
  rename(
    N = synergy_scores.conc1,
    X = synergy_scores.conc2,
    response = response.response,
    ZIP_synergy = synergy_scores.ZIP_synergy,
    Bliss_synergy = synergy_scores.Bliss_synergy,
    Loewe_synergy = synergy_scores.Loewe_synergy,
    HSA_synergy = synergy_scores.HSA_synergy
  )


res_selected <- res_clean %>%
  select(N, X, response, ZIP_synergy, Bliss_synergy, Loewe_synergy, HSA_synergy)


highlight_threshold <- 1


create_plot <- function(data, column_name, plot_number) {
  fill_condition <- if (column_name %in% c("N", "X", "response")) {
    "FALSE"  # No highlighting for N, X, or response
  } else {
    paste0(column_name, " > ", highlight_threshold)  # Apply threshold to synergy scores
  }
  
  ggplot(data, aes_string(x = column_name, y = column_name, fill = fill_condition)) +
    geom_bar(stat = "identity", width = 0.6, show.legend = FALSE) +
    geom_text(aes_string(label = column_name), hjust = -0.3, size = 5, color = "black") +
    scale_fill_manual(values = c("lightblue", "orange")) + # Keep threshold highlight color consistent
    coord_flip() +
    theme_minimal() +
    labs(
      title = paste(column_name),
      subtitle = "",
      x = "",
      y = "Value"
    ) +
    theme(
      plot.title = element_text(hjust = 0.5, face = "bold", size = 16),
      plot.subtitle = element_text(hjust = 0.5, size = 12),
      axis.text.y = element_blank() # Remove y-axis labels
    )
}


plots <- lapply(1:7, function(i) create_plot(res_selected, names(res_selected)[i], i))


grid.arrange(grobs = plots, ncol = 7, nrow = 1)




and got this ugly figure ...lol:

enter image description here


Solution

  • It's great that you added data to your question, but you didn't make sure that that data recreated the issue...you know...made it reproducible. To get the best answers quickly, that's always your best bet.

    In lieu of your data, I used the example data used in the synergy package.

    You wrote, "I used red arrow to indicate where the values should be"...but I don't know where your 'red arrows' are...

    I've started with some simple examples of where the data came from and how I can move the labels where I would like them to go.

    First, here's the data and plot I built with the PlotMultiDrugBar. Note it's an object named plt here.

    library(synergyfinder)
    library(tidyverse)
    
    data("mathews_screening_data")    # from example in synergy pkg
    dta <- ReshapeData(mathews_screening_data)
    res <- CalculateSynergy(dta)
    
    plt <- PlotMultiDrugBar(          # from your question
      data = res,
      plot_block = 1,
      plot_value = c("response", "ZIP_synergy", "Loewe_synergy", "HSA_synergy", "Bliss_synergy"),
      sort_by = "response",
      highlight_label_size = 4
    )
    

    Typically, you can get the data used in your plot by call for data. For example, I named the plot plt so normally I can get the data by calling plt$data. However, that's not the case here. The data is nested in the layer that builds the bar plot, so plt$layer[[1]]$data.

    In this first example, I kept it simple, using the value as the label and the y placement -- note that this is a faceted plot -- think x -> y and y -> x, if I move y it moves right and left.

    It's ugly!

    plt + geom_text(data = plt[["layers"]][[1]][["data"]],
                    mapping = aes(x = id, y = value, label = value))
    

    ugly

    If I just format the values, set y = 10, and make the size relative to the size of the plot....

    still pretty ugly...and illegible

    plt + geom_text(data = plt[["layers"]][[1]][["data"]],
                    mapping = aes(x = id, label = format(value, digits = 3)), # <-format me!
                    size = rel(2.3), y = 10)   # <--- I'm new
    

    ugly too

    If I wanted to right-justify my labels, I've got to do it by group, because each groups range is different. In this next example, I grouped the data by the column it's in (metric) and then captured the groups' max values. That new column (mxg) is what I assigned to y.

    It's the least ugly so far.

    plt + geom_text(data = plt[["layers"]][[1]][["data"]] %>% group_by(metric) %>% 
                      mutate(mxg = max(value)) %>% ungroup(),  # group max val!
                    mapping = aes(x = id, y = mxg,             # <--- I'm new
                                  label = format(value, digits = 3)),
                    size = rel(2.3))
    

    a bit better

    Alright, so there are some labeling options for you to pick through. As for some of the other elements you called out in your code, here's what I think you were trying to do.

    I'll start with renaming the faceted column names in the data, then add the color and theme options you identified.

    The appearance and readability is definitely much improved here. The x-axis is a bit difficult to read.

    levels( plt[["layers"]][[1]][["data"]]$metric )  # current levels
    # [1] "ispinesib\n(nM)"          "ibrutinib\n(nM)"         
    # [3] "Response\n(% inhibition)" "ZIP Synergy Score"       
    # [5] "Loewe Synergy Score"      "HSA Synergy Score"       
    # [7] "Bliss Synergy Score"      
    
    # change the names
    levels(plt$layers[[1]]$data$metric) <- c("N", "X", "response", "Zip Synergy", 
                                             "Bliss Synergy", "Loewe Synergy", "HSA Synergy")
    
    # with renamed columns 
    plt + geom_text(data = plt[["layers"]][[1]][["data"]] %>% group_by(metric) %>% 
                     mutate(mxg = max(value)) %>% ungroup(),  # group max val!
                   mapping = aes(x = id, y = mxg,
                                 label = format(value, digits = 3)),
                   size = rel(2.3)) +
      scale_fill_discrete(c("lightblue", "orange")) + 
      theme_minimal() %>% 
      theme(strip.text = element_text(face = "bold", size = rel(.8)), # facet titles
            axis.text = element_text(size = rel(.75)))                # axis tick text  
    # there is no subtitle, the titles are already centered, there is no y-axis text...unless you knew they switched...hmmm
    

    best so far