Shift legend into empty facets of a faceted plot in ggplot2

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












21















Consider the following plot:



library(ggplot2)

p <- ggplot(diamonds,
aes(x = carat, fill = cut)) +
geom_density(position = "stack") +
facet_wrap(~ color)


annotated facet_wrap plot



The facet_wrap function wraps a sequence of faceted panels into a roughly rectangular display of nrow rows and ncol columns. However, depending on the data, the actual number of panels is often a few panels short of nrow * ncol, which leaves a chunk of wasted space in the plot.



If the plot includes legend(s), the situation is exacerbated, because now we have even more wasted space due to the legend, whether it's on the right (default legend position), or one of the other three directions.



To save space, I would like to shift the legend(s) into the space created by unfilled facets.



The following works as a space-saving measure, but the legend is anchored to a corner of the plot area, with potentially a lot of space left on one side, creating an imbalanced look:



p +
theme(legend.position = c(1, 0),
legend.justification = c(1, 0))


legend anchored to a corner



Shifting a legend towards the centre of the blank space area by manually adjusting the legend.position/legend.justification values is a matter of trial and error, and difficult to scale if one has many faceted plots to work on.



In summary, I want a method that:




  1. Shifts the legend(s) of a faceted plot into the space created due to empty facets.

  2. Results in a reasonably nice-looking plot.

  3. Is easily automated to handle many plots.

This is a recurring use case for me, and I've decided to post it along with my working solution here in case anyone else finds it useful. I haven't seen this scenario asked/answered elsewhere on Stack Overflow. If anyone has, please leave a comment and I'll be happy to answer there instead or have this marked as a duplicate, as the case may be.










