I’d like to mention also the not so Frequently Asked Questions, it’s a great resource about gnuplot.
No, it can’t. And the reason is that there would be many details in the plot (such as the individual color lines, point symbols, etc… ) which would hardly be customizable, while Gnuplot.jl aims to provide fine control over all the details of a plot.
Still, you may implement a simple quicklook function, in the form of a plot recipe.
NOTE: these are not yet available in the released version!
But if you download the master
branch (]dev Gnuplot
) you may try the following code:
using RDatasets, DataFrames, Gnuplot
import Gnuplot: PlotRecipe, DataSetText
function plotdf(df::DataFrame, colx::Symbol, coly::Symbol; group=nothing)
if isnothing(group)
return PlotRecipe(data=DataSetText(df[:, colx], df[:, coly]),
plot="w p notit",
xlab=string(colx), ylab=string(coly))
end
data = Vector{Gnuplot.DataSet}()
plot = Vector{String}()
for g in sort(unique(df[:, group]))
i = findall(df[:, group] .== g)
if length(i) > 0
push!(data, DataSetText(df[i, colx], df[i, coly]))
push!(plot, "w p t '$g'")
end
end
return PlotRecipe(data=data, plot=plot,
xlab=string(colx), ylab=string(coly))
end
The recipe can be used as follows:
iris = dataset("datasets", "iris")
@gp plotdf(iris, :SepalLength, :SepalWidth)
@gp plotdf(iris, :SepalLength, :SepalWidth, group=:Species)
And the output is:
Wow - That’s very cool! Thanks - will try it!
Although a solution with a recipe is elegant and clean, it’s not hard to do it without one. I added 4 examples in the gallery page.
- Iris data set
- cars
- gapminder
- flights [the one you asked for]
using HTTP, CSV, DataFrames, Gnuplot, Random
using ColorSchemes, Colors, DataFramesMeta
url = "https://raw.githubusercontent.com/alstat/Julia-Data-Query/master/data-raw/flights.csv"
data = HTTP.download(url)
flights = CSV.read(data)
# @df flights plot(:Day, :NumFlights, group=:City)
#this is all the help you need
daily = groupby(flights, [:day, :dest]) # :origin, only 3 (boring)
per_day = @based_on(daily, flights = length(:day))
byCat = per_day.dest
categ = unique(byCat)
# aesthetics
@gp xlab ="day" ylab = "number of flights" "set auto fix"
@gp :- title = "Flights" :-
@gp :- "set offsets graph .05, graph .05, graph .05, graph .05" :-
@gp :- "set key outside font ',8' title 'destination'" :-
@gp :- "set logscale y 2" yrange = (0.8,700)
cmap = get(colorschemes[:rainbow1], LinRange(0,1,length(categ)))
# the actual plot
Random.seed!(213)
for (i,c) in enumerate(categ)
indc = findall(x->x == c, byCat)
x, y = per_day.day[indc], per_day.flights[indc]
@gp :- x y "w linespoints tit '$(c)' lc '#B3$(hex(cmap[i]))' pt $(rand(5:2:13)) ps 0.5"
lx = length(x)
rlx = rand(1:lx)
@gp :- "set label '$(c)' at $(x[rlx]-0.4),$(y[rlx]) font ',5' front"
end
save(term ="pdf size 4,5", output ="flights_ptslines.pdf")
All the best, and looking forward for the recipe proposal from @gcalderone.
Thanks! Those are fantastic examples! – Adam
Thanks again for the all the examples! I was trying to get plots to appear in a Jupyter Notebook. I wrote a very yucky way to do it. The issue is that @gp
, @gps
don’t return anything. An object needs to be returned and Base.show
overloaded to display it. Here’s what I did,
# We don't want the qt terminal, as it steals the window focus after displaying a plot
@gp "set term unknown"
# We need an object that will represent a plot
struct AGnuPlot
fileName::String
end
# IJulia will call this function to display AGnuPlot
import Base.show
function Base.show(io::IO, ::MIME"image/svg+xml", x::AGnuPlot)
# Read in the file
write(io, read(x.fileName, String))
end
# Function to trigger IJulia into showing the plot
# We'll write the current plot to a svg file. Once AGnuPlot is returned, IJulia will run Base.show on it
# and the plot should appear
const gnuplotout_file = "/tmp/gnuplotout"
function p()
save(term="svg", output=gnuplotout_file) # Writing to a file is yucky, but it works
return AGnuPlot(gnuplotout_file)
end
@gp "p sin(x)" ; p()
Maybe that’s helpful. Perhaps integration with IJulia, Juno, VSCode is on your roadmap.
Thanks again for this great package!
Thanks for working this out!
I’ll think about it and try to found a nice way to include it in the next version.
I made a PR for it: https://github.com/gcalderone/Gnuplot.jl/pull/17
Thanks!!! This allows me to send plot with data to my colleague, who does not need Julia to view and/or post-process the plot, which is a nice feature.
One minor question: is there a way to easily suppress the printout of messages from Gnuplot process unless there is a critical error, while not affecting other printing (i.e. not a workaround of redirecting the stdout)?
If I make a package that uses Gnuplot for plotting, I prefer not to show a lot of messages unless it is necessary (e.g. when something did go wrong and the plot wouldn’t work). Is this achievable by changing a setting somewhere, either in Gnuplot.jl or Gnuplot software itself?
Thanks!
No message should be displayed on stdout unless:
- gnuplot raised an error;
- you set
Gnuplot.options.verbose = true
.
If you still see messages please open an issue on the repo describing how to replicate, and the messages you get.
Thanks! It works.
Dear all,
this is a small update to announce that a new version of Gnuplot.jl (v1.2.0) is now available, introducing the following functionalities:
- A gnuplot REPL is now available (#14);
- Plots are now automatically displayed in Jupyter and Juno (#17);
- The plot recipe mechanism has been implemented;
Full list of changes is available in the ChangeLog.
Enjoy!
Could someone post a simple example showing fundamental differences between packages Gnuplot
and Plots
?
I’m afraid simple examples don’t tell much, i.e.:
using Gnuplot, Plots
x = 1:10; y = rand(10);
@gp x y
plot(x, y)
On the other hand, it is very difficult to envisage complex examples of sufficiently general interest, able to discriminate against one or the other.
IMHO, the best approach is to try to solve a specific problem (e.g. here).
Or have a look at the example galleries
Specific resources for Gnuplot.jl are here and here, but consider that any other gnuplot example (e.g. the official demos) can be reproduced from Julia.
The most fundamental difference is: gnuplot is old, hence mature. The oldest reference I found with a quick search is from 1992 (gnuplot for Windows : Free Download, Borrow, and Streaming : Internet Archive)
Julia is young, Plots.jl is even younger and even most (or none? didn’t check.) of the backends of Plots.jl didn’t exist when gnuplot was already old And I remember using it as a student on a silicon graphics machine (hmmm, must find what it was later) in between some time not playing mtrek (online) by Chuck L. Peterson .
I know, that wasn’t the question, but I like to mention it anyways.
Correct, but is also actively maintained (current stable version released on Dec. 2019).
Right. This is an important note I forgot to mention!
In case someone else runs into this annoying problem… When I save(term="pngcairo", output="out.png")
or with term="pdfcairo"
, I get a mangled output with all text kinda printed on top of each other. Looks very ugly. This is apparently a “feature” in pango
(the library that cairo
uses for text). The latest version v1.44 no longer supports Freetype fonts (see Pango future directions | Goings on). You can repair this by downgrading your version of pango
to v1.43. For the Mac, see macos - Gnuplot PDF Terminal Exhibits Font Issues on Mac - Stack Overflow for instructions.
Does anyone know a good way to deal with plotting things against dates? They only way I’ve figured out how to do it is with a named dataset. For example,
dates = [ Date(2020, 4, floor.(Int, x .* 30 .+ 1)) for x in rand(10) ]
df = DataFrame(Date=dates, val=rand(10))
name = "\$thedata"
@gp name => ( string.(df.Date), df.val)
@gp :- "set xdata time" :-
@gp :- "set format x '%Y-%m-%d'" "set xtics rotate by -45" :-
@gp :- "set rmargin 7" :-
@gp :- "p $name u (strptime('%Y-%m-%d', strcol(1))):2 w p notitl"
Is there a way to do this without having to make a named dataset? Thanks! – Adam
Yes, simply use the data in the @gp
call, e.g.:
using Gnuplot, Dates
dates = [ Date(2020, 4, floor.(Int, x .* 30 .+ 1)) for x in rand(10) ]
val=rand(10)
@gp "set xdata time" "set timefmt '\"%Y-%m-%d\"'" :-
@gp :- "set format x '%Y-%m-%d'" "set xtics rotate by -45" :-
@gp :- "set rmargin 7" :-
@gp :- string.(dates) val "u 1:2 w p notitl"
Note that you can specify a global time format for reading with set timefmt
. Also, Gnuplot.jl writes string within double quotes, hence you need to specify those as well in timefmt
Finally, note that when plotting time/data the using
clause is mandatory, although in this case it simply amounts to using 1:2
.