20

I'm trying to generate some fractals and have a question regarding the margins with ggplot in R. I'm using the following code to generate the fractals.

library(ggplot2)
library(grid)

max_iter=25
cl=colours()
step=seq(-2,0.8,by=0.005)
points=array(0,dim=c(length(step)^2,3))
t=0

for(a in step) {
  for(b in step+0.6) {
    x=0;y=0;n=0;dist=0
    while(n<max_iter & dist<4)  {
      n=n+1
      newx=a+x^2-y^2
      newy=b+2*x*y
      dist=newx^2+newy^2
      x=newx;y=newy
    }

    if(dist<4)  { 
      color=24 # black
    } else {
      color=n*floor(length(cl)/max_iter)
    }
    t=t+1
    points[t,]=c(a,b,color)
  }
}

df=as.data.frame(points)    

ggplot(data=df, aes(V1, V2, color=cl[V3]))+ 
  geom_point() + 
  theme(panel.background=element_blank(), 
       panel.grid.major=element_blank(), 
       panel.grid.minor=element_blank(), 
       panel.margin = unit(c(0, 0, 0, 0), "cm"),       
       axis.ticks=element_blank(), 
       axis.text.x=element_blank(), 
       axis.text.y=element_blank(), 
       axis.title.x=element_blank(), 
       axis.title.y=element_blank(),
       plot.background = element_rect(fill = "transparent",colour = NA),
       plot.margin = unit(c(0, 0, 0, 0), "cm"),
       legend.position = 'none')

last_plot() + scale_colour_manual(values=sort(c("#00000000", rainbow(35)), decreasing=FALSE))

ggsave('mandelbrot.png');
print('Image Saved.')

I'm looking for ideas to remove the margins surrounding the plot area. I've tried a whole bunch of tricks, such as setting parameters in the 'par', xaxes / yaxes, last_plot() + labs(x=NULL, y=NULL), etc., but nothing seems to work.

Does anybody have an idea to remove this intractable margin from the plot? I also contemplated setting a transparent background, but I'd have to cut out the margins - a step I'd like to avoid.

enter image description here

hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
jixtacom
  • 205
  • 1
  • 2
  • 7

5 Answers5

22

You can also use theme_nothing() from the cowplot package:

require(cowplot)
qplot(1:10, (1:10)^2, geom='line') + theme_nothing() + 
  scale_x_continuous(expand=c(0,0)) +
  scale_y_continuous(expand=c(0,0)) +
  labs(x = NULL, y = NULL)

Unfortunately, you still need to add labs(x = NULL, y = NULL), because there is no way in ggplot2's theme machinery to remove the axes completely. And you need to set expand=c(0,0) in the scale parameters to make sure the scale doesn't extend beyond your data range.

Result:

enter image description here

Claus Wilke
  • 16,992
  • 7
  • 53
  • 104
  • Thanks so much, this is exactly what I was looking for! Just for info, you can also use `ggplot2`'s own `theme_void()` if you want to avoid loading `cowplot` as well. – EcologyTom Sep 01 '23 at 13:21
14

After using your code, I see more clearly what you're looking for. This:

gg <- ggplot(data=df, aes(V1, V2, color=cl[V3]))
gg + 
  geom_point() +
  labs(x = NULL, y = NULL, title = NULL) +
  scale_x_continuous(expand = c(0, 0), limits = range(df$V1)) +
  scale_y_continuous(expand = c(0, 0), limits = range(df$V2)) +
  scale_colour_manual(values = sort(c("#00000000", rainbow(35)), decreasing = FALSE)) +
  theme(
    panel.background = element_rect(fill = "transparent", colour = NA),
    plot.background = element_rect(fill = "transparent", colour = NA),
    panel.grid = element_blank(),
    panel.border = element_blank(),
    plot.margin = unit(c(0, 0, 0, 0), "null"),
    panel.margin = unit(c(0, 0, 0, 0), "null"),
    axis.ticks = element_blank(),
    axis.text = element_blank(),
    axis.title = element_blank(),
    axis.line = element_blank(),
    legend.position = "none",
    axis.ticks.length = unit(0, "null"),
    axis.ticks.margin = unit(0, "null"),
    legend.margin = unit(0, "null")
  )

you have to remove the labels, not-expand the x & y axis and set hard limits. The nulls are also important.'

This can also be done by doing gb <- ggplotGrob(gg) and manually editing the grobs & parameters, but I think this probably gets you what you need.

tjebo
  • 21,977
  • 7
  • 58
  • 94
hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
  • Or, instead of all these theme statements, use `theme_nothing()` from the cowplot package. It's basically the same thing, though. – Claus Wilke Jul 06 '15 at 21:09
  • Teaching to fish…and I think the `"null"`s work better than `"lines"` if memory serves for completely getting rid of spacing, which can be validated at the grob-level if so (I can check later). – hrbrmstr Jul 06 '15 at 21:10
  • I'd definitely want to know if `null` is a better unit choice than `lines` in this application, so please let me know what you find. A priori, it seems `null` generates some unnecessary overhead, since it's a relative unit that can grow and shrink depending on the size of other elements in the plot. – Claus Wilke Jul 06 '15 at 23:57
  • obligatory deprecation: it's now ``panel.spacing`` rather than ``panel.margin`` – PatrickT Jan 25 '18 at 06:37
10

An approach that selects just the plot panel from the ggplot layout. It creates the ggplot, setting elements within the plot panel to element_blank, and no expansion of the x and y scales. It then creates the ggplot grob so that the plot panel only can be selected from the layout.

Minor edit: Updating to ggplot2 2.2.0

library(ggplot2)
library(grid)

max_iter=25
cl=colours()
step=seq(-2,0.8,by=0.005)
points=array(0,dim=c(length(step)^2,3))
t=0

for(a in step) {
  for(b in step+0.6) {
    x=0;y=0;n=0;dist=0
    while(n<max_iter & dist<4)  {
      n=n+1
      newx=a+x^2-y^2
      newy=b+2*x*y
      dist=newx^2+newy^2
      x=newx;y=newy
    }

    if(dist<4)  { 
      color=24 # black
    } else {
      color=n*floor(length(cl)/max_iter)
    }
    t=t+1
    points[t,]=c(a,b,color)
  }
}

df=as.data.frame(points)    

# ggplot with elements in the plot panel set to element_blank()
# and no expansion on the scales
p = ggplot(data=df, aes(V1, V2, color=cl[V3]))+ 
  geom_point() + 
  scale_x_continuous(expand = c(0,0), limits=range(df$V1)) +
  scale_y_continuous(expand = c(0,0), limits=range(df$V2))+
  theme(panel.grid=element_blank(), 
       panel.background=element_rect(fill = "transparent",colour = NA),
       panel.border=element_blank()) +
  scale_colour_manual(values=sort(c("#00000000", rainbow(35)), decreasing=FALSE))

# Get the ggplot grob
gt = ggplotGrob(p)

# Select plot panel only
#   gt = gt[6,4]    # Using index notation; OR
gt = gtable::gtable_filter(gt, "panel")  

# Draw it
grid.newpage()
grid.draw(gt)


# Set up a print method 
class(gt) = c("Panel", class(gt))
print.Panel <- function(x) {
   grid.newpage()   
   grid.draw(x)
}

gt
ggsave('mandelbrot.png', gt)
Sandy Muspratt
  • 31,719
  • 12
  • 116
  • 122
5

I was able to get rid of the white border by setting negative plot margins and setting the axis titles to NULL. I've marked the edits in the code below.

p = ggplot(data=df, aes(V1, V2, color=cl[V3]))+ 
  geom_point() + 
  theme(panel.background=element_blank(), 
        panel.grid.major=element_blank(), 
        panel.grid.minor=element_blank(), 
        panel.margin = unit(c(0, 0, 0, 0), "cm"),       
        axis.ticks=element_blank(), 
        axis.text.x=element_blank(), 
        axis.text.y=element_blank(), 
        axis.title.x=element_blank(), 
        axis.title.y=element_blank(),
        plot.background = element_rect(fill = "transparent",colour = NA),
        plot.margin = unit(c(-1, -1.2, -1.2, -1.5), "cm"),  # Edited code
        legend.position = 'none') +
  labs(x=NULL, y=NULL) # New code
eipi10
  • 91,525
  • 24
  • 209
  • 285
  • are those hard-coded `plot.margin` entries specifically for this plot? the `null` solution (which does work now) in my answer makes it more generic if so. – hrbrmstr Jul 06 '15 at 20:58
  • Yes. They're hard-coded, so a generic solution is definitely better. – eipi10 Jul 06 '15 at 21:03
  • Also, it seems there is a change in arguments name!? _`panel.margin` is deprecated. Please use `panel.spacing` property instead_ – Matifou Aug 29 '21 at 18:52
3

I'm several years late to the party but I thought of leaving the solution that worked for me (with ggplot 3.3.6). My solution involved using ggplots's own empty theme called theme_void() and hiding the legend (as you had already done). to stretch the graph to fill the entire canvas, I simply used the expand = c(0,0) parameter for both the x and y axes, and Viola!

enter image description here

Here is the R code specific for generating the plot using ggplot:

df=as.data.frame(points)    

ggplot(data=df, aes(V1, V2, color=cl[V3]))+ 
  geom_point() + 
  scale_colour_manual(values = sort(c("#00000000", rainbow(35)), 
                                  decreasing=FALSE))+
  scale_x_continuous(expand=c(0,0))+
  scale_y_continuous(expand=c(0,0))+
  theme_void()+
  theme(legend.position = "none")