Git Product home page Git Product logo

scalaplot's Introduction

Build Status

scalaplot

This is a library for quick and easy plotting of simple plots (such as XY line plots, scatter plots) and supports outputs using different engines (currently Gnuplot and JFreeGraph).

Note: The project is still in beta. If you just need a clean way to interface Java with gnuplot, see gnujavaplot.

Requirements

  • maven
  • for gnuplot: gnuplot 4.6 with pdf support (on the mac+homebrew, brew install pdflib-lite gnuplot)

Installation

Maven dependency

The easiest (and recommended) way to use scalaplot is as a maven dependency. Insert the following in your pom file:

<dependencies>
  ...
  <dependency>
    <groupId>org.sameersingh.scalaplot</groupId>
    <artifactId>scalaplot</artifactId>
    <version>0.0.4</version>
  </dependency>
  ...
</dependencies>

SBT dependency

Or to use scalaplot with SBT, add the following dependency to your build.sbt file:

libraryDependencies += "org.sameersingh.scalaplot" % "scalaplot" % "0.0.4"

Creating Charts

Currently, the library supports line and point (scatter) charts. Let's start with a simple, complete example:

import org.sameersingh.scalaplot.Implicits._

val x = 0.0 until 2.0 * math.Pi by 0.1
output(PNG("docs/img/", "test"), xyChart(x ->(math.sin(_), math.cos(_))))

which produces

Example scalaplot

while

output(ASCII, xyChart(x ->(math.sin(_), math.cos(_))))

produces

    1 BBBB------+--AAAAAA-+---------+--------+---------+---------BBB------++
      +   BB    +AA      AAA        +        +         +       BB+         +
  0.8 ++    BB AA           AA                               BB           ++
      |      AA               A                            BB              |
  0.6 ++    A  BB              A                          B               ++
      |    A     B              AA                       B                 |
  0.4 ++  A       B               A                     B                 ++
      |  A         B               A                   B                   |
  0.2 ++A           B               A                 B                   ++
    0 AA             B               A               B                    ++
      |               B               A             B              A       |
 -0.2 ++               B               A           B              A       ++
      |                 B               A        BB              A         |
 -0.4 ++                 BB              A      B               A         ++
      |                    B             A     B               A           |
 -0.6 ++                    B             AA  B              AA           ++
      |                      B              AB              A              |
 -0.8 ++                      BB           B AA           AA              ++
      +         +         +     BBB +    BB  + AA      +AA       +         +
   -1 ++--------+---------+--------BBBBBB----+---AAAAAAA---------+--------++
      0         1         2         3        4         5         6         7

As another example to introduce a bit of customization:

import org.sameersingh.scalaplot.Implicits._

val x = 0.0 until 10.0 by 0.01
val rnd = new scala.util.Random(0)

output(PNG("docs/img/", "scatter"), xyChart(
  x -> Seq(Y(x, style = XYPlotStyle.Lines),
           Y(x.map(_ + rnd.nextDouble - 0.5), style = XYPlotStyle.Dots))))

produces

Example scatter

Output Formats

The library, of course, supports different output formats. Most of these also produce an accompanying Gnuplot source file, allowing archival and further customization if needed. The current list of formats are:

output(ASCII, xyChart(...)) // returns the string as above
output(SVG, xyChart(...)) // returns the SVG text, which can be embedded in html or saved as a SVG file
output(PDF(dir, name), xyChart(...)) // produces dir/name.gpl as the gnuplot source, and attempts dir/name.pdf
output(PNG(dir, name), xyChart(...)) // produces dir/name.gpl as the gnuplot source, and attempts dir/name.png
output(GUI, xyChart(...)) // opens a window with the plot, which can be modified/exported/resized/etc.

Note that scalaplot calls the gnuplot command to render the image in dir/name.EXT, but in case it fails, do the following:

$ cd dir/
$ gnuplot name.gpl

which will create name.EXT, where EXT is one of PDF or PNG.

XYChart

The xyChart function is the main entry point for creating charts. The first argument of plot requires a XYData object, that we will describe in the next section. The rest of the arguments customize the aspects of the chart that are not data-specific.

val d: XYData = ...
xyChart(d)
xyChart(d, "Chart Title!")
xyChart(d, x = Axis(label = "Age"), y = Axis(log = true))

Here are the relevant definitions and default parameters that you can override:

def xyChart(data: XYData, title: String = "",
            x: NumericAxis = new NumericAxis,
            y: NumericAxis = new NumericAxis,
            pointSize: Option[Double] = None,
            legendPosX: LegendPosX.Type = LegendPosX.Right,
            legendPosY: LegendPosY.Type = LegendPosY.Center,
            showLegend: Boolean = false,
            monochrome: Boolean = false,
            size: Option[(Double, Double)] = None): XYChart
def Axis(label: String = "",
         backward: Boolean = false,
         log: Boolean = false,
         range: Option[(Double, Double)] = None): NumericAxis

Data

The data is the first argument of the plot function, and can be specified in many different ways, depending on the format your data is available in. Primarily, XYData consists of multiple sequences of (Double,Double) pairs, where each sequence forms a single series (line in line plots). Here are some ways of data can be specified.

If you have a single x sequence and multiple y sequences, you can use:

// data
val x = (1 until 100).map(_.toDouble)
val y1 = (1 until 100).map(j => math.pow(j, 1))
val y2 = (1 until 100).map(j => math.pow(j, 2))
val y3 = (1 until 100).map(j => math.pow(j, 3))

xyChart(x ->(y1, y2, y3))
xyChart(x ->(math.sin(_), math.cos(_))) // inline definition
xyChart(x -> Seq(Y(y1, "1"), Y(y2, "2"), Y(y3, "3"))) // with labels and other possible customizations
xyChart(x -> Seq(Yf(math.sin, "sin"), Yf(math.cos, color = Color.Blue), Yf(math.tan, lw = 3.0))) // Yf for functions

where each series can be fully customized using the following:

def Y(yp: Seq[Double],
      label: String = "Label",
      style: XYPlotStyle.Type = XYPlotStyle.LinesPoints,
      color: Option[Color.Type] = None,
      ps: Option[Double] = None,
      pt: Option[PointType.Type] = None,
      lw: Option[Double] = None,
      lt: Option[LineType.Type] = None,
      every: Option[Int] = None)
def Yf(f: Double => Double,
       label: String = "Label",
       style: XYPlotStyle.Type = XYPlotStyle.LinesPoints,
       color: Option[Color.Type] = None,
       ps: Option[Double] = None,
       pt: Option[PointType.Type] = None,
       lw: Option[Double] = None,
       lt: Option[LineType.Type] = None,
       every: Option[Int] = None)

If you have sequences of (x,y) pairs as your data, or if you want to use different x for each series:

xyChart(List(x -> Y(y1), x -> Y(y2)))
xyChart(List(x -> Y(y1, "1"), x -> Y(y2, color = Color.Blue)))

val xy1 = x zip y1
val xy2 = x zip y2
xyChart(List(XY(xy1), XY(xy2)))
xyChart(List(XY(xy1, "1"), XY(xy2, "2")))

where the customization is similar to above:

def XY(points: Seq[(Double, Double)],
       label: String = "Label",
       style: XYPlotStyle.Type = XYPlotStyle.LinesPoints,
       color: Option[Color.Type] = None,
       ps: Option[Double] = None,
       pt: Option[PointType.Type] = None,
       lw: Option[Double] = None,
       lt: Option[LineType.Type] = None,
       every: Option[Int] = None)

Other Implicits

Scalaplot also supports a number of other implicits to make things easier to use.

val d: XYData = x ->(y1, y2, y3)
val c: XYChart = d // automatic conversion from data to chart

// series
val s1: XYSeries = x -> y1
val s2: XYSeries = x zip y2
val f1 = math.sin(_)
val s1f: XYSeries = x -> f1
val s2f: XYSeries = x -> Yf(math.sin)

// series to data
val d1: XYData = s1
val d2: XYData = Seq(s1, s2)
val d2l: XYData = s1 :: s2 :: List()

Explicit Data Structures

For even further customization of the charts, you will need to dive into the API instead of relying on the above implicits.

XY Line Charts

First step is to get your data into Seq[Double].

val x = (1 until 100).map(_.toDouble)
val y = x.map(i => i*i)

Create a dataset that represents these sequences.

val series = new MemXYSeries(x, y, "Square")
val data = new XYData(series)

You can add more series too.

data += new MemXYSeries(x, x.map(i => i*i*i), "Cube")

Let's create the chart.

val chart = new XYChart("Powers!", data)
chart.showLegend = true

Rendering Charts

