简体   繁体   中英

Replacing legend with labels in multilevel ggplot2 plot

I created this chart. The labels take too much space and it is difficult to see which state is which. So I would like to replace the legend by labels of State codes inside the graph, or bellow de x axis. Is there an easy way to do this?

Chart: 在此处输入图片说明

Code to generate it:

url <- 'https://www.dropbox.com/s/f046jroutvt8ctk/SO_example_data_put_labels_in_graph.csv?raw=1'
d <- read_csv(url)

d %>% 
  ggplot(aes(x=popNb,y=tx_atendimento)) +
  geom_rect(aes(xmin=pop1b,xmax=popNb,
                ymin=tx0,ymax=tx_atendimento,
                fill=UF)) +
  geom_segment(aes(x=pop1b,xend=popNb,
                   y=tx_atendimento_UF,yend=tx_atendimento_UF)) +
  theme(legend.position = "bottom", legend.direction = "horizontal" ) +
  ggtitle('Daycare provision rate per state and municipality in Brazil (2014)') +
  ggsave('plot_rec_bar_needs_labels.png')

Explanation:

Note that this is not a normal bar plot. Each municipality (row in dataset) is represented by a rectangle with width corresponding to the number of children in that age and height corresponding to the provision rate. I created the x values so municipalities are arranged first by the average provision rate in the state (UF) and second by the same rate among municipalities (cod_mun6) of the state. I also added bars showing the average of each municipality, although the data for these is only available in the first observation of each municipality.

So this plot mixes information at municipal and state levels (although they are "denormalized" in just one data.frame.

I know the legend can be removed with: + theme(legend.position = "none", ) I tried adding labels with:

  • + geom_label(aes(x=mean_popNb_uf,label=UF2), nudge_y =.4,label.size = 0.05 )
  • + geom_text(aes(label=UF2))

but the resulting labels look cluttered, and positioning looks weird.

I also tried geom_text_repel(aes(label=UF2)) from the ggrepel package, but no labels showed. In the past, I have used the package directlabels in the past, but don't know how to use it in this case.

The best approach may be to use facet_wrap set to 1 row to separate out the states. Note that it appears that some of the states are missing data, so they need to be filtered out (otherwise facet_wrap fails when trying to set the x axis limits for the empty plots:

d %>% 
  filter(!is.na(pop1b)
         , !is.na(popNb)) %>%
  ggplot(aes(x=popNb,y=tx_atendimento)) +
  geom_rect(aes(xmin=pop1b,xmax=popNb,
                ymin=tx0,ymax=tx_atendimento
                )) +
  geom_segment(aes(x=pop1b,xend=popNb,
                   y=tx_atendimento_UF,yend=tx_atendimento_UF)) +
  theme(axis.text.x = element_blank()) +
  ggtitle('Daycare provision rate per state and municipality in Brazil (2014)') +
  facet_wrap(~UF, scales = "free_x", nrow = 1, switch = "x") +
  theme_minimal()

Note that if you want to change the sort, you would need to set the factor levels of the UF column to whatever order you want them in.

剧情

If you want it with the "size" of the states shown, you can use, facet_grid with space = "free" like this

d %>% 
  filter(!is.na(pop1b)
         , !is.na(popNb)) %>%
  ggplot(aes(x=popNb,y=tx_atendimento)) +
  geom_rect(aes(xmin=pop1b,xmax=popNb,
                ymin=tx0,ymax=tx_atendimento
                )) +
  geom_segment(aes(x=pop1b,xend=popNb,
                   y=tx_atendimento_UF,yend=tx_atendimento_UF)) +
  ggtitle('Daycare provision rate per state and municipality in Brazil (2014)') +
  # facet_wrap(~UF, scales = "free_x", nrow = 1, switch = "x") +
  facet_grid(~UF, scales = "free_x", switch = "x", space = "free") +
  theme_minimal() +
  theme(axis.text.x = element_blank()
        , panel.margin.x = unit(0,"in"))

though note that you may need to pad some of the states if they are too narrow for the labels to fit.

在此处输入图片说明

I went ahead and added code to pad all of the States to an arbitrary desired width and to sort the values:

library(dplyr)
library(ggplot2)
library(magrittr)
url <- 'https://www.dropbox.com/s/f046jroutvt8ctk/SO_example_data_put_labels_in_graph.csv?raw=1'
# d <- read.csv(url)

desiredWidth <- 350000

toPlot <-
  d %>%
  filter(!is.na(pop1b)
         , !is.na(popNb)
         , !is.na(UF)) %>%
  split(.$UF) %>%
  lapply(function(thisState){
    # thisState <- d %>% filter(UF == "AC")
    # Find current range:
    currRange <-
      thisState %>%
      {max(.$popNb, na.rm = TRUE) -
          min(.$pop1b, na.rm = TRUE)}

    spacing <- (desiredWidth - currRange)/2

    # Add the spacing
    temp <- thisState[1:2,]
    temp$pop1b <-
      c(min(thisState$pop1b, na.rm = TRUE) - spacing
        , max(thisState$popNb, na.rm = TRUE) + 1
        )
    temp$popNb <-
      c(min(thisState$pop1b, na.rm = TRUE) - 1
        , max(thisState$popNb, na.rm = TRUE) + spacing
      )
    temp$tx_atendimento <- 0
    return(rbind(thisState , temp))
  }) %>%
  bind_rows %>%
  filter(!is.na(UF)) %>%
  droplevels

# summary values
sumVal <-
  toPlot %>%
  group_by(UF) %>%
  summarise(sumVal = tx_atendimento_UF[1])

# Sort the states:
toPlot$UF <-
  factor(
    toPlot$UF
    , levels = as.character(sumVal$UF)[order(sumVal$sumVal)]
  )


toPlot %>% 
  ggplot(aes(x=popNb,y=tx_atendimento)) +
  geom_rect(aes(xmin=pop1b,xmax=popNb,
                ymin=tx0,ymax=tx_atendimento
                )) +
  geom_segment(aes(x=pop1b,xend=popNb,
                   y=tx_atendimento_UF,yend=tx_atendimento_UF)) +
  ggtitle('Daycare provision rate per state and municipality in Brazil (2014)') +
  # facet_wrap(~UF, scales = "free_x", nrow = 1, switch = "x") +
  facet_grid(~UF, scales = "free_x", switch = "x", space = "free") +
  theme_minimal() +
  theme(axis.text.x = element_blank()
        , panel.margin.x = unit(0,"in"))

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM