FnuPlot


Getting started with FnuPlot

FnuPlot is a lightweight wrapper for the gnuplot charting and visualization library. Since gnuplot is cross-platform (works on Mac, Linux and Windows), you can use this library on all of the platforms mentioned above.

FnuPlot provides two features on top of gnuplot. First, it hides the gnuplot process, so you do not have to start and control the process; you can use the GnuPlot type and call it using SendCommand. Second, FnuPlot implements a simple domain-specific language for building a number of common chart types.

Installing and configuring FnuPlot

  • Install gnuplot if you haven't already.

  • The library is available as FnuPlot on NuGet. To get the code also, get the code from GitHub.

  • Alternatively: Because FnuPlot is implemented in a single source file, you can instead just copy the FnuPlot.fs file into your project. A robust way to do this is to use Paket for managing your references and use GitHub reference specified in your paket.dependencies file using github fsprojects/FnuPlot src/FnuPlot/FnuPlot.fs.

  • Once you have the reference, you can either add FnuPlot.dll to your reference (when using a compiled project) or use #load "FnuPlot.fs" to load the FnuPlot file obtained using Paket. Alternatively, you can use #r if you're referencing FnuPlot using NuGet package:
1: 
2: 
3: 
4: 
#r "FnuPlot.dll"
open FnuPlot
open System
open System.Drawing

The access to gnuplot is managed through the GnuPlot type. When creating an instance, you can specify the path to the gnuplot executable. If you do not pass a path explicitly, then FnuPlot assumes that you have gnuplot in your PATH variable:

1: 
2: 
let gp_default = new GnuPlot()
let gp = new GnuPlot(path)

The Set method on the GnuPlot object provides a way to configure gnuplot. Here, we specify that plots should appear in a new window (using X11) and we also specify the default font:

1: 
gp.Set(output = Output(X11, font="arial"))

Plotting functions and line charts

The Plot method provided by GnuPlot has a number of overloads. You can specify a function to be drawn (as a string), you can specify a single data series or you can specify multiple series (which can also use different chart types).

The following shows how to call Plot with a function specified as a string:

1: 
gp.Plot(Series.Lines "sin(x)")
Sin function

This creates a chart with a single series, created (implicitly) by specifying the function as a string. If you want to create other kinds of series, you need to use the Series type. The following uses Series.Lines to create a single line chart from a specified list of data points:

1: 
gp.Plot(Series.Lines [2.0; 1.0; 2.0; 5.0]) 
Line series

The Series.Lines method takes a number of optional parameters that you can use to specify the title, colour and other properties of the line chart. You can also make the call using the pipelining operator:

1: 
2: 
3: 
4: 
Series.Lines
  ( title="Some plot", lineColor = Color.OliveDrab, 
    weight = 3, data = [2.0; 1.0; 2.0; 5.0])
|> gp.Plot
Line series with configuration

When creating a line series from data points consisting of both X and Y values, you can also use the Series.Lines method with a series of that type (note that FnuPlot also supports time-series data sets, which are discussed below).

1: 
2: 
Series.Lines( [0.0,1.0; 0.2,2.0; 2.0,1.5; 2.1,3.0] , title = "Some xy plot")
|> gp.Plot
Line series with X and Y values

Plotting histograms and time-series

Another kind of chart for which FnuPlot provides an easy-to-use wrapper is histogram. This can be created using the Series.Histogram function. Again, we can specify additional properties using a number of optional parameters:

1: 
2: 
3: 
4: 
Series.Histogram
  ( [2.0; 5.0; 2.0; 1.0], title = "Some plot", fill = Solid, 
    lineColor = Color.SteelBlue, weight = 3) 
|> gp.Plot
Histogram

As already mentioned, FnuPlot also supports time-series charts. These can be created by passing values as pairs of DateTime and float:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let values =
  [ DateTime(2014,1,1), 1.0
    DateTime(2014,1,5), 3.0
    DateTime(2014,2,1), 1.5 ]

Series.Lines(values, title = "Some time series" )
|> gp.Plot
Time-series

Combining multiple chart series

As mentioned earlier, the Plot function has a number of overloads. We have already seen an overload that takes a string (to plot a specified function) and an overload that takes a single series. However, you can also call Plot with a collection of series. In that case, gnuplot will render multiple series into a single chart.

The following (slightly silly) demo combines the sin(x*3)+3) function specified as a string together with line chart and histogram created from values:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
[ Series.Lines
    ( "sin(x*3) + 3", title = "Sinus", 
      lineColor = Color.Goldenrod, weight = 3)
  Series.Lines
    ( [2.0; 1.0; 2.0; 5.0], title = "Line", 
      lineColor = Color.OliveDrab, weight = 3)
  Series.Histogram
    ( [2.0; 5.0; 2.0; 1.0], title = "Hist", fill = Solid, 
      lineColor = Color.SteelBlue, weight = 3) ]
|> gp.Plot
Combining multiple charts

Configuring ranges and styles

So far, we always specified properties of individual chart series locally using optional parameters of a Series.<Some> method. However, the gp.Plot method also takes a number of additional parameters that can be used to specify properties of the whole chart. Most importantly, you can use these to specify ranges:

1: 
2: 
3: 
gp.Plot(
  range = RangeY.[-10.0 .. 10.0 ],
  data = Series.Lines [2.0; 1.0; 2.0; 5.0])  
Combining multiple charts

To specify a range, you can use RangeY (as shown in the code snippet) or RangeX to set the range for one of the two axes. If you want to configure both ranges, you can use the Range type. For example, to specify X axis range from -10 to 10 and Y axis range from -1 to 1, you can write Range.[-10.0 .. 10.0, -1.0 .. 1.0].

The next example shows how to specify fill style for the whole chart, so that you do not have to repeat this for every single histogram that is being combined:

1: 
2: 
3: 
4: 
5: 
6: 
gp.Plot(
  style = Style(fill=Solid),
  range = Range.[-0.5 .. 3.7, 0.0 .. 6.0],
  data = 
    [ Series.Histogram([2.0; 1.0; 2.0; 5.0], lineColor = Color.OliveDrab)
      Series.Histogram([1.5; 2.0; 2.5; 4.5], lineColor = Color.SteelBlue) ])
Combining multiple charts

Saving charts to a file

At the beginning of the tutorial, we used the gp.Set method to specify the output kind for gnuplot. We used Output(X11, font="arial") to use the X11 server, which means that all charts are created in a gnuplot window.

If you want to save charts to a file, then you can use the Png output. The following (slightly longer) example also demonstrates other optional parameters of the gp.Set method. This gives you another way to specify the range and style - this time, the configuration will apply to all created charts:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
// Set global properties using the 'Set' method
gp.Set(style = Style(fill = Solid), range = Range.[-0.5 .. 3.7, 0.0 .. 6.0])
// Specify the 'Png' output kind
gp.Set(output = Output(Png("/home/tomas/Temp/test1.png")))

// Create a plot (which will be saved to a file)
gp.Plot
  [ Series.Histogram([2.0; 1.0; 2.0; 5.0], lineColor = Color.OliveDrab)
    Series.Histogram([1.5; 2.0; 2.5; 4.5], lineColor = Color.SteelBlue) ]

// Reset the configuration back to X11 plots
gp.Set(output = Output(X11, font="arial"))

Impulses and Points

Using the Series.Impulses and Series.Points methods we can plot impulses and points.

1: 
2: 
3: 
[ Series.Impulses( "besj0(x)*0.12e1", title = "Plot as Impulses")
  Series.Points( "(x**besj0(x))-2.5", title = "Plot as Points")]
|> gp.Plot
Impulses and Points
val pathOpt : string option

Full name: Tutorial.pathOpt
module Seq

from Microsoft.FSharp.Collections
val tryFind : predicate:('T -> bool) -> source:seq<'T> -> 'T option

Full name: Microsoft.FSharp.Collections.Seq.tryFind
namespace System
namespace System.IO
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  static member Encrypt : path:string -> unit
  static member Exists : path:string -> bool
  ...

Full name: System.IO.File
System.IO.File.Exists(path: string) : bool
val path : string

Full name: Tutorial.path
val defaultArg : arg:'T option -> defaultValue:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.defaultArg
namespace FnuPlot
namespace System.Drawing
val gp_default : GnuPlot

Full name: Tutorial.gp_default
Multiple items
type GnuPlot =
  interface IDisposable
  new : ?path:string -> GnuPlot
  private new : actualPath:string -> GnuPlot
  member private Dispose : disposing:bool -> unit
  override Finalize : unit -> unit
  member Plot : data:seq<Series> * ?style:Style * ?range:Range * ?output:Output * ?titles:Titles -> unit
  member Plot : data:Series * ?style:Style * ?range:Range * ?output:Output * ?titles:Titles -> unit
  member Plot : func:string * ?style:Style * ?range:Range * ?output:Output * ?titles:Titles -> unit
  member SendCommand : str:string -> unit
  member Set : ?style:Style * ?range:Range * ?output:Output * ?titles:Titles * ?TimeFormatX:TimeFormatX -> unit
  ...

Full name: FnuPlot.GnuPlot

--------------------
new : ?path:string -> GnuPlot
val gp : GnuPlot

Full name: Tutorial.gp
member GnuPlot.Set : ?style:Style * ?range:Internal.Range * ?output:Output * ?titles:Titles * ?TimeFormatX:TimeFormatX -> unit
Multiple items
type Output =
  interface ICommand
  new : output:OutputType * ?font:string -> Output

Full name: FnuPlot.Output

--------------------
new : output:OutputType * ?font:string -> Output
union case OutputType.X11: OutputType
member GnuPlot.Plot : data:seq<Series> * ?style:Style * ?range:Internal.Range * ?output:Output * ?titles:Titles -> unit
member GnuPlot.Plot : data:Series * ?style:Style * ?range:Internal.Range * ?output:Output * ?titles:Titles -> unit
member GnuPlot.Plot : func:string * ?style:Style * ?range:Internal.Range * ?output:Output * ?titles:Titles -> unit
Multiple items
type Series =
  new : plot:SeriesType * data:Data * ?title:string * ?lineColor:Color * ?weight:int * ?fill:FillStyle -> Series
  member Command : string
  member Data : Data
  static member Histogram : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int * ?fill:FillStyle -> Series
  static member Impulses : data:seq<DateTime * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
  static member Impulses : data:string * ?title:string * ?lineColor:Color * ?weight:int -> Series
  static member Impulses : data:seq<float * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
  static member Impulses : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
  static member Lines : data:seq<DateTime * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
  static member Lines : data:string * ?title:string * ?lineColor:Color * ?weight:int -> Series
  ...

Full name: FnuPlot.Series

--------------------
new : plot:SeriesType * data:Data * ?title:string * ?lineColor:Color * ?weight:int * ?fill:FillStyle -> Series
static member Series.Lines : data:seq<DateTime * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Lines : data:string * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Lines : data:seq<float * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Lines : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
type Color =
  struct
    member A : byte
    member B : byte
    member Equals : obj:obj -> bool
    member G : byte
    member GetBrightness : unit -> float32
    member GetHashCode : unit -> int
    member GetHue : unit -> float32
    member GetSaturation : unit -> float32
    member IsEmpty : bool
    member IsKnownColor : bool
    ...
  end

Full name: System.Drawing.Color
property Color.OliveDrab: Color
static member Series.Histogram : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int * ?fill:FillStyle -> Series
union case FillStyle.Solid: FillStyle
property Color.SteelBlue: Color
val values : (DateTime * float) list

Full name: Tutorial.values
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
DateTime()
   (+0 other overloads)
DateTime(ticks: int64) : unit
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
   (+0 other overloads)
property Color.Goldenrod: Color
val RangeY : Internal.RangeImplY

Full name: FnuPlot.Ranges.RangeY
Multiple items
type Style =
  interface ICommand
  new : ?fill:FillStyle -> Style

Full name: FnuPlot.Style

--------------------
new : ?fill:FillStyle -> Style
val Range : Internal.RangeImplXY

Full name: FnuPlot.Ranges.Range
union case OutputType.Png: string -> OutputType
static member Series.Impulses : data:seq<DateTime * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Impulses : data:string * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Impulses : data:seq<float * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Impulses : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Points : data:seq<DateTime * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Points : data:string * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Points : data:seq<float * float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
static member Series.Points : data:seq<float> * ?title:string * ?lineColor:Color * ?weight:int -> Series
Fork me on GitHub