Even though multiple backends are being supported to render the charts, gnuplot is the most actively developed and supported since it allows post plotting customizations (editing the script files), may possible output formats, and ease of use.

Gnuplot

Generates gnuplot scripts that will need to be run to actually generate the images.

val plotter = new GnuplotPlotter(chart)
plotter.writeToPdf("dir/", "name")

The output looks like

Example gnuplot output

JFreegraph

JFreegraph can also be called similarly to produce pdf plots (use JFGraphPlotter). However, it also supports a gui() option for when you just want to see the graph.

val plotter = new JFGraphPlotter(chart)
plotter.gui()

produces

Example jfreegraph output

scalaplot's People

Contributors

sameersingh avatar nightscape avatar

Stargazers

 avatar Ainur Hakimov  avatar Robert Carcassés Quevedo avatar Andrzej J. Skalski avatar Mikael Zulfigarov avatar Jonas Anso avatar Jeff Hammerbacher avatar Robert Butacu avatar  avatar Andriy Onyshchuk avatar Robbie Nohra avatar Mariusz Wojakowski avatar Petr Grishin avatar Dmitry Parenskiy avatar strobe avatar Matthew avatar Kenny Lu avatar Kaique Silva avatar Vinícius Barros avatar  avatar Josh avatar Dennis Vriend avatar Aaron J. Radke avatar Alexander Slesarenko avatar Aron Trauring avatar Joel Carlson avatar Harrison Klaperman avatar Liang Depeng avatar Shane Delmore avatar  avatar Koki Shibata avatar Tarık Yılmaz avatar WMJ improved avatar Felix Cheung avatar wp.yi' avatar Jonathan Word avatar Yang, Bo avatar Tae Matsumoto avatar  avatar Brian Schlining avatar Sergey Vaytsel avatar Piyush Chauhan avatar Parisa Kordjamshidi avatar  avatar  avatar Yuto Suzuki avatar yutaono avatar Ryoma Kawajiri avatar Jappie Klooster avatar Angus H. avatar Matthew Chu avatar Ilya avatar  avatar Nikita Bukhal avatar Daniel Khashabi avatar Francois-Guillaume Ribreau avatar  avatar kimihiro_n avatar Kirill Klimov avatar Eliza Weisman avatar Geoffroy Couprie avatar JK avatar Jason Chaffee avatar Dawid Dworak avatar Tom Link avatar Piotr Limanowski avatar Alex Gryzlov avatar Fayi FB avatar Damien RAYMOND avatar kzhou avatar  avatar Povilas Skruibis avatar Sebastian Ganslandt avatar Adriano Machado avatar ShiZhan avatar Tin Pavlinic avatar  avatar Zhao Han avatar Peter Lacey-Bordeaux avatar Henry Stevens avatar Vladimir Vishnevskii avatar Ivano Pagano avatar Josef Galea avatar Clark Kampfe avatar Rowland Watkins avatar Choucri FAHED avatar Tongfei Chen avatar Pinglei Guo avatar  avatar  avatar lodsb avatar Yun Yuan avatar Bill Frasure avatar Piotr Kacprzak avatar Simeon H.K. Fitch avatar Mathieu Goeminne avatar Chris Frohoff avatar Mateusz Jaje avatar b3n01t avatar Andrew Bullen avatar

Watchers

David Hall avatar  avatar corey@cn avatar Ryan LeCompte avatar JK avatar Kaique Silva avatar Tim Rocktäschel avatar b3n01t avatar Bill Frasure avatar Piotr Kacprzak avatar  avatar  avatar

scalaplot's Issues

error: not found: value plot

When I run the first example,

import org.sameersingh.scalaplot.Implicits._

val x = 0.0 until 2.0 * math.Pi by 0.1
output(PNG("docs/img/", "test"), plot(x ->(math.sin(), math.cos())))

scala cannot find the plot function.

problem with code example in README.md file

In the Gnuplot section of the README, there is a code example plotter.writeToPdf("dir/", "name"). I don't find any such method. Are you sure that writeToPdf is a method callable on plotter ?

Screenshot 2019-06-22 at 13 38 57

Is it possible to add a listener for closing gui plotting?

I want to view the data in a dataset as follows:

for(data in dataset){
  xxx
  val plotter = new JFGraphPlotter(chart)
  plotter.gui()
}

When I execute it, it just keeps plotting. So is there a way to make it plot next figure after I close the current figure?

