Answers.Body: <p>ggplot2 is cool and logical. There isn't an equivalent &quot;cool and logical&quot; for Python.</p>
  <p>Here is the answer minus the statistical overlay (library loading issue),</p>
  <pre><code>library(ggplot2)
  library(dplyr)
  
  # Dummy data
  set.seed(123)  # set random seed!!
  metadata &lt;- data.frame(
      sex = sample(c(&quot;M&quot;, &quot;F&quot;), 100, replace = TRUE),
      subgroups = sample(c(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;), 100, replace = TRUE),
      air_quality = sample(0:100, 100, replace = TRUE)
  )
  
  # Boxplot
  p &lt;- ggplot(metadata, aes(x = sex, y = air_quality)) +
      geom_boxplot(aes(fill = subgroups)) +
      labs(x = &quot;Sex&quot;, y = &quot;Air Quality&quot;, title = &quot;Air Quality by Gender and Subgroup&quot;)
  
  # Counts and subgroups
  n_data &lt;- metadata %&gt;%
      group_by(sex, subgroups) %&gt;%
      summarise(n = n(), .groups = &quot;drop&quot;) %&gt;%
      group_by(sex) %&gt;%
      mutate(y_pos = max(metadata<span class="math-container">$air_quality[metadata$</span>sex == first(sex)]) + 5)
  
  # Total counts for each gender
  total_n &lt;- metadata %&gt;%
      group_by(sex) %&gt;%
      summarise(total = n()) %&gt;%
      mutate(y_pos = max(metadata$air_quality) + 15)  # Position above subgroup counts
  
  # &quot;stack the plot&quot;
  p &lt;- p +
      # Subgroup counts
      geom_text(data = n_data,
                aes(x = sex, y = y_pos, label = paste(&quot;n =&quot;, n), group = subgroups),
                position = position_dodge(width = 0.75),
                size = 4,
                color = &quot;red&quot;) +
      # Total counts per gender
      geom_text(data = total_n,
                aes(x = sex, y = y_pos, label = paste(&quot;Group n =&quot;, total)),
                size = 5,
                color = &quot;blue&quot;)
  
  print(p)
  
  # Save it
  ggsave(&quot;air_quality_plot.pdf&quot;, p)
  </code></pre>
  <p><a href="https://i.sstatic.net/TMEhu3QJ.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/TMEhu3QJ.jpg" alt="enter image description here" /></a></p>
  <p>Is this ok? I dunno why I'd got a stats library loading issue, but R ... its not my favourite.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-12-19T09:36:08.077Z
Answers.Text: Thank you very much. However the numbers are going on top of each other, any idea how I could fix this? Thanks again ! (see picture  in original post)
Answers.UserDisplayName: Bine
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-12-20T15:47:26.280Z
Answers.Text: Thank you so much for your answer. I get the following error messagen:Error in `mutate()`:
  ℹ In argument: `max_y = +...`.
  ℹ In group 1: `sex = "FEMALE"`.
  Caused by error in `Summary.factor()`:
  ! 'max' not meaningful for factors
Answers.UserDisplayName: Bine
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-12-31T06:56:07.743Z
Answers.Text: @Bine done minus the statistical overlay ... stats package issue.
Answers.UserDisplayName: M__
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-12-31T07:25:00.117Z
Answers.Text: Awesome! That's exactly what I was looking for. I added the statistical test in the final solution above. Thanks again for your help very appreciated, happy new year!
Answers.UserDisplayName: Bine
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-12-19T01:33:11.467Z
Answers.LastActivityDate: 2024-12-31T07:06:45.247Z
Answers.LastEditDate: 2024-12-31T07:06:45.247Z
Answers.UserDisplayName: M__
Body: <p>Good afternoon,</p>
  <p>I am trying to add the n for each of the subgroups in females and males (not just for the total in male (n=107) and female (n=94)). However, I havent found a way to do so:</p>
  <p>The following code is used to generate the plot below:</p>
  <pre><code>stat.test &lt;- metadata %&gt;%
    wilcox_test(air_quality ~ sex) %&gt;%
    add_significance()
  
  p1&lt;-ggplot(data = metadata, aes(x=sex, y=air_quality)) + geom_boxplot(aes(fill=subgroups),show.legend = TRUE) 
  stat.test &lt;- stat.test %&gt;% add_xy_position(x = &quot;sex&quot;)
  
  p2&lt;-p1+ stat_pvalue_manual(stat.test, label = &quot;p&quot;) +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  p3&lt;-p2+ stat_n_text( y.pos =0.05) + labs(x =&quot;Sex&quot;, y = &quot;Air quality&quot;)+labs(title = &quot;Airquality&quot;)
  p3
  </code></pre>
  <p><a href="https://i.sstatic.net/jykYiXeF.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/jykYiXeF.png" alt="enter image description here" /></a></p>
  <p>Any idea?</p>
  <p>Thanks a lot!</p>
  <hr />
  <p>Update after adding the suggested solution below from M:</p>
  <p><a href="https://i.sstatic.net/MB6MGDKp.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/MB6MGDKp.png" alt="enter image description here" /></a></p>
  <hr />
  <p>Update 2 with dummy data:</p>
  <pre><code>## Create some dummy data
  
  metadata &lt;- data.frame(sex = sample(c(&quot;M&quot;, &quot;F&quot;), 100, replace = TRUE), 
                         subgroups = sample(c(&quot;1&quot;, &quot;2&quot;,&quot;3&quot;), 100, replace = TRUE), 
                         air_quality = sample(0:100,100, replace = TRUE))
  
  
  # Is air_quality an issue?
  metadata<span class="math-container">$air_quality &lt;- as.numeric(as.character(metadata$</span>air_quality))
  
  stat.test &lt;- metadata %&gt;%
    wilcox_test(air_quality ~ sex) %&gt;%
    add_significance()
  
  # boxplot
  p1 &lt;- ggplot(data = metadata, aes(x = sex, y = air_quality)) + 
    geom_boxplot(aes(fill = subgroups), show.legend = TRUE)
  
  # p-values
  stat.test &lt;- stat.test %&gt;% add_xy_position(x = &quot;sex&quot;)
  
  # p-value plot
  p2 &lt;- p1 + stat_pvalue_manual(stat.test, label = &quot;p&quot;) +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  # Count data for labels
  n_data &lt;- metadata %&gt;%
    group_by(sex, subgroups) %&gt;%
    summarise(n = n(), .groups = 'drop')
  
  # Dynamic stuff
  n_data &lt;- n_data %&gt;%
    group_by(sex) %&gt;%
    mutate(max_y = max(metadata<span class="math-container">$air_quality[metadata$</span>sex == sex], na.rm = TRUE) + 0.1)
  
  p3 &lt;- p2 + 
    geom_text(data = n_data, aes(x = sex, y = max_y, label = paste(&quot;n =&quot;, n), group = subgroups),
              position = position_dodge(width = 0.1), size = 3, color = &quot;blue&quot;) +
    labs(x = &quot;Sex&quot;, y = &quot;Air Quality&quot;) + 
    labs(title = &quot;Air Quality by Gender and Subgroup&quot;)
  
  p3
  </code></pre>
  <p><a href="https://i.sstatic.net/cWPJEkwg.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/cWPJEkwg.png" alt="enter image description here" /></a></p>
  <hr />
  <p>Update 3 including final solution with statistical test</p>
  <pre><code># Dummy data
  set.seed(123)  # set random seed!!
  metadata &lt;- data.frame(
    sex = sample(c(&quot;M&quot;, &quot;F&quot;), 100, replace = TRUE),
    subgroups = sample(c(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;), 100, replace = TRUE),
    air_quality = sample(0:100, 100, replace = TRUE)
  )
  
  stat.test &lt;- metadata %&gt;%
    wilcox_test(air_quality ~ sex) %&gt;%
    add_significance()
  
  
  # Boxplot
  p &lt;- ggplot(metadata, aes(x = sex, y = air_quality)) +
    geom_boxplot(aes(fill = subgroups)) +
    labs(x = &quot;Sex&quot;, y = &quot;Air Quality&quot;, title = &quot;Air Quality by Gender and Subgroup&quot;)
  
  # p-values
  stat.test &lt;- stat.test %&gt;% add_xy_position(x = &quot;sex&quot;)
  
  # p-value plot
  p &lt;- p + stat_pvalue_manual(stat.test, label = &quot;p&quot;) +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  # Counts and subgroups
  n_data &lt;- metadata %&gt;%
    group_by(sex, subgroups) %&gt;%
    summarise(n = n(), .groups = &quot;drop&quot;) %&gt;%
    group_by(sex) %&gt;%
    mutate(y_pos = max(metadata<span class="math-container">$air_quality[metadata$</span>sex == first(sex)]) + 5)
  
  # Total counts for each gender
  total_n &lt;- metadata %&gt;%
    group_by(sex) %&gt;%
    summarise(total = n()) %&gt;%
    mutate(y_pos = max(metadata$air_quality) + 15)  # Position above subgroup counts
  
  # &quot;stack the plot&quot;
  p &lt;- p +
    # Subgroup counts
    geom_text(data = n_data,
              aes(x = sex, y = y_pos, label = paste(&quot;n =&quot;, n), group = subgroups),
              position = position_dodge(width = 0.75),
              size = 4,
              color = &quot;red&quot;) +
    # Total counts per gender
    geom_text(data = total_n,
              aes(x = sex, y = y_pos, label = paste(&quot;Group n =&quot;, total)),
              size = 5,
              color = &quot;blue&quot;)
  
  p
  </code></pre>
  <p><a href="https://i.sstatic.net/kZaJnkUb.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/kZaJnkUb.png" alt="enter image description here" /></a></p>
  
