Save Plot figure in Julia - julia

I am new to Julia and am using the Plots.jl [GR] library to create plots. I am trying to save my plot as a PNG to display on my GTK GUI. I am able to save the plot as an html file and text file but not .png using the savefig function (https://docs.juliaplots.org/latest/api/#Plots.savefig-Tuple{Plots.Plot,%20AbstractString})
I have been unsuccessful following this post: https://stackoverflow.com/a/62981826/13306172
The error i receive is:
ERROR: MethodError: no method matching savefig(::Plots.Plot{Plots.GRBackend}, ::String)
x = 1:10; y = rand(10, 4)
import GR
using Plots
using PlotlyBase
using PlotlySave
gr()
import PlotlySave.savefig
function return_image_plot()
x = 1:10; y = rand(10, 4)
x1 = 1:10; y1 = rand(10, 1)
plot1 = Plots.plot(x, y)
plot2 = Plots.plot(x1, y1)
plotty = Plots.plot(plot1, plot2, layout = (2, 1))
Plots.savefig(plotty, "ploty2.png")
end
I also attempted to use I also attempted to use PlotySave Julia library https://github.com/hhaensel/PlotlySave.jl but was unsuccessful and also received a 'no matching method' error
I have followed/copied multiple SoF posts but have continued to be unsuccessful. Any help is appreciated.

Try this:
using Plots
function saveplot()
x = 1:10
y1 = rand(10, 4)
y2 = rand(10, 1)
p1 = plot(x, y1)
p2 = plot(x, y2)
p = plot(p1, p2, layout=(2, 1))
savefig(p, "plot.png")
end
saveplot()

Related

Julia equivalent for rgl R package?

I would like to know whether there exists in Julia something which would be rougly equivalent to the rgl package in R language; i.e., a library allowing for a dynamic/interactive representation of 3D plots, 3D surfaces, etc.
A bit of context: if you're studying, for example, morphometrics, you regularly end up with files in PLY format, or another format produced by 3D scanners. In R, you can for instance visualize (in an interactive way) easily a 3D surface acquired by such a scanner (here, a set of molars):
Do we have currently a similar feature in Julia? If so, which library should I use?
Thanks!
Makie.jl, specifically via either the GLMakie.jl or WebGLMakie.jl backends, is a good option for interactive plots. For instance, the following example from the BeautifulMakie gallery
using GLMakie
let
x = y = LinRange(-2, 2, 51)
z = (-x .* exp.(-x .^ 2 .- (y') .^ 2)) .* 4
zmin, zmax = minimum(z), maximum(z)
cmap = :viridis
fig = Figure(resolution = (900,900))
ax = Axis3(fig, aspect = :data, perspectiveness = 0.5, elevation = π/9,
xzpanelcolor= (:black, 0.75), yzpanelcolor= (:black,0.75),
zgridcolor = :grey, ygridcolor = :grey,xgridcolor = :grey)
surface!(ax, x, y, z, colormap = cmap, colorrange = (zmin, zmax))
xm, ym, zm = minimum(ax.finallimits[])
contour!(ax, x, y, z, levels = 20, colormap = cmap, linewidth = 2,
colorrange=(zmin, zmax), transformation = (:xy, zm))
wireframe!(ax, x, y, z, overdraw = true, transparency = true,
color = (:black, 0.1))
fig[1,1] = ax
fig
end
opens an interactive window that can be rotated at will with the cursor.
I am not familiar with the PLY format however, so cannot comment on that aspect of the question.

Is there a way to create a 3d plot with contours beneath the plot in R?

Is there a way to create a 3d plot in R that has contours beneath the plot like so? If not, is there a way to take my existing contour plot and put it on top of the 3d persp plot?
I have been able to make a 3d plot, and a contour plot of the function and constraint, but have not been able to combine the two. Here is my R code:
obj = function(x1,x2){
value = -(cos((x1-.1)*x2))^2 - x1*sin(3*x1+x2)
return(value)
}
con1 = function(x1,x2){
t = atan2(x1,x2)
value = x1^2 + x2^2 -((2*cos(t)-1/2*cos(2*t)-1/4*cos(3*t)-1/8*cos(4*t))^2) - ((2*sin(t))^2)
return(value)
}
x1 = seq(-2.25,2.5,.015)
x2 = seq(-2.5,1.75,.015)
out = outer(x1,x2,obj)
infeasible = ifelse(c(outer(x1,x2,con1)<=0),1,0)
image(x1,x2,out,xlab=expression(x[1]),ylab=expression(x[2]),main="Modified Townsend Problem")
X = cbind(expand.grid(x1,x2),infeasible)
points(X[X[,3]==0,1],X[X[,3]==0,2],pch=19,col="lightgrey")
persp(x1,x2,out,phi=35,col="lightgrey")
Any suggestions or packages that do it are greatly appreciated.
I reckon this is doable via ?contourLines and some transformation of your points:
p <- persp(x1, x2, out, phi=35, col="#00000000", border=NA, box=FALSE)
cl <- contourLines(x1, x2, replace(out, outer(x1,x2,con1) > 0, NA))
levs <- factor(sapply(cl, `[[`, "level"))
Map(
function(cl,col) lines(trans3d(cl$x, cl$y, min(out), pmat=p), col=col),
cl,
hcl.colors(nlevels(levs), "YlOrRd", rev = TRUE)[levs]
)
par(new=TRUE)
persp(x1, x2, out, phi=35, col="lightgrey")

How to plot a surface in rgl plot3d

So I have this code that produces the exact surface
f = function(x, y){
z = ((x^2)+(3*y^2))*exp(-(x^2)-(y^2))
}
plot3d(f, col = colorRampPalette(c("blue", "white")),
xlab = "X", ylab = "Y", zlab = "Z",
xlim = c(-3, 3), ylim = c(-3, 3),
aspect = c(1, 1, 0.5))
Giving the following plot:
Now I have some code that does a random walk metropolis algorithm to reproduce the above image. I think it works as if I do another plot of these calculated values I get the next image with 500 points. Here is the code
open3d()
plot3d(x0, y0, f(x0, y0), type = "p")
Which gives the following plot:
I know it's hard looking at this still image but being able to rotate the sampling is working.
Now here is my question: How can I use plot3d() so that I can have a surface that connects all these points and gives a more jagged representation of the exact plot? Or how can I have each point in the z axis as a bar from the xy plane? I just want something more 3 dimensional than points and I can't find how to do this.
Thanks for your help
You can do this by triangulating the surface. You don't give us your actual data, but I can create some similar data using
f = function(x, y){
z = ((x^2)+(3*y^2))*exp(-(x^2)-(y^2))
}
x <- runif(500, -3, 3)
y <- runif(500, -3, 3)
z <- f(x, y)
Then the plotting is done using the method in ?persp3d.deldir:
library(deldir)
library(rgl)
col <- colorRampPalette(c("blue", "white"))(20)[1 + round(19*(z - min(z))/diff(range(z)))]
dxyz <- deldir::deldir(x, y, z = z, suppressMsge = TRUE)
persp3d(dxyz, col = col, front = "lines", back = "lines")
This might need some cosmetic fixes, e.g.
aspect3d(2, 2, 1)
After some rotation, this gives me the following plot:
I'm not sure to understand what you want. If my understanding is correct, here is a solution. Define a parametric representation of your surface:
fx <- function(u,v) u
fy <- function(u,v) v
fz <- function(u,v){
((u^2)+(3*v^2))*exp(-(u^2)-(v^2))
}
Let's say you have these points:
x0 <- seq(-3, 3, length.out = 20)
y0 <- seq(-3, 3, length.out = 20)
Then you can use the function parametric3d of the misc3d package, with the option fill=FALSE to get a wireframe:
library(misc3d)
parametric3d(fx, fy, fz, u=x0, v=y0,
color="blue", fill = FALSE)
Is it what you want?
To get some vertical bars, use the function segments3d of rgl:
i <- 8
bar <- rbind(c(x0[i],y0[i],0),c(x0[i],y0[i],f(x0[i],y0[i])))
segments3d(bar, color="red")
Here is a plot with only 50 points using my original code.
When I then apply what was said by Stéphane Laurent I then get this plot which feels too accurate when given the actual points I have
Perhaps you need to explain to me what is actually happening in the function parametric3d

How to plot multiple columns at the same time? [duplicate]

I would like to plot y1 and y2 in the same plot.
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
plot(x, y1, type = "l", col = "red")
plot(x, y2, type = "l", col = "green")
But when I do it like this, they are not plotted in the same plot together.
In Matlab one can do hold on, but does anyone know how to do this in R?
lines() or points() will add to the existing graph, but will not create a new window. So you'd need to do
plot(x,y1,type="l",col="red")
lines(x,y2,col="green")
You can also use par and plot on the same graph but different axis. Something as follows:
plot( x, y1, type="l", col="red" )
par(new=TRUE)
plot( x, y2, type="l", col="green" )
If you read in detail about par in R, you will be able to generate really interesting graphs. Another book to look at is Paul Murrel's R Graphics.
When constructing multilayer plots one should consider ggplot package. The idea is to create a graphical object with basic aesthetics and enhance it incrementally.
ggplot style requires data to be packed in data.frame.
# Data generation
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
df <- data.frame(x,y1,y2)
Basic solution:
require(ggplot2)
ggplot(df, aes(x)) + # basic graphical object
geom_line(aes(y=y1), colour="red") + # first layer
geom_line(aes(y=y2), colour="green") # second layer
Here + operator is used to add extra layers to basic object.
With ggplot you have access to graphical object on every stage of plotting. Say, usual step-by-step setup can look like this:
g <- ggplot(df, aes(x))
g <- g + geom_line(aes(y=y1), colour="red")
g <- g + geom_line(aes(y=y2), colour="green")
g
g produces the plot, and you can see it at every stage (well, after creation of at least one layer). Further enchantments of the plot are also made with created object. For example, we can add labels for axises:
g <- g + ylab("Y") + xlab("X")
g
Final g looks like:
UPDATE (2013-11-08):
As pointed out in comments, ggplot's philosophy suggests using data in long format.
You can refer to this answer in order to see the corresponding code.
I think that the answer you are looking for is:
plot(first thing to plot)
plot(second thing to plot,add=TRUE)
Use the matplot function:
matplot(x, cbind(y1,y2),type="l",col=c("red","green"),lty=c(1,1))
use this if y1 and y2 are evaluated at the same x points. It scales the Y-axis to fit whichever is bigger (y1 or y2), unlike some of the other answers here that will clip y2 if it gets bigger than y1 (ggplot solutions mostly are okay with this).
Alternatively, and if the two lines don't have the same x-coordinates, set the axis limits on the first plot and add:
x1 <- seq(-2, 2, 0.05)
x2 <- seq(-3, 3, 0.05)
y1 <- pnorm(x1)
y2 <- pnorm(x2,1,1)
plot(x1,y1,ylim=range(c(y1,y2)),xlim=range(c(x1,x2)), type="l",col="red")
lines(x2,y2,col="green")
Am astonished this Q is 4 years old and nobody has mentioned matplot or x/ylim...
tl;dr: You want to use curve (with add=TRUE) or lines.
I disagree with par(new=TRUE) because that will double-print tick-marks and axis labels. Eg
The output of plot(sin); par(new=T); plot( function(x) x**2 ).
Look how messed up the vertical axis labels are! Since the ranges are different you would need to set ylim=c(lowest point between the two functions, highest point between the two functions), which is less easy than what I'm about to show you---and way less easy if you want to add not just two curves, but many.
What always confused me about plotting is the difference between curve and lines. (If you can't remember that these are the names of the two important plotting commands, just sing it.)
Here's the big difference between curve and lines.
curve will plot a function, like curve(sin). lines plots points with x and y values, like: lines( x=0:10, y=sin(0:10) ).
And here's a minor difference: curve needs to be called with add=TRUE for what you're trying to do, while lines already assumes you're adding to an existing plot.
Here's the result of calling plot(0:2); curve(sin).
Behind the scenes, check out methods(plot). And check body( plot.function )[[5]]. When you call plot(sin) R figures out that sin is a function (not y values) and uses the plot.function method, which ends up calling curve. So curve is the tool meant to handle functions.
if you want to split the plot into two columns (2 plots next to each other), you can do it like this:
par(mfrow=c(1,2))
plot(x)
plot(y)
Reference Link
As described by #redmode, you may plot the two lines in the same graphical device using ggplot. In that answer the data were in a 'wide' format. However, when using ggplot it is generally most convenient to keep the data in a data frame in a 'long' format. Then, by using different 'grouping variables' in the aesthetics arguments, properties of the line, such as linetype or colour, will vary according to the grouping variable, and corresponding legends will appear.
In this case, we can use the colour aessthetics, which matches colour of the lines to different levels of a variable in the data set (here: y1 vs y2). But first we need to melt the data from wide to long format, using e.g. the function 'melt' from reshape2 package. Other methods to reshape the data are described here: Reshaping data.frame from wide to long format.
library(ggplot2)
library(reshape2)
# original data in a 'wide' format
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
df <- data.frame(x, y1, y2)
# melt the data to a long format
df2 <- melt(data = df, id.vars = "x")
# plot, using the aesthetics argument 'colour'
ggplot(data = df2, aes(x = x, y = value, colour = variable)) + geom_line()
If you are using base graphics (i.e. not lattice/ grid graphics), then you can mimic MATLAB's hold on feature by using the points/lines/polygons functions to add additional details to your plots without starting a new plot. In the case of a multiplot layout, you can use par(mfg=...) to pick which plot you add things to.
You can use points for the overplot, that is.
plot(x1, y1,col='red')
points(x2,y2,col='blue')
Idiomatic Matlab plot(x1,y1,x2,y2) can be translated in R with ggplot2 for example in this way:
x1 <- seq(1,10,.2)
df1 <- data.frame(x=x1,y=log(x1),type="Log")
x2 <- seq(1,10)
df2 <- data.frame(x=x2,y=cumsum(1/x2),type="Harmonic")
df <- rbind(df1,df2)
library(ggplot2)
ggplot(df)+geom_line(aes(x,y,colour=type))
Inspired by Tingting Zhao's Dual line plots with different range of x-axis Using ggplot2.
Rather than keeping the values to be plotted in an array, store them in a matrix. By default the entire matrix will be treated as one data set. However if you add the same number of modifiers to the plot, e.g. the col(), as you have rows in the matrix, R will figure out that each row should be treated independently. For example:
x = matrix( c(21,50,80,41), nrow=2 )
y = matrix( c(1,2,1,2), nrow=2 )
plot(x, y, col("red","blue")
This should work unless your data sets are of differing sizes.
You could use the ggplotly() function from the plotly package to turn any of the gggplot2 examples here into an interactive plot, but I think this sort of plot is better without ggplot2:
# call Plotly and enter username and key
library(plotly)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
plot_ly(x = x) %>%
add_lines(y = y1, color = I("red"), name = "Red") %>%
add_lines(y = y2, color = I("green"), name = "Green")
You can also create your plot using ggvis:
library(ggvis)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
df <- data.frame(x, y1, y2)
df %>%
ggvis(~x, ~y1, stroke := 'red') %>%
layer_paths() %>%
layer_paths(data = df, x = ~x, y = ~y2, stroke := 'blue')
This will create the following plot:
Using plotly (adding solution from plotly with primary and secondary y axis- It seems to be missing):
library(plotly)
x <- seq(-2, 2, 0.05)
y1 <- pnorm(x)
y2 <- pnorm(x, 1, 1)
df=cbind.data.frame(x,y1,y2)
plot_ly(df) %>%
add_trace(x=~x,y=~y1,name = 'Line 1',type = 'scatter',mode = 'lines+markers',connectgaps = TRUE) %>%
add_trace(x=~x,y=~y2,name = 'Line 2',type = 'scatter',mode = 'lines+markers',connectgaps = TRUE,yaxis = "y2") %>%
layout(title = 'Title',
xaxis = list(title = "X-axis title"),
yaxis2 = list(side = 'right', overlaying = "y", title = 'secondary y axis', showgrid = FALSE, zeroline = FALSE))
Screenshot from working demo:
we can also use lattice library
library(lattice)
x <- seq(-2,2,0.05)
y1 <- pnorm(x)
y2 <- pnorm(x,1,1)
xyplot(y1 + y2 ~ x, ylab = "y1 and y2", type = "l", auto.key = list(points = FALSE,lines = TRUE))
For specific colors
xyplot(y1 + y2 ~ x,ylab = "y1 and y2", type = "l", auto.key = list(points = F,lines = T), par.settings = list(superpose.line = list(col = c("red","green"))))
Use curve for mathematical functions.
And use add=TRUE to use the same plot and axis.
curve( log2 , to=5 , col="black", ylab="log's(.)")
curve( log , add=TRUE , col="red" )
curve( log10, add=TRUE , col="blue" )
abline( h=0 )

How to project a 2D image as a surface in a 3D scatter plot (in R)?

I'm working with 3 dimensional coordinates data, which i'm plotting in a scatterplot, i have ~30.000 datapoints, and i've included the first 10 here so that you can reproduce it
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694,
173.89244,
135.73004,
121.93766,
109.72152,
92.53709,
165.46588,
169.77744,
127.01796,
99.34347),
meanY = c(140.40816,
110.99128,
134.56023,
164.18703,
166.04051,
155.97329,
105.29377,
104.42683,
130.17066,
155.99696),
avgDist = c(40.788118,
12.957329,
14.24348,
39.10424,
34.694258,
25.532335,
21.491695,
23.528944,
9.309201,
31.916879))
I've been using the scatter3d function to plot this
scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
Now my "problem", is that I would like to have a 2d surface with an external image file overlayed onto it at z=0, and as a bonus, if i could project a heatmap/contours from the scatterplot data (meanX and meanY used for the contours) over that image as well, that would be great.
This is the image i'd like to have draped at z = 0:
http://i.imgur.com/m6j4q3M.png
That image was made with this ggplot:
map.colors <- colorRampPalette(c("green","yellow","red"))
densityPlot <- ggplot(direData, aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
xlim(70,185) + ylim(70,185)
minimap <- readPNG('~/yasp/minimap.png')
densityPlot + annotation_raster(minimap, ymin = 70 ,ymax=185 ,xmin = 70,xmax = 185) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=10*sqrt(..density..)), contour=FALSE, n=100)
Is there any way to do this? I've googled quite a bit for a solution but found no real way of doing this. I don't mind creating the image first in ggplot2 with the heatmap, saving that, and then using that as input for the surface draping, but it would of course be quite cool if it could all be done in one call to plot.
(2nd Edit) I try to write something better code and confirm two xy-coordinates are the same. ggplot2 theme with no axes or grid help me to plot only the panel region.
library(rgl); library(grid); library(gtable)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
map.colors <- colorRampPalette(c("green","yellow","red"))
# set scale_*_continuous() to plot only the panel region. limits mean xlim (or ylim)
# change "tile" into "raster" because of making noise lines on my screen
densityPlot <- ggplot(df[,1:2], aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="raster", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
scale_x_continuous(limits=c(70,185), expand = c(0,0)) + scale_y_continuous(limits=c(70,185), expand = c(0,0)) +
geom_point(size=4) # to test XY-coordinate (black points on the map)
open3d()
plot3d( df, type="s", radius=1, col="red", axes=F,
xlim = c(70,185), ylim = c(70,185),
expand = 1 )
plot3d( df, type="h", col="blue", add=T ) # to test XY-coordinate (line segments from z = 0)
axes3d(c("x","y","z") )
show2d({ # show2d uses 2D plot function's output as a texture on a box.
grid.draw(gtable_filter(ggplotGrob(densityPlot), "panel"))
},
expand = 1 , texmipmap = F ) # texmipmap = F makes tone clear (not essential)
# I think this is clearly better than using a intermediate file,
# so I deleted related code. Thanks Mike !
How about this?
I stored your lined image file in a png in the local directory, there is probably a way to do that without an intermediate file, but I would ask that as a separate question.
Note that this is actually a simple case of texture mapping. The texture is saved in the gameshot.png file you specified. You could warp the text around a more complicated object by adding more points to the geometry and adjusting the texture map coordinates accordingly.
While they should not have been absolutely necessary here, I added texture map coordinates as it looked like the file and the data were not aligned by default - and in fact the gameshot.png file was displaying reversed. It looks to me like the png file you specified does not quite match the data, I think there is an inversion somewhere before you saved it.
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
car::scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
xvek <- c(0,1)
yvek <- c(0,1)
lnx <- length(xvek)
lny <- length(yvek)
zmat <- matrix(0,lnx,lny)
# Setup the Texture coordinates - defaults seem to invert image
# tms <- matrix(c(0,0,1,1),lnx,lny) # generic case (xy-maped texture looks like png file)
# tmt <- matrix(c(0,1,0,1),lnx,lny)
tmt <- matrix(c(1,1,0,0),lnx,lny) # "correct case" (ball density look more like picture)
tms <- matrix(c(1,0,1,0),lnx,lny) # I think the gameshot.png is in error
# Texture file specified in question was stored locally in "gameshot.png"
surface3d(xvek,yvek,zmat,coord=c(3,1),texture_s=tms,texture_t=tmt,
lit=F,fog=T,color="white",textype="rgb",texture="gameshot.png",add=T)
Yields this:

Resources