I am trying add axes to the bitmap plot in R. But its not showing those axes. I am using OS X 10.10 with R v3.3.0
set.seed(0)
#Sample matrix
bitmap<-matrix(rnorm(150000,mean=1:500),nrow = 300, ncol = 500)
image(bitmap,col = RColorBrewer::brewer.pal(9,"Greys"), axes=FALSE,
useRaster = TRUE)
axis(1,at = seq(from=1000,to = 10000,length.out = 19),
labels = seq(from=1000,to = 10000,length.out = 19))
axis(2,at = seq(from=0,to = 100,length.out = 11),
labels = seq(from=0,to = 100,length.out = 11))
How to show these required axes with required ranges in R ?
How to specify the size of the plotted bitmap in R, either in terms of pixels or in some length unit ?
How can I scale bitmap's width and height independently ?
Is there any other R package which will allow me addressing these parameters in a better way ?
image() scales both axes to 1. You just need to adjust your at= values
axis(1,at = seq(from=1000,to = 10000,length.out = 19)/10000,
labels = seq(from=1000,to = 10000,length.out = 19))
axis(2,at = seq(from=0,to = 100,length.out = 11)/100,
labels = seq(from=0,to = 100,length.out = 11))
The size of the rendered image is based on your current graphics device. You cannot explicitly control the size of the drawing area in pixels, just the overall plot size (with axes and everything). Graphics in R tend to grow and shrink depending on window size. You can fix the aspect ratio if you like with asp=1 in the plot() call.
If you need pixel-level control, you might want to use some other program for plotting.
Related
With a large number of variables, the sizing of tick mark labels is too large in barplots along the diagonal of a mosaic pairs plot created with pairs_barplot() in the R vcd package. Is there any way to make them smaller? Here is a minimal working example:
library(vcd)
#> Loading required package: grid
library(vcdExtra)
#> Loading required package: gnm
pairs(table(ICU[,c(1,3:9)]),
diag_panel = pairs_barplot(
gp_vartext = gpar(fontsize = 10, fontface = 2),
gp_leveltext = gpar(fontsize = 8),
abbreviate = 1,
var_offset = 1.25))
Created on 2021-10-16 by the reprex package (v2.0.1)
Currently, you cannot set gpar() for drawing the y-axis of the bar plot explicitly. There are two general workarounds, though: (1) Not messing with the font sizes but instead plotting on a bigger device. (2) Setting an outer viewport with a different gpar(fontsize = ...) that is used as the viewports further down in the plot.
(1) Bigger device
For illustration I use a png() device here because the PNG graphic is what I embed on StackOverflow. But, of course, you could use the same trick on other devices including those that you do not create yourself but via chunk options in R/Markdown etc.
I use a device size of 13 x 13 inches (as opposed to a more common setting of 6 x 6 or 7 x 7 inches). Then I can omit all of the gpar() settings because the device is large enough to accomodate the default parameters. I still set abbreviate and var_offset, though.
png("pairs1.png", height = 13, width = 13, units = "in", res = 100)
pairs(table(ICU[, c(1, 3:9)]),
diag_panel = pairs_barplot(abbreviate = 1, var_offset = 1.25))
dev.off()
(2) Outer viewport
Alternatively, I can create a new grid page myself and push a viewport with gpar(fontsize = 7) used as the default in this viewport and its children. Then I keep your gpar() settings in pairs_barplot() and just add newpage = FALSE in the pairs() call because I want to use the page I already created.
Then all font sizes are decreased so that plotting on a 7 x 7 inches device works fine.
png("pairs2.png", height = 7, width = 7, units = "in", res = 150)
grid.newpage()
pushViewport(viewport(gp = gpar(fontsize = 7)))
pairs(table(ICU[, c(1, 3:9)]),
diag_panel = pairs_barplot(
gp_vartext = gpar(fontsize = 10, fontface = 2),
gp_leveltext = gpar(fontsize = 8),
abbreviate = 1, var_offset = 1.25),
newpage = FALSE)
dev.off()
Not the problem:
There are lots of folks who ask variations on "how do I save a figure" where the figure has borders, annotations, and style; I'm not looking for any of that because I can do it in base, lattice, or ggsave. If you need me to, then I can make a list of 20 SO questions this isn't the same as.
tl;dr
I want to have a bitmap file where my matrix is the values. In python, using OpenCV I can read a matrix, and the pixel [1,1] is going to have a particular value. If I change it, and save it, then that intensity value has changed. How do I get that?
Details:
When I run this code:
set.seed(1)
img_data <- matrix(sample(x = 0:255, size = 228*228,replace = T),nrow = 228,ncol = 228)
image(img_data)
I get this image:
You can see the default annotation. Annotation can be removed.
set.seed(1)
img_data <- matrix(sample(x = 0:255, size = 228*228,replace = T),nrow = 228,ncol = 228)
image(img_data, xaxt='n',yaxt='n')
And this looks less bad.
but opened in mspaint, it shows the problem.
Problems:
White border around actual information
image size is 789x503
I want an image that is 228x228, and the value [1,1] of the image is value [1,1] of the matrix.
How in base, lattice, ggplot, or something else R, does one make that?
Update:
This almost works.
set.seed(1)
img_data <- matrix(sample(x = 0:255, size = 228*228,replace = T),nrow = 228,ncol = 228)
mar_old <- par("mar") #lets not permanently change values
xpd_old <- par("xpd") #lets not permanently change values
bmp(filename = "mytest.bmp", width = 227, height = 228, units = "px")
par(mar=rep(0, 4), xpd = NA)
image(img_data, bty ="n",axes=F,frame.plot=F, xaxt='n', ann=FALSE, yaxt='n', asp=1)
dev.off()
par(mar=mar_old, xpd=xpd_old)
It makes this image
It still leaves a white line on the right and lower edges when viewed in mspaint.
Perhaps bitmaps start their counting at zero??
Update2:
This almost works, and might be what I have to go with.
library(magick)
set.seed(1)
img_data <- array(sample(x = 0:255, size = 228*228*3,replace = T),dim = c(228,228,3))
img <- magick::image_read(img_data/255)
image_write(img, path = "mystes3.bmp", format = "bmp")
It gives this:
And in mspaint:
It has to have 3 layers, RGB (rgba?), to get converted. This means it is a 3d array and not a 2d matrix. It gets the size right in that it doesn't add padding.
You need to set the useRaster parameter to TRUE in image():
set.seed(1)
img_data <- matrix(sample(x = 0:255, size = 228*228,replace = T),nrow = 228,ncol = 228)
mar_old <- par("mar") #lets not permanently change values
xpd_old <- par("xpd") #lets not permanently change values
bmp(filename = "mytest.bmp", width = 228, height = 228, units = "px")
par(mar=rep(0, 4), xpd = NA)
image(img_data, bty ="n",axes=F,frame.plot=F, xaxt='n', ann=FALSE, yaxt='n', asp=1, useRaster = T)
dev.off()
par(mar=mar_old, xpd=xpd_old)
I also corrected the height to 228 instead of 227.
Did a smaller image:
With useRaster = T
Without useRaster:
Without useRaster you even lose a row and a column.
To produce a high-quality png file from a plot, I usually increase the value of the res = argument of the png(). But in the following case, I have a complex 7x4 plotting platform, and changing the res = changes the actual appearance of the plot (e.g., plot frames look much thicker etc.).
I am wondering how I could raise the quality of the following plot while preserving its original appearance (i.e., as shown in the graphical device)?
P.S: I'm just trying to achieve a high-quality version of what I see in my graphical device.
png("Plot.png")
par(mfcol = c(7, 4), mar = rep(.1, 4), oma = rep(7, 4))
invisible(lapply(1:28, plot, t = "n", xaxt = "n", yaxt = "n"))
dev.off()
Does this look better?
png("Plot.png", res = 600, width = 8, height = 7, units = "in")
par(mai = c(0.5,0.5,0.5,0.5))
par(mfcol = c(7, 4), mar = rep(.1, 4), oma = rep(7, 4))
invisible(lapply(1:28, function(x){
plot(rnorm(20), rnorm(20), axes = FALSE, col = sample(1:7, 1), ann = FALSE)
box()
}))
dev.off()
Depending on what you're trying to accomplish, you can also increase width and height parameters within png. As you increase res, if you increase the width and height the relative layout will stay the same, but you'll end up with more pixels so for a given physical dimension it will be higher resolution.
I use the following script to generate a legend in R. But the legend box is too small... how do I increase the box width?
legend("topleft", lty = 1, legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),col = c("black","red","blue"))
You are probably resizing your graph after you plot it and the legend. If that is the case, and you want to keep the box, one option would be to plot the graph, resize it, and then generate the legend. Perhaps a better option would be to size the window to the desired width to start with:
# on Windows, you can use the `windows` function. elsewhere, try quartz or X11
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
#plot = FALSE,
#bty = "n")
)
You can also define exactly where you want the box to fall by providing a pair of x and y coordinates to the legend function. Those values would represent the upper left and bottom right corners of the box. The legend function will actually generate the coordinates for the upper-left hand corner of the box along with the width and height. By default it returns them invisibly, but you can assign them to an object, and If you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
windows(height = 7, width = 3.5)
plot(hp ~ mpg, data = mtcars)
legend(x = c(9.46, 31), y = c(346.32, 298),
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
lty = 1)
The legend function will actually generate the coordinates for the upper-left hand corner of the box (that's where I got 9.46 and 346.62) along with the width and height of the box. By default it returns them invisibly, but you can assign them to an object, and if you use the plot = FALSE, option to legend you can capture those coordinates and modify them as you wish without actually plotting the legend.
plot(hp ~ mpg, data = mtcars)
leg <- legend("topleft", lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"),
plot = FALSE)
# adjust as desired
leftx <- leg$rect$left
rightx <- (leg$rect$left + leg$rect$w) * 1.2
topy <- leg$rect$top
bottomy <- (leg$rect$top - leg$rect$h) * 1
# use the new coordinates to define custom
legend(x = c(leftx, rightx), y = c(topy, bottomy), lty = 1,
legend = c("Sub_metering_1","Sub_metering_2","Sub_metering_3"),
col = c("black","red","blue"))
Part of the legend width is determined by the longest width of the labels you use, which is calculated via strwidth. Below an easy example how to halve or double the size by using legend(..., text.width = ...).
plot(1)
text = c("Sub_metering_1","Sub_metering_2","Sub_metering_3")
legend("topleft"
,lty = 1
,legend = text
,col = c("black","red","blue")
)
strwidth(text)
# [1] 0.1734099 0.1734099 0.1734099
# half the length
legend("bottomleft"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]/2
,col = c("black","red","blue")
)
# double the length
legend("center"
,lty = 1
,legend = text
,text.width = strwidth(text)[1]*2
,col = c("black","red","blue")
)
I've created a choropleth of Brazil. When saving the plot in .png, the upper and the lower part of the plot are lost (covered). Here are the lines to save the plot.
plot.new()
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
plot(c(-75,-35),c(0,-30),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()
For saving the plot without losing the upper and the lower part of the map, I must change the coordinates to add white space at the bottom and at the top (i.e. replace c(0,-30) by c(5,-33)):
plot.new()
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
plot(c(-75,-35),c(5,-33),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()
This works in the sense that I can see the full map but the map then does not use all the available area in the figure. It seems that there are some margin in the upper and the lower part of the figure when saving the plot. I've never had that problem with other types of plot.
Sorry, I don't have enough "reputation" to post images to show you how the maps look like.
Any idea of how to fix this?
Edit:
The comments below got me searching more into the problem and I finally found a fix. I apologize as I now realized that I did not understand the source of the problem and thus did not explain as best as I could have,
It seems that png resets the outer margin of the plot. Thus, even though I had set omi=c(0,0,0,0), those were not the value used by the png command in saving the plot. The solution was to set the plot parameters after calling png so save the figure.
plot.new()
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
par(omi=c(0,0,0,0), mgp=c(0,0,0),mar=c(0,0,0,0) , family = "D")
par(mfrow=c(1,1),cex=1,cex.lab = 0.75,cex.main=0.2,cex.axis=0.2)
plot(c(-75,-35),c(5,-33),type="n",axes=FALSE,xlab="",ylab="",asp=1.2)
plot(Brazil,col=cols[Brazil$Cons.g_ri],add=TRUE,border="black",lwd=0.5)
dev.off()
From Details in ?par:
Each device has its own set of graphical parameters.
Thus, even though I had set the outer margin of the plot in par (omi = c(0,0,0,0)), those value were overwritten by the parameters in png when saving the plot.
The solution was to set the margin parameters in par after calling png
plot.new()
# first open png device...
png(filename = "map_cons_g.png", width = 6,height = 6, units = "in", res = 600)
# ...then set par
par(omi = c(0,0,0,0), mgp = c(0,0,0), mar = c(0,0,0,0), family = "D")
par(mfrow = c(1, 1), cex = 1, cex.lab = 0.75, cex.main = 0.2, cex.axis = 0.2)
plot(c(-75, -35), c(5, -33), type = "n", axes = FALSE, xlab = "", ylab = "", asp = 1.2)
plot(Brazil, col = cols[Brazil$Cons.g_ri], add = TRUE, border = "black", lwd = 0.5)
dev.off()