Tags: <r><ggplot2>
Title: ggplot with stat_n_text with n for subgroups
ThreadText: # ggplot with stat_n_text with n for subgroups
  
  (Asked by: username_0 on 2024-12-18)
  
  Good afternoon,
  
  I am trying to add the n for each of the subgroups in females and males (not just for the total in male (n=107) and female (n=94)). However, I havent found a way to do so:
  
  The following code is used to generate the plot below:
  
  ```
  stat.test <- metadata %>%
    wilcox_test(air_quality ~ sex) %>%
    add_significance()
  
  p1<-ggplot(data = metadata, aes(x=sex, y=air_quality)) + geom_boxplot(aes(fill=subgroups),show.legend = TRUE) 
  stat.test <- stat.test %>% add_xy_position(x = "sex")
  
  p2<-p1+ stat_pvalue_manual(stat.test, label = "p") +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  p3<-p2+ stat_n_text( y.pos =0.05) + labs(x ="Sex", y = "Air quality")+labs(title = "Airquality")
  p3
  
  ```
  
  [![enter image description here](https://i.sstatic.net/jykYiXeF.png)](https://i.sstatic.net/jykYiXeF.png)
  
  Any idea?
  
  Thanks a lot!
  
  ---
  
  Update after adding the suggested solution below from M:
  
  [![enter image description here](https://i.sstatic.net/MB6MGDKp.png)](https://i.sstatic.net/MB6MGDKp.png)
  
  ---
  
  Update 2 with dummy data:
  
  ```
  ## Create some dummy data
  
  metadata <- data.frame(sex = sample(c("M", "F"), 100, replace = TRUE), 
                         subgroups = sample(c("1", "2","3"), 100, replace = TRUE), 
                         air_quality = sample(0:100,100, replace = TRUE))
  
  # Is air_quality an issue?
  metadata$air_quality <- as.numeric(as.character(metadata$air_quality))
  
  stat.test <- metadata %>%
    wilcox_test(air_quality ~ sex) %>%
    add_significance()
  
  # boxplot
  p1 <- ggplot(data = metadata, aes(x = sex, y = air_quality)) + 
    geom_boxplot(aes(fill = subgroups), show.legend = TRUE)
  
  # p-values
  stat.test <- stat.test %>% add_xy_position(x = "sex")
  
  # p-value plot
  p2 <- p1 + stat_pvalue_manual(stat.test, label = "p") +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  # Count data for labels
  n_data <- metadata %>%
    group_by(sex, subgroups) %>%
    summarise(n = n(), .groups = 'drop')
  
  # Dynamic stuff
  n_data <- n_data %>%
    group_by(sex) %>%
    mutate(max_y = max(metadata$air_quality[metadata$sex == sex], na.rm = TRUE) + 0.1)
  
  p3 <- p2 + 
    geom_text(data = n_data, aes(x = sex, y = max_y, label = paste("n =", n), group = subgroups),
              position = position_dodge(width = 0.1), size = 3, color = "blue") +
    labs(x = "Sex", y = "Air Quality") + 
    labs(title = "Air Quality by Gender and Subgroup")
  
  p3
  
  ```
  
  [![enter image description here](https://i.sstatic.net/cWPJEkwg.png)](https://i.sstatic.net/cWPJEkwg.png)
  
  ---
  
  Update 3 including final solution with statistical test
  
  ```
  # Dummy data
  set.seed(123)  # set random seed!!
  metadata <- data.frame(
    sex = sample(c("M", "F"), 100, replace = TRUE),
    subgroups = sample(c("1", "2", "3"), 100, replace = TRUE),
    air_quality = sample(0:100, 100, replace = TRUE)
  )
  
  stat.test <- metadata %>%
    wilcox_test(air_quality ~ sex) %>%
    add_significance()
  
  # Boxplot
  p <- ggplot(metadata, aes(x = sex, y = air_quality)) +
    geom_boxplot(aes(fill = subgroups)) +
    labs(x = "Sex", y = "Air Quality", title = "Air Quality by Gender and Subgroup")
  
  # p-values
  stat.test <- stat.test %>% add_xy_position(x = "sex")
  
  # p-value plot
  p <- p + stat_pvalue_manual(stat.test, label = "p") +
    scale_y_continuous(expand = expansion(mult = c(0.01, 0.1)))
  
  # Counts and subgroups
  n_data <- metadata %>%
    group_by(sex, subgroups) %>%
    summarise(n = n(), .groups = "drop") %>%
    group_by(sex) %>%
    mutate(y_pos = max(metadata$air_quality[metadata$sex == first(sex)]) + 5)
  
  # Total counts for each gender
  total_n <- metadata %>%
    group_by(sex) %>%
    summarise(total = n()) %>%
    mutate(y_pos = max(metadata$air_quality) + 15)  # Position above subgroup counts
  
  # "stack the plot"
  p <- p +
    # Subgroup counts
    geom_text(data = n_data,
              aes(x = sex, y = y_pos, label = paste("n =", n), group = subgroups),
              position = position_dodge(width = 0.75),
              size = 4,
              color = "red") +
    # Total counts per gender
    geom_text(data = total_n,
              aes(x = sex, y = y_pos, label = paste("Group n =", total)),
              size = 5,
              color = "blue")
  
  p
  
  ```
  
  [![enter image description here](https://i.sstatic.net/kZaJnkUb.png)](https://i.sstatic.net/kZaJnkUb.png)
  
  **Tags:** r, ggplot2
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2024-12-19. Score: 1 | Accepted answer
  
  ggplot2 is cool and logical. There isn't an equivalent "cool and logical" for Python.
  
  Here is the answer minus the statistical overlay (library loading issue),
  
  ```
  library(ggplot2)
  library(dplyr)
  
  # Dummy data
  set.seed(123)  # set random seed!!
  metadata <- data.frame(
      sex = sample(c("M", "F"), 100, replace = TRUE),
      subgroups = sample(c("1", "2", "3"), 100, replace = TRUE),
      air_quality = sample(0:100, 100, replace = TRUE)
  )
  
  # Boxplot
  p <- ggplot(metadata, aes(x = sex, y = air_quality)) +
      geom_boxplot(aes(fill = subgroups)) +
      labs(x = "Sex", y = "Air Quality", title = "Air Quality by Gender and Subgroup")
  
  # Counts and subgroups
  n_data <- metadata %>%
      group_by(sex, subgroups) %>%
      summarise(n = n(), .groups = "drop") %>%
      group_by(sex) %>%
      mutate(y_pos = max(metadata$air_quality[metadata$sex == first(sex)]) + 5)
  
  # Total counts for each gender
  total_n <- metadata %>%
      group_by(sex) %>%
      summarise(total = n()) %>%
      mutate(y_pos = max(metadata$air_quality) + 15)  # Position above subgroup counts
  
  # "stack the plot"
  p <- p +
      # Subgroup counts
      geom_text(data = n_data,
                aes(x = sex, y = y_pos, label = paste("n =", n), group = subgroups),
                position = position_dodge(width = 0.75),
                size = 4,
                color = "red") +
      # Total counts per gender
      geom_text(data = total_n,
                aes(x = sex, y = y_pos, label = paste("Group n =", total)),
                size = 5,
                color = "blue")
  
  print(p)
  
  # Save it
  ggsave("air_quality_plot.pdf", p)
  
  ```
  
  [![enter image description here](https://i.sstatic.net/TMEhu3QJ.jpg)](https://i.sstatic.net/TMEhu3QJ.jpg)
  
  Is this ok? I dunno why I'd got a stats library loading issue, but R ... its not my favourite.
  
  > **Comments:**
  > **username_0**: Thank you very much. However the numbers are going on top of each other, any idea how I could fix this? Thanks again ! (see picture  in original post)  
  > **username_0**: Thank you so much for your answer. I get the following error messagen:Error in `mutate()`: ℹ In argument: `max_y = +...`. ℹ In group 1: `sex = "FEMALE"`. Caused by error in `Summary.factor()`: ! 'max' not meaningful for factors  
  > **username_1**: @username_0 done minus the statistical overlay ... stats package issue.  
  > **username_0**: Awesome! That's exactly what I was looking for. I added the statistical test in the final solution above. Thanks again for your help very appreciated, happy new year!  
  
  ---
  
  