GUI mode doesn't support style customizations

Outputting a graph in PNG supports options like color and style, but those same options when applied to GUI mode have no effect. I'm using gnuplot 4.6.6 installed via Homebrew on OS X 10.10.3.

Ambiguous example in README.md file

The README.md file specifies to use the JFGraphPlotter class, but does not indicate how bring it into scope. Do I need to add something to my build.sbt file? or does it require an import from the library already referenced in the build.sbt file?

libraryDependencies += "org.sameersingh.scalaplot" % "scalaplot" % "0.0.4"

Screenshot 2019-06-22 at 13 52 07

Screenshot 2019-06-22 at 13 55 25

sbt Support?

Hey,

This isn't an issue, but I didn't know where else to post this. Do you plan on supporting sbt? Or do you know how to install this through sbt? This looks really nice and I'd like to try it out, but I'd rather not use Maven.

Thanks!

Exception in thread "main" java.lang.NoClassDefFoundError: scala/Product$class

I'm trying to create a plot with scalaplot. If I use the approach using plotter.pdf and plotter.gui it seems to work.

    val plotter = new JFGraphPlotter(chart)
    plotter.pdf(outputDirName, outputFileName)
    plotter.gui()

However If I use the approach suggested in the webpage https://github.com/sameersingh/scalaplot, using

import org.sameersingh.scalaplot.Implicits._
import org.sameersingh.scalaplot._
val x = 0.0 until 10.0 by 0.01
val rnd = new scala.util.Random(0)

output(PNG("/tmp/", "scatter"), xyChart(
  x -> Seq(Y(x, style = XYPlotStyle.Lines),
           Y(x.map(_ + rnd.nextDouble - 0.5), style = XYPlotStyle.Dots))))

I get a java error.

java.lang.NoClassDefFoundError: scala/Product$class
at org.sameersingh.scalaplot.PlotterImplicits$PNG.<init>(Plotter.scala:27)
... 30 elided
Caused by: java.lang.ClassNotFoundException: scala.Product$class
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 31 more

New color type: "RGB" (for custom colors)

First off, nifty project!

Gnuplot internally supports RGB color specs (not just named colors). It would be nice if scalaplot provided access to it (e.g., by implementing Color as a case class rather than an enum).

Improve API using implicits

Need to make the API much cleaner for simple graphs.

Desired API

// plotting
val p1 = plot(xs -> ys)
val p2 = plot(xys, "Title!")
val p3 = plot(("x" -> xs, "y" -> Seq(y1s, y2s)), "Title!")
val p4 = plot(xs -> Seq(sin, cos, tan, x => x*x))
// output
output(PDF("dir/", "test"), plot(xs -> ys))
output(GUI, p1)

Needs to have three components (does this correspond to ggplot2?):

  • Data
  • Chart
  • Output

Data

Currently, consider only datasets that are X and Y coordinates:

val d1: XYData = xs -> ys
val d2: XYData = xys
val d3: XYData = xs -> Seq(y1s, y2s, y3s)
val d3: XYData = xs -> Seq(f1, f2, f3) // f1, f2, f3 are Double => Double
val d4: XYData = xs -> Seq("y1" -> y1s, "y2" -> y2s, "y3" -> y3s)
val d5: XYData = Seq("x" -> xs, "y" -> Seq("y1" -> y1s, "y2" -> y2s))
val d6: XYData = Pair("x" -> xs, "y" -> Seq(y1s, y2s))

For more details, use a series descriptor:

val d1: XYData = xs -> Seq(series(y1s), series(y2s), series(y3s))
val d2: XYData = xs -> Seq(series(y1s, "y1", lt = "Line"), series(y1s, "y1", lt = "Points")
val d3: XYData = Seq("x" -> xs, "y" -> Seq(series(y1s, "y1"), series(y2s, "y2")))

For even more details, use a data descriptor:

val d: XYData = data(xs -> ys, showLegend=true, legendPosX = "Left", legendPosX = "Right")

Chart

val d: Data = ...
val c1 = plot(d)
val c2 = plot(d, "Chart Title")

Output

val c: Chart = ...
output(PDF("dir/", "chart"), c)
output(SVG("dir/test.svg", 3 -> 2), c)
output(GUI, c)

Escape Series Name

For example, if a series name contains an '_', it's a problem for Latex used to render it in legend, but escaping it by '_' is a problem when writing to file with that name.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.