share|improve this question




























    21















    Consider the following plot:



    library(ggplot2)

    p <- ggplot(diamonds,
    aes(x = carat, fill = cut)) +
    geom_density(position = "stack") +
    facet_wrap(~ color)


    annotated facet_wrap plot



    The facet_wrap function wraps a sequence of faceted panels into a roughly rectangular display of nrow rows and ncol columns. However, depending on the data, the actual number of panels is often a few panels short of nrow * ncol, which leaves a chunk of wasted space in the plot.



    If the plot includes legend(s), the situation is exacerbated, because now we have even more wasted space due to the legend, whether it's on the right (default legend position), or one of the other three directions.



    To save space, I would like to shift the legend(s) into the space created by unfilled facets.



    The following works as a space-saving measure, but the legend is anchored to a corner of the plot area, with potentially a lot of space left on one side, creating an imbalanced look:



    p +
    theme(legend.position = c(1, 0),
    legend.justification = c(1, 0))


    legend anchored to a corner



    Shifting a legend towards the centre of the blank space area by manually adjusting the legend.position/legend.justification values is a matter of trial and error, and difficult to scale if one has many faceted plots to work on.



    In summary, I want a method that:




    1. Shifts the legend(s) of a faceted plot into the space created due to empty facets.

    2. Results in a reasonably nice-looking plot.

    3. Is easily automated to handle many plots.

    This is a recurring use case for me, and I've decided to post it along with my working solution here in case anyone else finds it useful. I haven't seen this scenario asked/answered elsewhere on Stack Overflow. If anyone has, please leave a comment and I'll be happy to answer there instead or have this marked as a duplicate, as the case may be.










    share|improve this question


























      21












      21








      21


      6






      Consider the following plot:



      library(ggplot2)

      p <- ggplot(diamonds,
      aes(x = carat, fill = cut)) +
      geom_density(position = "stack") +
      facet_wrap(~ color)


      annotated facet_wrap plot



      The facet_wrap function wraps a sequence of faceted panels into a roughly rectangular display of nrow rows and ncol columns. However, depending on the data, the actual number of panels is often a few panels short of nrow * ncol, which leaves a chunk of wasted space in the plot.



      If the plot includes legend(s), the situation is exacerbated, because now we have even more wasted space due to the legend, whether it's on the right (default legend position), or one of the other three directions.



      To save space, I would like to shift the legend(s) into the space created by unfilled facets.



      The following works as a space-saving measure, but the legend is anchored to a corner of the plot area, with potentially a lot of space left on one side, creating an imbalanced look:



      p +
      theme(legend.position = c(1, 0),
      legend.justification = c(1, 0))


      legend anchored to a corner



      Shifting a legend towards the centre of the blank space area by manually adjusting the legend.position/legend.justification values is a matter of trial and error, and difficult to scale if one has many faceted plots to work on.



      In summary, I want a method that:




      1. Shifts the legend(s) of a faceted plot into the space created due to empty facets.

      2. Results in a reasonably nice-looking plot.

      3. Is easily automated to handle many plots.

      This is a recurring use case for me, and I've decided to post it along with my working solution here in case anyone else finds it useful. I haven't seen this scenario asked/answered elsewhere on Stack Overflow. If anyone has, please leave a comment and I'll be happy to answer there instead or have this marked as a duplicate, as the case may be.










      share|improve this question
















      Consider the following plot:



      library(ggplot2)

      p <- ggplot(diamonds,
      aes(x = carat, fill = cut)) +
      geom_density(position = "stack") +
      facet_wrap(~ color)


      annotated facet_wrap plot



      The facet_wrap function wraps a sequence of faceted panels into a roughly rectangular display of nrow rows and ncol columns. However, depending on the data, the actual number of panels is often a few panels short of nrow * ncol, which leaves a chunk of wasted space in the plot.



      If the plot includes legend(s), the situation is exacerbated, because now we have even more wasted space due to the legend, whether it's on the right (default legend position), or one of the other three directions.



      To save space, I would like to shift the legend(s) into the space created by unfilled facets.



      The following works as a space-saving measure, but the legend is anchored to a corner of the plot area, with potentially a lot of space left on one side, creating an imbalanced look:



      p +
      theme(legend.position = c(1, 0),
      legend.justification = c(1, 0))


      legend anchored to a corner



      Shifting a legend towards the centre of the blank space area by manually adjusting the legend.position/legend.justification values is a matter of trial and error, and difficult to scale if one has many faceted plots to work on.



      In summary, I want a method that:




      1. Shifts the legend(s) of a faceted plot into the space created due to empty facets.

      2. Results in a reasonably nice-looking plot.

      3. Is easily automated to handle many plots.

      This is a recurring use case for me, and I've decided to post it along with my working solution here in case anyone else finds it useful. I haven't seen this scenario asked/answered elsewhere on Stack Overflow. If anyone has, please leave a comment and I'll be happy to answer there instead or have this marked as a duplicate, as the case may be.







      r ggplot2






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 30 at 20:01









      double-beep

      2,39641026




      2,39641026










      asked Jan 30 at 10:34









      Z.LinZ.Lin

      11.6k21835




      11.6k21835






















          2 Answers
          2






          active

          oldest

          votes


















          23














          The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.



          Essentially, I wrote a function that takes a ggplot/grob object converted by ggplotGrob(), converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.



          Function:



          library(gtable)
          library(cowplot)

          shift_legend <- function(p)

          # check if p is a valid object
          if(!"gtable" %in% class(p))
          if("ggplot" %in% class(p))
          gp <- ggplotGrob(p) # convert to grob
          else
          message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
          return(p)

          else
          gp <- p


          # check for unfilled facet panels
          facet.panels <- grep("^panel", gp[["layout"]][["name"]])
          empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
          empty.facet.panels <- facet.panels[empty.facet.panels]
          if(length(empty.facet.panels) == 0)
          message("There are no unfilled facet panels to shift legend into. Returning original plot.")
          return(p)


          # establish extent of unfilled facet panels (including any axis cells in between)
          empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
          empty.facet.panels <- list(min(empty.facet.panels[["t"]]), min(empty.facet.panels[["l"]]),
          max(empty.facet.panels[["b"]]), max(empty.facet.panels[["r"]]))
          names(empty.facet.panels) <- c("t", "l", "b", "r")

          # extract legend & copy over to location of unfilled facet panels
          guide.grob <- which(gp[["layout"]][["name"]] == "guide-box")
          if(length(guide.grob) == 0)
          message("There is no legend present. Returning original plot.")
          return(p)

          gp <- gtable_add_grob(x = gp,
          grobs = gp[["grobs"]][[guide.grob]],
          t = empty.facet.panels[["t"]],
          l = empty.facet.panels[["l"]],
          b = empty.facet.panels[["b"]],
          r = empty.facet.panels[["r"]],
          name = "new-guide-box")

          # squash the original guide box's row / column (whichever applicable)
          # & empty its cell
          guide.grob <- gp[["layout"]][guide.grob, ]
          if(guide.grob[["l"]] == guide.grob[["r"]])
          gp <- gtable_squash_cols(gp, cols = guide.grob[["l"]])

          if(guide.grob[["t"]] == guide.grob[["b"]])
          gp <- gtable_squash_rows(gp, rows = guide.grob[["t"]])

          gp <- gtable_remove_grobs(gp, "guide-box")

          return(gp)



          Result:



          library(grid)

          grid.draw(shift_legend(p))


          vertical legend result for p



          Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:



          p.new <- p +
          guides(fill = guide_legend(title.position = "top",
          label.position = "bottom",
          nrow = 1)) +
          theme(legend.direction = "horizontal")
          grid.draw(shift_legend(p.new))


          horizontal legend result for p.new



          Some other examples:



          # example 1: 1 empty panel, 1 vertical legend
          p1 <- ggplot(economics_long,
          aes(date, value, color = variable)) +
          geom_line() +
          facet_wrap(~ variable,
          scales = "free_y", nrow = 2,
          strip.position = "bottom") +
          theme(strip.background = element_blank(),
          strip.placement = "outside")
          grid.draw(shift_legend(p1))

          # example 2: 2 empty panels (vertically aligned) & 2 vertical legends side by side
          p2 <- ggplot(mpg,
          aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
          geom_point(size = 3) +
          facet_wrap(~ class, dir = "v") +
          theme(legend.box = "horizontal")
          grid.draw(shift_legend(p2))

          # example 3: facets in polar coordinates
          p3 <- ggplot(mtcars,
          aes(x = factor(1), fill = factor(cyl))) +
          geom_bar(width = 1, position = "fill") +
          facet_wrap(~ gear, nrow = 2) +
          coord_polar(theta = "y") +
          theme_void()
          grid.draw(shift_legend(p3))


          more illustrations






          share|improve this answer




















          • 1





            Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

            – Konrad Rudolph
            Jan 30 at 17:02


















          8














          Nice Q&A!



          I found something similar at this link. So, I thought that it would have been a nice addition to your function.



          More precisely the function reposition_legend() from lemon seems to be quite what you needed, except that it doesn't look for the empty spaces.



          I took inspiration from your function to find the names of the empty panels that are passed to reposition_legend() with the panel arg.



          Example data and libraries:



          library(ggplot2)
          library(gtable)
          library(lemon)

          p <- ggplot(diamonds,
          aes(x = carat, fill = cut)) +
          geom_density(position = "stack") +
          facet_wrap(~ color) +
          theme(legend.direction = "horizontal")


          Of course, I removed all the checks (if cases, which should be the same) just to concentrate on the important stuff.



          shift_legend2 <- function(p) 
          # ...
          # to grob
          gp <- ggplotGrob(p)
          facet.panels <- grep("^panel", gp[["layout"]][["name"]])
          empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
          empty.facet.panels <- facet.panels[empty.facet.panels]

          # establish name of empty panels
          empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
          names <- empty.facet.panels$name
          # example of names:
          #[1] "panel-3-2" "panel-3-3"

          # now we just need a simple call to reposition the legend
          reposition_legend(p, 'center', panel=names)


          shift_legend2(p)


          enter image description here



          Note that this might still need some tweaking, I just thought it was something worth to be shared.



          At the moment the behaviour seems OK, and the function is a few lines shorter.




          Other cases.



          First example:



          p1 <- ggplot(economics_long, 
          aes(date, value, color = variable)) +
          geom_line() +
          facet_wrap(~ variable,
          scales = "free_y", nrow = 2,
          strip.position = "bottom") +
          theme(strip.background = element_blank(),
          strip.placement = "outside")

          shift_legend2(p1)


          enter image description here



          Second example:



          p2 <- ggplot(mpg,
          aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
          geom_point(size = 3) +
          facet_wrap(~ class, dir = "v") +
          theme(legend.box = "horizontal")

          #[1] "panel-2-3" "panel-3-3" are the names of empty panels in this case
          shift_legend2(p2)


          enter image description here



          Third example:



          p3 <- ggplot(mtcars, 
          aes(x = factor(1), fill = factor(cyl))) +
          geom_bar(width = 1, position = "fill") +
          facet_wrap(~ gear, nrow = 2) +
          coord_polar(theta = "y") +
          theme_void()
          shift_legend2(p3)


          enter image description here




          Complete function:



          shift_legend2 <- function(p) 
          # check if p is a valid object
          if(!(inherits(p, "gtable")))
          if(inherits(p, "ggplot"))
          gp <- ggplotGrob(p) # convert to grob
          else
          message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
          return(p)

          else
          gp <- p


          # check for unfilled facet panels
          facet.panels <- grep("^panel", gp[["layout"]][["name"]])
          empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]),
          USE.NAMES = F)
          empty.facet.panels <- facet.panels[empty.facet.panels]

          if(length(empty.facet.panels) == 0)
          message("There are no unfilled facet panels to shift legend into. Returning original plot.")
          return(p)


          # establish name of empty panels
          empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
          names <- empty.facet.panels$name

          # return repositioned legend
          reposition_legend(p, 'center', panel=names)






          share|improve this answer
























            Your Answer






            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54438495%2fshift-legend-into-empty-facets-of-a-faceted-plot-in-ggplot2%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            23














            The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.



            Essentially, I wrote a function that takes a ggplot/grob object converted by ggplotGrob(), converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.



            Function:



            library(gtable)
            library(cowplot)

            shift_legend <- function(p)

            # check if p is a valid object
            if(!"gtable" %in% class(p))
            if("ggplot" %in% class(p))
            gp <- ggplotGrob(p) # convert to grob
            else
            message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
            return(p)

            else
            gp <- p


            # check for unfilled facet panels
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
            empty.facet.panels <- facet.panels[empty.facet.panels]
            if(length(empty.facet.panels) == 0)
            message("There are no unfilled facet panels to shift legend into. Returning original plot.")
            return(p)


            # establish extent of unfilled facet panels (including any axis cells in between)
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            empty.facet.panels <- list(min(empty.facet.panels[["t"]]), min(empty.facet.panels[["l"]]),
            max(empty.facet.panels[["b"]]), max(empty.facet.panels[["r"]]))
            names(empty.facet.panels) <- c("t", "l", "b", "r")

            # extract legend & copy over to location of unfilled facet panels
            guide.grob <- which(gp[["layout"]][["name"]] == "guide-box")
            if(length(guide.grob) == 0)
            message("There is no legend present. Returning original plot.")
            return(p)

            gp <- gtable_add_grob(x = gp,
            grobs = gp[["grobs"]][[guide.grob]],
            t = empty.facet.panels[["t"]],
            l = empty.facet.panels[["l"]],
            b = empty.facet.panels[["b"]],
            r = empty.facet.panels[["r"]],
            name = "new-guide-box")

            # squash the original guide box's row / column (whichever applicable)
            # & empty its cell
            guide.grob <- gp[["layout"]][guide.grob, ]
            if(guide.grob[["l"]] == guide.grob[["r"]])
            gp <- gtable_squash_cols(gp, cols = guide.grob[["l"]])

            if(guide.grob[["t"]] == guide.grob[["b"]])
            gp <- gtable_squash_rows(gp, rows = guide.grob[["t"]])

            gp <- gtable_remove_grobs(gp, "guide-box")

            return(gp)



            Result:



            library(grid)

            grid.draw(shift_legend(p))


            vertical legend result for p



            Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:



            p.new <- p +
            guides(fill = guide_legend(title.position = "top",
            label.position = "bottom",
            nrow = 1)) +
            theme(legend.direction = "horizontal")
            grid.draw(shift_legend(p.new))


            horizontal legend result for p.new



            Some other examples:



            # example 1: 1 empty panel, 1 vertical legend
            p1 <- ggplot(economics_long,
            aes(date, value, color = variable)) +
            geom_line() +
            facet_wrap(~ variable,
            scales = "free_y", nrow = 2,
            strip.position = "bottom") +
            theme(strip.background = element_blank(),
            strip.placement = "outside")
            grid.draw(shift_legend(p1))

            # example 2: 2 empty panels (vertically aligned) & 2 vertical legends side by side
            p2 <- ggplot(mpg,
            aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
            geom_point(size = 3) +
            facet_wrap(~ class, dir = "v") +
            theme(legend.box = "horizontal")
            grid.draw(shift_legend(p2))

            # example 3: facets in polar coordinates
            p3 <- ggplot(mtcars,
            aes(x = factor(1), fill = factor(cyl))) +
            geom_bar(width = 1, position = "fill") +
            facet_wrap(~ gear, nrow = 2) +
            coord_polar(theta = "y") +
            theme_void()
            grid.draw(shift_legend(p3))


            more illustrations






            share|improve this answer




















            • 1





              Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

              – Konrad Rudolph
              Jan 30 at 17:02















            23














            The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.



            Essentially, I wrote a function that takes a ggplot/grob object converted by ggplotGrob(), converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.



            Function:



            library(gtable)
            library(cowplot)

            shift_legend <- function(p)

            # check if p is a valid object
            if(!"gtable" %in% class(p))
            if("ggplot" %in% class(p))
            gp <- ggplotGrob(p) # convert to grob
            else
            message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
            return(p)

            else
            gp <- p


            # check for unfilled facet panels
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
            empty.facet.panels <- facet.panels[empty.facet.panels]
            if(length(empty.facet.panels) == 0)
            message("There are no unfilled facet panels to shift legend into. Returning original plot.")
            return(p)


            # establish extent of unfilled facet panels (including any axis cells in between)
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            empty.facet.panels <- list(min(empty.facet.panels[["t"]]), min(empty.facet.panels[["l"]]),
            max(empty.facet.panels[["b"]]), max(empty.facet.panels[["r"]]))
            names(empty.facet.panels) <- c("t", "l", "b", "r")

            # extract legend & copy over to location of unfilled facet panels
            guide.grob <- which(gp[["layout"]][["name"]] == "guide-box")
            if(length(guide.grob) == 0)
            message("There is no legend present. Returning original plot.")
            return(p)

            gp <- gtable_add_grob(x = gp,
            grobs = gp[["grobs"]][[guide.grob]],
            t = empty.facet.panels[["t"]],
            l = empty.facet.panels[["l"]],
            b = empty.facet.panels[["b"]],
            r = empty.facet.panels[["r"]],
            name = "new-guide-box")

            # squash the original guide box's row / column (whichever applicable)
            # & empty its cell
            guide.grob <- gp[["layout"]][guide.grob, ]
            if(guide.grob[["l"]] == guide.grob[["r"]])
            gp <- gtable_squash_cols(gp, cols = guide.grob[["l"]])

            if(guide.grob[["t"]] == guide.grob[["b"]])
            gp <- gtable_squash_rows(gp, rows = guide.grob[["t"]])

            gp <- gtable_remove_grobs(gp, "guide-box")

            return(gp)



            Result:



            library(grid)

            grid.draw(shift_legend(p))


            vertical legend result for p



            Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:



            p.new <- p +
            guides(fill = guide_legend(title.position = "top",
            label.position = "bottom",
            nrow = 1)) +
            theme(legend.direction = "horizontal")
            grid.draw(shift_legend(p.new))


            horizontal legend result for p.new



            Some other examples:



            # example 1: 1 empty panel, 1 vertical legend
            p1 <- ggplot(economics_long,
            aes(date, value, color = variable)) +
            geom_line() +
            facet_wrap(~ variable,
            scales = "free_y", nrow = 2,
            strip.position = "bottom") +
            theme(strip.background = element_blank(),
            strip.placement = "outside")
            grid.draw(shift_legend(p1))

            # example 2: 2 empty panels (vertically aligned) & 2 vertical legends side by side
            p2 <- ggplot(mpg,
            aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
            geom_point(size = 3) +
            facet_wrap(~ class, dir = "v") +
            theme(legend.box = "horizontal")
            grid.draw(shift_legend(p2))

            # example 3: facets in polar coordinates
            p3 <- ggplot(mtcars,
            aes(x = factor(1), fill = factor(cyl))) +
            geom_bar(width = 1, position = "fill") +
            facet_wrap(~ gear, nrow = 2) +
            coord_polar(theta = "y") +
            theme_void()
            grid.draw(shift_legend(p3))


            more illustrations






            share|improve this answer




















            • 1





              Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

              – Konrad Rudolph
              Jan 30 at 17:02













            23












            23








            23







            The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.



            Essentially, I wrote a function that takes a ggplot/grob object converted by ggplotGrob(), converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.



            Function:



            library(gtable)
            library(cowplot)

            shift_legend <- function(p)

            # check if p is a valid object
            if(!"gtable" %in% class(p))
            if("ggplot" %in% class(p))
            gp <- ggplotGrob(p) # convert to grob
            else
            message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
            return(p)

            else
            gp <- p


            # check for unfilled facet panels
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
            empty.facet.panels <- facet.panels[empty.facet.panels]
            if(length(empty.facet.panels) == 0)
            message("There are no unfilled facet panels to shift legend into. Returning original plot.")
            return(p)


            # establish extent of unfilled facet panels (including any axis cells in between)
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            empty.facet.panels <- list(min(empty.facet.panels[["t"]]), min(empty.facet.panels[["l"]]),
            max(empty.facet.panels[["b"]]), max(empty.facet.panels[["r"]]))
            names(empty.facet.panels) <- c("t", "l", "b", "r")

            # extract legend & copy over to location of unfilled facet panels
            guide.grob <- which(gp[["layout"]][["name"]] == "guide-box")
            if(length(guide.grob) == 0)
            message("There is no legend present. Returning original plot.")
            return(p)

            gp <- gtable_add_grob(x = gp,
            grobs = gp[["grobs"]][[guide.grob]],
            t = empty.facet.panels[["t"]],
            l = empty.facet.panels[["l"]],
            b = empty.facet.panels[["b"]],
            r = empty.facet.panels[["r"]],
            name = "new-guide-box")

            # squash the original guide box's row / column (whichever applicable)
            # & empty its cell
            guide.grob <- gp[["layout"]][guide.grob, ]
            if(guide.grob[["l"]] == guide.grob[["r"]])
            gp <- gtable_squash_cols(gp, cols = guide.grob[["l"]])

            if(guide.grob[["t"]] == guide.grob[["b"]])
            gp <- gtable_squash_rows(gp, rows = guide.grob[["t"]])

            gp <- gtable_remove_grobs(gp, "guide-box")

            return(gp)



            Result:



            library(grid)

            grid.draw(shift_legend(p))


            vertical legend result for p



            Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:



            p.new <- p +
            guides(fill = guide_legend(title.position = "top",
            label.position = "bottom",
            nrow = 1)) +
            theme(legend.direction = "horizontal")
            grid.draw(shift_legend(p.new))


            horizontal legend result for p.new



            Some other examples:



            # example 1: 1 empty panel, 1 vertical legend
            p1 <- ggplot(economics_long,
            aes(date, value, color = variable)) +
            geom_line() +
            facet_wrap(~ variable,
            scales = "free_y", nrow = 2,
            strip.position = "bottom") +
            theme(strip.background = element_blank(),
            strip.placement = "outside")
            grid.draw(shift_legend(p1))

            # example 2: 2 empty panels (vertically aligned) & 2 vertical legends side by side
            p2 <- ggplot(mpg,
            aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
            geom_point(size = 3) +
            facet_wrap(~ class, dir = "v") +
            theme(legend.box = "horizontal")
            grid.draw(shift_legend(p2))

            # example 3: facets in polar coordinates
            p3 <- ggplot(mtcars,
            aes(x = factor(1), fill = factor(cyl))) +
            geom_bar(width = 1, position = "fill") +
            facet_wrap(~ gear, nrow = 2) +
            coord_polar(theta = "y") +
            theme_void()
            grid.draw(shift_legend(p3))


            more illustrations






            share|improve this answer















            The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.



            Essentially, I wrote a function that takes a ggplot/grob object converted by ggplotGrob(), converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.



            Function:



            library(gtable)
            library(cowplot)

            shift_legend <- function(p)

            # check if p is a valid object
            if(!"gtable" %in% class(p))
            if("ggplot" %in% class(p))
            gp <- ggplotGrob(p) # convert to grob
            else
            message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
            return(p)

            else
            gp <- p


            # check for unfilled facet panels
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
            empty.facet.panels <- facet.panels[empty.facet.panels]
            if(length(empty.facet.panels) == 0)
            message("There are no unfilled facet panels to shift legend into. Returning original plot.")
            return(p)


            # establish extent of unfilled facet panels (including any axis cells in between)
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            empty.facet.panels <- list(min(empty.facet.panels[["t"]]), min(empty.facet.panels[["l"]]),
            max(empty.facet.panels[["b"]]), max(empty.facet.panels[["r"]]))
            names(empty.facet.panels) <- c("t", "l", "b", "r")

            # extract legend & copy over to location of unfilled facet panels
            guide.grob <- which(gp[["layout"]][["name"]] == "guide-box")
            if(length(guide.grob) == 0)
            message("There is no legend present. Returning original plot.")
            return(p)

            gp <- gtable_add_grob(x = gp,
            grobs = gp[["grobs"]][[guide.grob]],
            t = empty.facet.panels[["t"]],
            l = empty.facet.panels[["l"]],
            b = empty.facet.panels[["b"]],
            r = empty.facet.panels[["r"]],
            name = "new-guide-box")

            # squash the original guide box's row / column (whichever applicable)
            # & empty its cell
            guide.grob <- gp[["layout"]][guide.grob, ]
            if(guide.grob[["l"]] == guide.grob[["r"]])
            gp <- gtable_squash_cols(gp, cols = guide.grob[["l"]])

            if(guide.grob[["t"]] == guide.grob[["b"]])
            gp <- gtable_squash_rows(gp, rows = guide.grob[["t"]])

            gp <- gtable_remove_grobs(gp, "guide-box")

            return(gp)



            Result:



            library(grid)

            grid.draw(shift_legend(p))


            vertical legend result for p



            Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:



            p.new <- p +
            guides(fill = guide_legend(title.position = "top",
            label.position = "bottom",
            nrow = 1)) +
            theme(legend.direction = "horizontal")
            grid.draw(shift_legend(p.new))


            horizontal legend result for p.new



            Some other examples:



            # example 1: 1 empty panel, 1 vertical legend
            p1 <- ggplot(economics_long,
            aes(date, value, color = variable)) +
            geom_line() +
            facet_wrap(~ variable,
            scales = "free_y", nrow = 2,
            strip.position = "bottom") +
            theme(strip.background = element_blank(),
            strip.placement = "outside")
            grid.draw(shift_legend(p1))

            # example 2: 2 empty panels (vertically aligned) & 2 vertical legends side by side
            p2 <- ggplot(mpg,
            aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
            geom_point(size = 3) +
            facet_wrap(~ class, dir = "v") +
            theme(legend.box = "horizontal")
            grid.draw(shift_legend(p2))

            # example 3: facets in polar coordinates
            p3 <- ggplot(mtcars,
            aes(x = factor(1), fill = factor(cyl))) +
            geom_bar(width = 1, position = "fill") +
            facet_wrap(~ gear, nrow = 2) +
            coord_polar(theta = "y") +
            theme_void()
            grid.draw(shift_legend(p3))


            more illustrations







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jan 30 at 20:05









            double-beep

            2,39641026




            2,39641026










            answered Jan 30 at 10:34









            Z.LinZ.Lin

            11.6k21835




            11.6k21835







            • 1





              Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

              – Konrad Rudolph
              Jan 30 at 17:02












            • 1





              Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

              – Konrad Rudolph
              Jan 30 at 17:02







            1




            1





            Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

            – Konrad Rudolph
            Jan 30 at 17:02





            Very nice solution! Your inheritance tests (!"gtable" %in% class(p) etc) should be written via inherits: if (! inherits(p, 'gtable')). And since you do different things depending on the object class, consider using S3 methods instead of if statements.

            – Konrad Rudolph
            Jan 30 at 17:02













            8














            Nice Q&A!



            I found something similar at this link. So, I thought that it would have been a nice addition to your function.



            More precisely the function reposition_legend() from lemon seems to be quite what you needed, except that it doesn't look for the empty spaces.



            I took inspiration from your function to find the names of the empty panels that are passed to reposition_legend() with the panel arg.



            Example data and libraries:



            library(ggplot2)
            library(gtable)
            library(lemon)

            p <- ggplot(diamonds,
            aes(x = carat, fill = cut)) +
            geom_density(position = "stack") +
            facet_wrap(~ color) +
            theme(legend.direction = "horizontal")


            Of course, I removed all the checks (if cases, which should be the same) just to concentrate on the important stuff.



            shift_legend2 <- function(p) 
            # ...
            # to grob
            gp <- ggplotGrob(p)
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
            empty.facet.panels <- facet.panels[empty.facet.panels]

            # establish name of empty panels
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            names <- empty.facet.panels$name
            # example of names:
            #[1] "panel-3-2" "panel-3-3"

            # now we just need a simple call to reposition the legend
            reposition_legend(p, 'center', panel=names)


            shift_legend2(p)


            enter image description here



            Note that this might still need some tweaking, I just thought it was something worth to be shared.



            At the moment the behaviour seems OK, and the function is a few lines shorter.




            Other cases.



            First example:



            p1 <- ggplot(economics_long, 
            aes(date, value, color = variable)) +
            geom_line() +
            facet_wrap(~ variable,
            scales = "free_y", nrow = 2,
            strip.position = "bottom") +
            theme(strip.background = element_blank(),
            strip.placement = "outside")

            shift_legend2(p1)


            enter image description here



            Second example:



            p2 <- ggplot(mpg,
            aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
            geom_point(size = 3) +
            facet_wrap(~ class, dir = "v") +
            theme(legend.box = "horizontal")

            #[1] "panel-2-3" "panel-3-3" are the names of empty panels in this case
            shift_legend2(p2)


            enter image description here



            Third example:



            p3 <- ggplot(mtcars, 
            aes(x = factor(1), fill = factor(cyl))) +
            geom_bar(width = 1, position = "fill") +
            facet_wrap(~ gear, nrow = 2) +
            coord_polar(theta = "y") +
            theme_void()
            shift_legend2(p3)


            enter image description here




            Complete function:



            shift_legend2 <- function(p) 
            # check if p is a valid object
            if(!(inherits(p, "gtable")))
            if(inherits(p, "ggplot"))
            gp <- ggplotGrob(p) # convert to grob
            else
            message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
            return(p)

            else
            gp <- p


            # check for unfilled facet panels
            facet.panels <- grep("^panel", gp[["layout"]][["name"]])
            empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]),
            USE.NAMES = F)
            empty.facet.panels <- facet.panels[empty.facet.panels]

            if(length(empty.facet.panels) == 0)
            message("There are no unfilled facet panels to shift legend into. Returning original plot.")
            return(p)


            # establish name of empty panels
            empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
            names <- empty.facet.panels$name

            # return repositioned legend
            reposition_legend(p, 'center', panel=names)






            share|improve this answer





























              8














              Nice Q&A!



              I found something similar at this link. So, I thought that it would have been a nice addition to your function.



              More precisely the function reposition_legend() from lemon seems to be quite what you needed, except that it doesn't look for the empty spaces.



              I took inspiration from your function to find the names of the empty panels that are passed to reposition_legend() with the panel arg.



              Example data and libraries:



              library(ggplot2)
              library(gtable)
              library(lemon)

              p <- ggplot(diamonds,
              aes(x = carat, fill = cut)) +
              geom_density(position = "stack") +
              facet_wrap(~ color) +
              theme(legend.direction = "horizontal")


              Of course, I removed all the checks (if cases, which should be the same) just to concentrate on the important stuff.



              shift_legend2 <- function(p) 
              # ...
              # to grob
              gp <- ggplotGrob(p)
              facet.panels <- grep("^panel", gp[["layout"]][["name"]])
              empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
              empty.facet.panels <- facet.panels[empty.facet.panels]

              # establish name of empty panels
              empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
              names <- empty.facet.panels$name
              # example of names:
              #[1] "panel-3-2" "panel-3-3"

              # now we just need a simple call to reposition the legend
              reposition_legend(p, 'center', panel=names)


              shift_legend2(p)


              enter image description here



              Note that this might still need some tweaking, I just thought it was something worth to be shared.



              At the moment the behaviour seems OK, and the function is a few lines shorter.




              Other cases.



              First example:



              p1 <- ggplot(economics_long, 
              aes(date, value, color = variable)) +
              geom_line() +
              facet_wrap(~ variable,
              scales = "free_y", nrow = 2,
              strip.position = "bottom") +
              theme(strip.background = element_blank(),
              strip.placement = "outside")

              shift_legend2(p1)


              enter image description here



              Second example:



              p2 <- ggplot(mpg,
              aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
              geom_point(size = 3) +
              facet_wrap(~ class, dir = "v") +
              theme(legend.box = "horizontal")

              #[1] "panel-2-3" "panel-3-3" are the names of empty panels in this case
              shift_legend2(p2)


              enter image description here



              Third example:



              p3 <- ggplot(mtcars, 
              aes(x = factor(1), fill = factor(cyl))) +
              geom_bar(width = 1, position = "fill") +
              facet_wrap(~ gear, nrow = 2) +
              coord_polar(theta = "y") +
              theme_void()
              shift_legend2(p3)


              enter image description here




              Complete function:



              shift_legend2 <- function(p) 
              # check if p is a valid object
              if(!(inherits(p, "gtable")))
              if(inherits(p, "ggplot"))
              gp <- ggplotGrob(p) # convert to grob
              else
              message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
              return(p)

              else
              gp <- p


              # check for unfilled facet panels
              facet.panels <- grep("^panel", gp[["layout"]][["name"]])
              empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]),
              USE.NAMES = F)
              empty.facet.panels <- facet.panels[empty.facet.panels]

              if(length(empty.facet.panels) == 0)
              message("There are no unfilled facet panels to shift legend into. Returning original plot.")
              return(p)


              # establish name of empty panels
              empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
              names <- empty.facet.panels$name

              # return repositioned legend
              reposition_legend(p, 'center', panel=names)






              share|improve this answer



























                8












                8








                8







                Nice Q&A!



                I found something similar at this link. So, I thought that it would have been a nice addition to your function.



                More precisely the function reposition_legend() from lemon seems to be quite what you needed, except that it doesn't look for the empty spaces.



                I took inspiration from your function to find the names of the empty panels that are passed to reposition_legend() with the panel arg.



                Example data and libraries:



                library(ggplot2)
                library(gtable)
                library(lemon)

                p <- ggplot(diamonds,
                aes(x = carat, fill = cut)) +
                geom_density(position = "stack") +
                facet_wrap(~ color) +
                theme(legend.direction = "horizontal")


                Of course, I removed all the checks (if cases, which should be the same) just to concentrate on the important stuff.



                shift_legend2 <- function(p) 
                # ...
                # to grob
                gp <- ggplotGrob(p)
                facet.panels <- grep("^panel", gp[["layout"]][["name"]])
                empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
                empty.facet.panels <- facet.panels[empty.facet.panels]

                # establish name of empty panels
                empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
                names <- empty.facet.panels$name
                # example of names:
                #[1] "panel-3-2" "panel-3-3"

                # now we just need a simple call to reposition the legend
                reposition_legend(p, 'center', panel=names)


                shift_legend2(p)


                enter image description here



                Note that this might still need some tweaking, I just thought it was something worth to be shared.



                At the moment the behaviour seems OK, and the function is a few lines shorter.




                Other cases.



                First example:



                p1 <- ggplot(economics_long, 
                aes(date, value, color = variable)) +
                geom_line() +
                facet_wrap(~ variable,
                scales = "free_y", nrow = 2,
                strip.position = "bottom") +
                theme(strip.background = element_blank(),
                strip.placement = "outside")

                shift_legend2(p1)


                enter image description here



                Second example:



                p2 <- ggplot(mpg,
                aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
                geom_point(size = 3) +
                facet_wrap(~ class, dir = "v") +
                theme(legend.box = "horizontal")

                #[1] "panel-2-3" "panel-3-3" are the names of empty panels in this case
                shift_legend2(p2)


                enter image description here



                Third example:



                p3 <- ggplot(mtcars, 
                aes(x = factor(1), fill = factor(cyl))) +
                geom_bar(width = 1, position = "fill") +
                facet_wrap(~ gear, nrow = 2) +
                coord_polar(theta = "y") +
                theme_void()
                shift_legend2(p3)


                enter image description here




                Complete function:



                shift_legend2 <- function(p) 
                # check if p is a valid object
                if(!(inherits(p, "gtable")))
                if(inherits(p, "ggplot"))
                gp <- ggplotGrob(p) # convert to grob
                else
                message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
                return(p)

                else
                gp <- p


                # check for unfilled facet panels
                facet.panels <- grep("^panel", gp[["layout"]][["name"]])
                empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]),
                USE.NAMES = F)
                empty.facet.panels <- facet.panels[empty.facet.panels]

                if(length(empty.facet.panels) == 0)
                message("There are no unfilled facet panels to shift legend into. Returning original plot.")
                return(p)


                # establish name of empty panels
                empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
                names <- empty.facet.panels$name

                # return repositioned legend
                reposition_legend(p, 'center', panel=names)






                share|improve this answer















                Nice Q&A!



                I found something similar at this link. So, I thought that it would have been a nice addition to your function.



                More precisely the function reposition_legend() from lemon seems to be quite what you needed, except that it doesn't look for the empty spaces.



                I took inspiration from your function to find the names of the empty panels that are passed to reposition_legend() with the panel arg.



                Example data and libraries:



                library(ggplot2)
                library(gtable)
                library(lemon)

                p <- ggplot(diamonds,
                aes(x = carat, fill = cut)) +
                geom_density(position = "stack") +
                facet_wrap(~ color) +
                theme(legend.direction = "horizontal")


                Of course, I removed all the checks (if cases, which should be the same) just to concentrate on the important stuff.



                shift_legend2 <- function(p) 
                # ...
                # to grob
                gp <- ggplotGrob(p)
                facet.panels <- grep("^panel", gp[["layout"]][["name"]])
                empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]))
                empty.facet.panels <- facet.panels[empty.facet.panels]

                # establish name of empty panels
                empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
                names <- empty.facet.panels$name
                # example of names:
                #[1] "panel-3-2" "panel-3-3"

                # now we just need a simple call to reposition the legend
                reposition_legend(p, 'center', panel=names)


                shift_legend2(p)


                enter image description here



                Note that this might still need some tweaking, I just thought it was something worth to be shared.



                At the moment the behaviour seems OK, and the function is a few lines shorter.




                Other cases.



                First example:



                p1 <- ggplot(economics_long, 
                aes(date, value, color = variable)) +
                geom_line() +
                facet_wrap(~ variable,
                scales = "free_y", nrow = 2,
                strip.position = "bottom") +
                theme(strip.background = element_blank(),
                strip.placement = "outside")

                shift_legend2(p1)


                enter image description here



                Second example:



                p2 <- ggplot(mpg,
                aes(x = displ, y = hwy, color = fl, shape = factor(cyl))) +
                geom_point(size = 3) +
                facet_wrap(~ class, dir = "v") +
                theme(legend.box = "horizontal")

                #[1] "panel-2-3" "panel-3-3" are the names of empty panels in this case
                shift_legend2(p2)


                enter image description here



                Third example:



                p3 <- ggplot(mtcars, 
                aes(x = factor(1), fill = factor(cyl))) +
                geom_bar(width = 1, position = "fill") +
                facet_wrap(~ gear, nrow = 2) +
                coord_polar(theta = "y") +
                theme_void()
                shift_legend2(p3)


                enter image description here




                Complete function:



                shift_legend2 <- function(p) 
                # check if p is a valid object
                if(!(inherits(p, "gtable")))
                if(inherits(p, "ggplot"))
                gp <- ggplotGrob(p) # convert to grob
                else
                message("This is neither a ggplot object nor a grob generated from ggplotGrob. Returning original plot.")
                return(p)

                else
                gp <- p


                # check for unfilled facet panels
                facet.panels <- grep("^panel", gp[["layout"]][["name"]])
                empty.facet.panels <- sapply(facet.panels, function(i) "zeroGrob" %in% class(gp[["grobs"]][[i]]),
                USE.NAMES = F)
                empty.facet.panels <- facet.panels[empty.facet.panels]

                if(length(empty.facet.panels) == 0)
                message("There are no unfilled facet panels to shift legend into. Returning original plot.")
                return(p)


                # establish name of empty panels
                empty.facet.panels <- gp[["layout"]][empty.facet.panels, ]
                names <- empty.facet.panels$name

                # return repositioned legend
                reposition_legend(p, 'center', panel=names)







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Feb 1 at 8:31

























                answered Jan 30 at 15:24









                RLaveRLave

                4,70211124




                4,70211124



























                    draft saved

                    draft discarded
















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54438495%2fshift-legend-into-empty-facets-of-a-faceted-plot-in-ggplot2%23new-answer', 'question_page');

                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown






                    Popular posts from this blog

                    How to check contact read email or not when send email to Individual?

                    Displaying single band from multi-band raster using QGIS

                    How many registers does an x86_64 CPU actually have?