This section goes over the steps required to create a plot, configure it, and add data and graphics to it.
In [ ]:
new Plot(title: "We Will Control the Title", xLabel: "Horizontal", yLabel: "Vertical")
There are multiple ways to specify the points of the line using Groovy. All the following lines achieve the same result.
In [ ]:
// just provide lists of x's and y's
new Plot().add(new Line(x: [0, 1, 2, 3, 4, 5], y: [0, 1, 6, 5, 2, 8]))
// Groovy range works
new Plot().add(new Line(x: (0..5), y: [0, 1, 6, 5, 2, 8]))
// use '<<' left-shift to save some key strokes
new Plot() << new Line(x: (0..5), y: [0, 1, 6, 5, 2, 8])
// the constructor of class Line is overloaded to take 1 or 2 lists
// if an Line was returned, and empty plot is automatically generated
new Line((0..5), [0, 1, 6, 5, 2, 8])
// if x is not provided, a default list of x values (0..5) will be used
new Line([0, 1, 6, 5, 2, 8])
You may change the rendering properties of the lines, by specifying the corresponding parameters. E.g. width, color, style, interpolation, etc.
In [ ]:
def plot = new Plot(title: "Setting line properties")
def ys = [0, 1, 6, 5, 2, 8]
def ys2 = [0, 2, 7, 6, 3, 8]
plot << new Line(y: ys, width: 10, color: Color.red)
plot << new Line(y: ys, width: 3, color: Color.yellow)
plot << new Line(y: ys, width: 4, color: new Color(33, 87, 141), style: StrokeType.DASH, interpolation: 0)
plot << new Line(y: ys2, width: 2, color: new Color(212, 57, 59), style: StrokeType.DOT)
plot << new Line(y: [5, 0], x: [0, 5], style: StrokeType.LONGDASH)
plot << new Line(y: [4, 0], x: [0, 5], style: StrokeType.DASHDOT)
Stems are vertical line segments. All the rendering properties for lines apply to stems.
In [ ]:
def plot = new Plot();
def y1 = [1.5, 1, 6, 5, 2, 8]
def cs = [Color.black, Color.red, Color.gray, Color.green, Color.blue, Color.pink]
def ss = [StrokeType.SOLID, StrokeType.SOLID, StrokeType.DASH, StrokeType.DOT, StrokeType.DASHDOT, StrokeType.LONGDASH]
plot << new Stems(y: y1, color: cs, style: ss, width: 5)
Draw points at the top / bottom of stems to make stem bases
In [ ]:
def plot = new Plot(title: "Setting the base of Stems")
def ys = [3, 5, 2, 3, 7]
def y2s = [2.5, -1.0, 3.5, 2.0, 3.0]
plot << new Stems(y: ys, width: 2, base: y2s)
plot << new Points(y: ys)
You can set the width and color of the bars. You can set the color property for each single bar element using a list Bar colors are fill colors, To change the outline color, use outlineColor. Bar width is in terms of the data domain.
For Bar Charts, see the other tutorial on Category Plots.
In [ ]:
def plot = new Plot(title: "Bars")
def cs = [new Color(255, 0, 0, 128)] * 5 // transparent bars
cs[3] = Color.red // set color of a single bar, solid colored bar
plot << new Bars(x: (1..5), y: [3, 5, 2, 3, 7], color: cs, outlineColor: Color.black, width: 0.3)
You can change the size and shape of points. Points also support outlineColor.
In [ ]:
def plot = new Plot(title: "Changing Point Size, Color, Shape")
def y1 = [6, 7, 12, 11, 8, 14]
def y2 = y1.collect { it - 2 }
def y3 = y2.collect { it - 2 }
def y4 = y3.collect { it - 2 }
plot << new Points(y: y1)
plot << new Points(y: y2, shape: ShapeType.CIRCLE)
plot << new Points(y: y3, size: 8.0, shape: ShapeType.DIAMOND)
plot << new Points(y: y4, size: 12.0, color: Color.orange, outlineColor: Color.red)
You can also set point properties using lists.
In [ ]:
def plot = new Plot(title: "Changing point properties with list")
def cs = [Color.black, Color.red, Color.orange, Color.green, Color.blue, Color.pink]
def ss = [6.0, 9.0, 12.0, 15.0, 18.0, 21.0]
def fs = [false, false, false, true, false, false]
plot << [new Points(y: [5] * 6, size: 12.0, color: cs),
new Points(y: [4] * 6, size: 12.0, color: Color.gray, outlineColor: cs),
new Points(y: [3] * 6, size: ss, color: Color.red),
new Points(y: [2] * 6, size: 12.0, color: Color.black, fill: fs, outlineColor: Color.black)]
In [ ]:
def plot = new Plot()
def y = [3, 5, 2, 3]
def x0 = [0, 1, 2, 3]
def x1 = [3, 4, 5, 8]
plot << new Area(x: x0, y: y)
plot << new Area(x: x1, y: y, color: new Color(128, 128, 128, 50), interpolation: 0)
You can also set bases for the areas.
In [ ]:
def p = new Plot()
p << new Line(y: [3, 6, 12, 24], displayName: "Median")
p << new Area(y: [4, 8, 16, 32], base: [2, 4, 8, 16],
color: new Color(255, 0, 0, 50), displayName: "Q1 to Q3")
You can combine plot items that have a base property (Bars, Stems, Area).
In [ ]:
def y1 = [1,5,3,2,3]
def y2 = [7,2,4,1,3]
def p = new Plot(title: 'Plot with XYStacker', initHeight: 200)
def a1 = new Area(y: y1, displayName: 'y1')
def a2 = new Area(y: y2, displayName: 'y2')
p << XYStacker.stack([a1, a2])
In [ ]:
def p = new Plot ()
p << new Line(y: [-1, 1])
p << new ConstantLine(x: 0.65, style: StrokeType.DOT, color: Color.blue)
p << new ConstantLine(y: 0.1, style: StrokeType.DASHDOT, color: Color.blue)
p << new ConstantLine(x: 0.3, y: 0.4, color: Color.gray, width: 5, showLabel: true)
In [ ]:
new Plot() << new Line(y: [-3, 1, 3, 4, 5]) << new ConstantBand(x: [1, 2], y: [1, 3])
You can change bands colors and use Infinity for values
In [ ]:
def p = new Plot()
p << new Line(x: [-3, 1, 2, 4, 5], y: [4, 2, 6, 1, 5])
p << new ConstantBand(x: [Double.NEGATIVE_INFINITY, 1], color: new Color(128, 128, 128, 50))
p << new ConstantBand(x: [1, 2])
p << new ConstantBand(x: [4, Double.POSITIVE_INFINITY])
In [ ]:
def plot = new Plot()
def xs = (1..10)
def ys = [8.6, 6.1, 7.4, 2.5, 0.4, 0.0, 0.5, 1.7, 8.4, 1]
def label = { i ->
if (ys[i] > ys[i+1] && ys[i] > ys[i-1]) return "max"
if (ys[i] < ys[i+1] && ys[i] < ys[i-1]) return "min"
if (ys[i] > ys[i-1]) return "rising"
if (ys[i] < ys[i-1]) return "falling"
return ""
}
for (i = 0; i < xs.size(); i++) {
if (i > 0 && i < xs.size()-1)
plot << new Text(x: xs[i], y: ys[i], text: label(i), pointerAngle: -i/3.0)
}
plot << new Line(x: xs, y: ys)
plot << new Points(x: xs, y: ys)
The Beaker Plot includes tool tips. Hover the changing points of the line or the bars to see them in the plot below.
You can interact with the tooltips.
The Beaker Plots include a legend box by default. You may drag the legend box around just as you drag the tool tips.
The legend box includes the control panel features that allow you to show / hide data groups by toggling the checkboxes. You may turn off this feature by using an “omitCheckboxes” property (in this case there would be no checkbox to show / hide data).
Use “displayName” to show the data’s legend. The legend will automatically appear when a “displayName” property presents. However, you my turn the legend off explicitly by setting “showLegend”.
If you do not specify the “displayName” property, or the “displayName” is an empty string, the data will not appear in the legend box.
By default the legend is placed in the top-right corner. To change its position you can specify a “legendPosition” property. You can use predefined values (TOP, TOP_LEFT, etc..) or provide an array of coordinates (x, y).
Also you can change legend’s layout to horizontal by setting a “legendLayout” property.
Now it would be a good time for you to try the above interactions in the following demo plot. Make sure you try out the following things:
In [ ]:
def ch = new Crosshair(color: new Color(255, 128, 5), width: 2, style: StrokeType.DOT)
pp = new Plot(crosshair: ch, omitCheckboxes: true,
legendLayout: LegendLayout.HORIZONTAL, legendPosition: LegendPosition.TOP)
def x = [1, 4, 6, 8, 10]
def y = [3, 6, 4, 5, 9]
pp << new Line(displayName: "Line", x: x, y: y, width: 3)
pp << new Bars(displayName: "Bar", x: (1..10), y: [2, 2, 4, 4, 2, 2, 0, 2, 2, 4], width: 0.5)
pp << new Points(x: x, y: y, size: 10, toolTip: {xs, ys -> "x = " + xs + ", y = " + ys })
In [ ]:
pp.setLegendPosition(LegendPosition.RIGHT);
OutputCell.HIDDEN
In [ ]:
import com.twosigma.beakerx.fileloader.CSV
rates = new CSV().read("../resources/data/interest-rates.csv")
def size = rates.size()
(0 ..< size).each{row = rates[it]; row.spread = row.y10 - row.m3}
Using SimpleTimePlot you can create a time plot based on the complex table data. To do this just specify table and table column names for plot, like in the example at the top of this notebook.
Data for time axis is taken from 'time' column. To change this behavior use 'timeColumn' parameter.
By default lines are used to draw the plot, but you can also add points using the parameter 'displayPoints'. The 'displayNames' property give sthe names of the lines, as displayedin the legend. To specify custom colors use the 'colors' parameter, and give it a list with colors in a variety of formats.
In [ ]:
new SimpleTimePlot(rates, ["y1", "y10"], // column names
timeColumn : "time", // time is default value for a timeColumn
yLabel: "Price",
displayNames: ["1 Year", "10 Year"],
colors : [[216, 154, 54], '#aabbcc'],
displayLines: false, // no lines (true by default)
displayPoints: true) // show points (false by default))
The plot can have two y-axes. Just add a YAxis
to the plot object, and specify its label.
Then for data that should be scaled according to this second axis,
specify the property yAxis
with a value that coincides with the label given.
You can use upperMargin
and lowerMargin
to restrict the range of the data leaving more white, perhaps for the data on the other axis.
In [ ]:
def p = new TimePlot(xLabel: "Time", yLabel: "Interest Rates")
p << new YAxis(label: "Spread", upperMargin: 4)
p << new Area(x: rates.time, y: rates.spread, displayName: "Spread",
yAxis: "Spread", color: new Color(180, 50, 50, 128))
p << new Line(x: rates.time, y: rates.m3, displayName: "3 Month")
p << new Line(x: rates.time, y: rates.y10, displayName: "10 Year")
The plots support log scale for both axes, independently. To add log scale you need to specify set the logX (for x-axis) or logY (for y-axis) property to true. By default a base 10 is used. To change this, use properties xLogBase and yLogBase.
In [ ]:
def points = 100;
def logBase = 10;
def expys = [];
def xs = [];
for(int i = 0; i < points; i++){
xs[i] = i / 15.0;
expys[i] = Math.exp(xs[i]);
}
def cplot = new CombinedPlot(xLabel: "Linear");
def logYPlot = new Plot(title: "Linear x, Log y", yLabel: "Log", logY: true, yLogBase: logBase);
logYPlot << new Line(x: xs, y: expys, displayName: "f(x) = exp(x)");
logYPlot << new Line(x: xs, y: xs, displayName: "g(x) = x");
cplot.add(logYPlot, 3);
// works for 2nd Y axis too:
// logYPlot << new YAxis(label: "Right Log Y-Axis", log: true, logBase: logBase);
def linearYPlot = new Plot(title: "Linear x, Linear y", yLabel: "Linear");
linearYPlot << new Line(x: xs, y: expys, displayName: "f(x) = exp(x)");
linearYPlot << new Line(x: xs, y: xs, displayName: "g(x) = x");
cplot.add(linearYPlot, 3);
cplot
In [ ]:
def points = 100;
def logBase = 10;
def expys = [];
def xs = [];
for(int i = 0; i < points; i++){
xs[i] = i /15
expys[i] = Math.exp(xs[i]);
}
def plot = new Plot(title: "Log x, Log y", xLabel: "Log", yLabel: "Log",
logX: true, xLogBase: logBase, logY: true, yLogBase: logBase);
plot << new Line(x: xs, y: expys, displayName: "f(x) = exp(x)");
plot << new Line(x: xs, y: xs, displayName: "f(x) = x");
plot
For Time plots you can provide x coordinate as:
In [ ]:
def cal = Calendar.getInstance();
cal.add(Calendar.HOUR, -1)
def today = new Date();
def millis = today.time;
def hour = 1000 * 60 * 60;
def plot = new TimePlot(
timeZone: new SimpleTimeZone(10800000, "America/New_York")
);
//list of milliseconds
plot << new Points(x:(0..10).collect{millis + hour * it}, y:(0..10), size: 10, displayName: "milliseconds");
//list of java.util.Date objects
plot << new Points(x:(0..10).collect{cal.add(Calendar.HOUR, 1); cal.getTime()}, y:(0..10), size: 4, displayName: "date objects");
Handling time with nanosecond resolution is easy in languages like Java and Groovy because they support 64-bit integers. Numbers in JavaScript however are limited to 53 bits. Beaker's plotting library can handle these large numbers, just use the NanoPlot class.
In [ ]:
def today = new Date()
def millis = today.time
def nanos = millis * 1000 * 1000g // g makes it arbitrary precision
def np = new NanoPlot()
np << new Points(x:(0..10).collect{nanos + 7 * it}, y:(0..10))
You can remove the tick labels from either or both axes.
In [ ]:
p = new Plot(title: "No Tick Labels", xTickLabelsVisible: false, yTickLabelsVisible: false)
p << new Line([0, 1, 6, 5, 2, 8])
And add arbitrary styles to various elements:
In [ ]:
r = new Random()
p = new Plot(title: "Advanced Plot Styling",
labelStyle: "font-size:32px; font-weight: bold; font-family: courier; fill: green;",
gridLineStyle: "stroke: purple; stroke-width: 3;",
titleStyle: "color: green;"
)
p << new Points(x: (1..1000).collect { r.nextGaussian() * 10.0d },
y: (1..1000).collect { r.nextGaussian() * 20.0d })
In [ ]:
import java.nio.file.Files
byte[] picture = Files.readAllBytes(new File("../resources/img/widgetArch.png").toPath());
def p = new Plot();
// x y width height are coordinates, opacity is a double in 0~1
// image can be loaded via bytes, filepath, or url
p << new Rasters(x: [-10,3], y: [3,1.5], width: [6,5], height:[10,8], opacity: [1,0.5], dataString: picture);
//p << new Rasters(x: -1, y: 4.5, width: 5, height: 8, opacity:0.5, filePath: "../resources/img/widgetArch.png");
p << new Rasters(x: [-4], y: [10.5], width: [7], height: [2], opacity:[1], fileUrl: "https://www.twosigma.com/static/img/twosigma.png");
// a list of images!
def x = [-8, -5, -3, -2, -1, 1, 2, 4, 6, 8]
def y = [4, 5, 1, 2, 0 ,3, 6, 4, 5, 9]
def width = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
def opacity = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
p << new Rasters(x: x, y: y, width:width, height:width, opacity:opacity,fileUrl: "http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png")
In [ ]:
def plot = new Plot(title: "Setting 2nd Axis bounds")
def ys = [0, 2, 4, 6, 15, 10]
def ys2 = [-40, 50, 6, 4, 2, 0]
def ys3 = [3, 6, 3, 6, 70, 6]
plot << new YAxis(label:"Spread")
plot << new Line(y: ys)
plot << new Line(y: ys2, yAxis: "Spread")
plot.getYAxes()[0].setBound(1,5);
plot.getYAxes()[1].setBound(3,6) // this should change the bounds of the 2nd, right axis
plot
In [ ]:
new Line(y: [4, 0], x: [0, 5])
In [ ]:
new Plot(yAutoRangeIncludesZero: true) << new Line(y: [5, 10])
In [ ]:
new Plot(xBound: [7, 9], yBound: [6, 9]) << new Line(x: (1..8), y: (1..8)) << new Line(x: (8..10), y: [8, 7, 6])
In [ ]:
new Plot(xBound: [7, 9]) << new Line(x: (1..8), y: (1..8)) << new Line(x: (8..10), y: [8, 7, 6])
In [ ]:
new Plot(yBound: [6, 9]) << new Line(x: (1..8), y: (1..8)) << new Line(x: (8..10), y: [8, 7, 6])
In [ ]:
p = new Plot(title: "Margins",
xLowerMargin: 1, xUpperMargin: 1,
yLowerMargin: 1, yUpperMargin: 1)
p << new Line(x: (1..10), y: (5..14))