在 R 中为 tikzdevice 栅格化 ggplot 图像
Rasterise ggplot images in R for tikzdevice
我使用 R 分析数据,使用 ggplot 创建绘图,使用 tikzDevice 打印它们,最后使用 latex 创建报告。问题是由于乳胶的内存限制,具有很多点的大图会失败。我在这里 https://github.com/yihui/tikzDevice/issues/103 找到了一个在打印 tikz 文件之前将绘图栅格化的解决方案,它允许单独打印点和文本。
require(png)
require(ggplot2)
require(tikzDevice)
## generate data
n=1000000; x=rnorm(n); y=rnorm(n)
## first try primitive
tikz("test.tex",standAlone=TRUE)
plot(x,y)
dev.off()
## fails due to memory
system("pdflatex test.tex")
## rasterise points first
png("inner.png",width=8,height=6,units="in",res=300,bg="transparent")
par(mar=c(0,0,0,0))
plot.new(); plot.window(range(x), range(y))
usr <- par("usr")
points(x,y)
dev.off()
# create tikz file with rasterised points
im <- readPNG("inner.png",native=TRUE)
tikz("test.tex",7,6,standAlone=TRUE)
plot.new()
plot.window(usr[1:2],usr[3:4],xaxs="i",yaxs="i")
rasterImage(im, usr[1],usr[3],usr[2],usr[4])
axis(1); axis(2); box(); title(xlab="x",ylab="y")
dev.off()
## this works
system("pdflatex test.tex")
## now with ggplot
p <- ggplot(data.frame(x=x, y=y), aes(x=x, y=y)) + geom_point()
## what here?
在此示例中,第一个 pdflatex
失败。由于光栅化,第二次成功。
如何使用 ggplot 应用它?
这里有一个 proof-of-principle 来说明将涉及的步骤。正如评论中指出的那样,它既不值得推荐也不实用,但可以作为 lower-level 实现的基础。
require(png)
require(ggplot2)
require(tikzDevice)
n=100;
d <- data.frame(x=rnorm(n), y=rnorm(n), z=rnorm(n))
p <- ggplot(d, aes(x=x, y=y, colour=z, size=z, alpha=x)) + geom_point()
## draw the layer by itself on a png file
library(grid)
g <- ggplotGrob(p)
# grid.newpage()
gg <- g$grobs[[6]]$children[[3]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=10, height=4, bg = "transparent", res = 30, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
rl <- readPNG(tmp, native = TRUE)
unlink(tmp)
## add it to a plot - note that the positions match,
## but the size can be off unless one ensures that the panel has the same size and aspect ratio
ggplot(d, aes(x=x, y=y)) + geom_point(shape="+", colour="red") +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"))) +
geom_point(aes(size=z), shape=1, colour="red", show.legend = FALSE)
## to illustrate the practical use, we use a blank layer to train the scales
## and set the panel size to match the png file
pf <- ggplot(d, aes(x=x, y=y)) + geom_blank() +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"), interpolate = FALSE))
tikz("test.tex", standAlone=TRUE)
grid.draw(egg::set_panel_size(pf, width=unit(10, "cm"), height=unit(4, "cm")))
dev.off()
system("lualatex test.tex")
system("open test.pdf")
我们可以放大并检查文本是 vector-based 而图层是(这里 low-res 用于演示)光栅。
好的,我会写在这里,因为它对于评论框来说太大了。您实际上可以用栅格化的 grob 替换原始的 grob,而不是将栅格化的点添加到具有新比例的 nw 图
g$grobs[[6]]$children[[3]] <- rasterGrob(rl)
。问题是它不能缩放,所以你必须事先知道最终图像的大小。然后你可以这样起诉:
rasterise <- function(ggp,
width = 6,
height = 3,
res.raster = 300,
raster.id= c(4,3),
file = ""){
## RASTERISE
require(grid)
require(png)
## draw the layer by itself on a png file
gb <- ggplot_build(ggp)
gt <- ggplot_gtable(gb)
## calculate widths
h <- as.numeric(convertUnit(sum(gt$heights), unitTo="in"))
w <- as.numeric(convertUnit(sum(gt$widths) , unitTo="in"))
w.raster <- width-w
h.raster <- height-h
## print points as png
grid.newpage()
gg <- gt$grobs[[raster.id[1]]]$children[[raster.id[2]]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=w.raster, height=h.raster, bg = "transparent", res = res.raster, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
points <- readPNG(tmp, native = TRUE)
points <- rasterGrob(points, width = w.raster, height = h.raster, default.units = "in")
unlink(tmp)
## ADD TO PLOT
gt$grobs[[raster.id[1]]]$children[[raster.id[2]]] <- points
## PLOT TMP
### HERE YOU CAN ONLY PRINT IT IN THIS DIMENSIONS!
pdf(file, width = width, height = height)
grid.draw(gt)
dev.off()
}
然后与
一起使用
data <- data.frame(x = rnorm(1000), y = rnorm(1000))
plot <- ggplot(data, aes(x = x, y = y)) +
geom_point() +
annotate("text", x = 2, y = 2, label = "annotation")
rasterise(ggp = plot,
width = 6,
height = 3,
res.raster = 10,
raster.id = c(4,2),
file = "~/test.pdf")
问题仍然是您要栅格化的 grob 的 ID。我没有想出一种自动找到正确的方法。这取决于您添加到图中的图层。
我使用 R 分析数据,使用 ggplot 创建绘图,使用 tikzDevice 打印它们,最后使用 latex 创建报告。问题是由于乳胶的内存限制,具有很多点的大图会失败。我在这里 https://github.com/yihui/tikzDevice/issues/103 找到了一个在打印 tikz 文件之前将绘图栅格化的解决方案,它允许单独打印点和文本。
require(png)
require(ggplot2)
require(tikzDevice)
## generate data
n=1000000; x=rnorm(n); y=rnorm(n)
## first try primitive
tikz("test.tex",standAlone=TRUE)
plot(x,y)
dev.off()
## fails due to memory
system("pdflatex test.tex")
## rasterise points first
png("inner.png",width=8,height=6,units="in",res=300,bg="transparent")
par(mar=c(0,0,0,0))
plot.new(); plot.window(range(x), range(y))
usr <- par("usr")
points(x,y)
dev.off()
# create tikz file with rasterised points
im <- readPNG("inner.png",native=TRUE)
tikz("test.tex",7,6,standAlone=TRUE)
plot.new()
plot.window(usr[1:2],usr[3:4],xaxs="i",yaxs="i")
rasterImage(im, usr[1],usr[3],usr[2],usr[4])
axis(1); axis(2); box(); title(xlab="x",ylab="y")
dev.off()
## this works
system("pdflatex test.tex")
## now with ggplot
p <- ggplot(data.frame(x=x, y=y), aes(x=x, y=y)) + geom_point()
## what here?
在此示例中,第一个 pdflatex
失败。由于光栅化,第二次成功。
如何使用 ggplot 应用它?
这里有一个 proof-of-principle 来说明将涉及的步骤。正如评论中指出的那样,它既不值得推荐也不实用,但可以作为 lower-level 实现的基础。
require(png)
require(ggplot2)
require(tikzDevice)
n=100;
d <- data.frame(x=rnorm(n), y=rnorm(n), z=rnorm(n))
p <- ggplot(d, aes(x=x, y=y, colour=z, size=z, alpha=x)) + geom_point()
## draw the layer by itself on a png file
library(grid)
g <- ggplotGrob(p)
# grid.newpage()
gg <- g$grobs[[6]]$children[[3]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=10, height=4, bg = "transparent", res = 30, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
rl <- readPNG(tmp, native = TRUE)
unlink(tmp)
## add it to a plot - note that the positions match,
## but the size can be off unless one ensures that the panel has the same size and aspect ratio
ggplot(d, aes(x=x, y=y)) + geom_point(shape="+", colour="red") +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"))) +
geom_point(aes(size=z), shape=1, colour="red", show.legend = FALSE)
## to illustrate the practical use, we use a blank layer to train the scales
## and set the panel size to match the png file
pf <- ggplot(d, aes(x=x, y=y)) + geom_blank() +
annotation_custom(rasterGrob(rl, width = unit(1,"npc"), height=unit(1,"npc"), interpolate = FALSE))
tikz("test.tex", standAlone=TRUE)
grid.draw(egg::set_panel_size(pf, width=unit(10, "cm"), height=unit(4, "cm")))
dev.off()
system("lualatex test.tex")
system("open test.pdf")
我们可以放大并检查文本是 vector-based 而图层是(这里 low-res 用于演示)光栅。
好的,我会写在这里,因为它对于评论框来说太大了。您实际上可以用栅格化的 grob 替换原始的 grob,而不是将栅格化的点添加到具有新比例的 nw 图
g$grobs[[6]]$children[[3]] <- rasterGrob(rl)
。问题是它不能缩放,所以你必须事先知道最终图像的大小。然后你可以这样起诉:
rasterise <- function(ggp,
width = 6,
height = 3,
res.raster = 300,
raster.id= c(4,3),
file = ""){
## RASTERISE
require(grid)
require(png)
## draw the layer by itself on a png file
gb <- ggplot_build(ggp)
gt <- ggplot_gtable(gb)
## calculate widths
h <- as.numeric(convertUnit(sum(gt$heights), unitTo="in"))
w <- as.numeric(convertUnit(sum(gt$widths) , unitTo="in"))
w.raster <- width-w
h.raster <- height-h
## print points as png
grid.newpage()
gg <- gt$grobs[[raster.id[1]]]$children[[raster.id[2]]]
gg$vp <- viewport() # don't ask me
tmp <- tempfile(fileext = "png")
png(tmp, width=w.raster, height=h.raster, bg = "transparent", res = res.raster, units = "in")
grid.draw(gg)
dev.off()
## import it as a raster layer
points <- readPNG(tmp, native = TRUE)
points <- rasterGrob(points, width = w.raster, height = h.raster, default.units = "in")
unlink(tmp)
## ADD TO PLOT
gt$grobs[[raster.id[1]]]$children[[raster.id[2]]] <- points
## PLOT TMP
### HERE YOU CAN ONLY PRINT IT IN THIS DIMENSIONS!
pdf(file, width = width, height = height)
grid.draw(gt)
dev.off()
}
然后与
一起使用data <- data.frame(x = rnorm(1000), y = rnorm(1000))
plot <- ggplot(data, aes(x = x, y = y)) +
geom_point() +
annotate("text", x = 2, y = 2, label = "annotation")
rasterise(ggp = plot,
width = 6,
height = 3,
res.raster = 10,
raster.id = c(4,2),
file = "~/test.pdf")
问题仍然是您要栅格化的 grob 的 ID。我没有想出一种自动找到正确的方法。这取决于您添加到图中的图层。