package umontreal.iro.lecuyer.charts;
import umontreal.iro.lecuyer.stat.TallyStore;
import umontreal.iro.lecuyer.util.Num;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.statistics.HistogramBin;
import cern.colt.list.DoubleArrayList;
import java.util.Locale;
import java.util.Formatter;
import java.util.List;
import java.util.ListIterator;
import java.awt.Color;
/**
* Stores data used in a HistogramChart. HistogramSeriesCollection provides complementary tools to draw histograms.
* One may add observation sets, define histogram bins, set plot color and plot style,
* enable/disable filled shapes, and set margin between shapes for each series.
* This class is linked with class CustomHistogramDataset to store data plots,
* and linked with JFreeChart XYBarRenderer to render the plot.
* CustomHistogramDataset has been developed at the Université de Montréal to extend the JFreeChart API,
* and is used to manage histogram datasets in a JFreeChart chart.
*
*/
public class HistogramSeriesCollection extends SSJXYSeriesCollection {
protected boolean[] filled; // fill flag for each series
protected double[] lineWidth; // sets line width
protected int numBin = 20; // default number of bins
private int calcNumBins (int n) {
// set the number of bins
int numbins = (int) Math.ceil (1.0 + Num.log2(n));
if (n > 5000)
numbins *= 2;
return numbins;
}
/**
* Creates a new HistogramSeriesCollection instance with empty dataset.
*
*/
public HistogramSeriesCollection() {
renderer = new XYBarRenderer();
seriesCollection = new CustomHistogramDataset();
}
/**
* Creates a new HistogramSeriesCollection instance with given data series.
* Bins the elements of data in equally spaced containers (the number of bins
* can be changed using the method {@link #setBins setBins}).
* Each input parameter represents a data series.
*
* @param data series of point sets.
*
*
*/
public HistogramSeriesCollection (double[]... data) {
seriesCollection = new CustomHistogramDataset();
renderer = new XYBarRenderer();
CustomHistogramDataset tempSeriesCollection =
(CustomHistogramDataset)seriesCollection;
for (int i = 0; i < data.length; i ++) {
numBin = calcNumBins(data[i].length);
tempSeriesCollection.addSeries(i, data[i], numBin);
}
// set default colors
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
renderer.setSeriesPaint(i, getDefaultColor(i));
}
// set default plot style
filled = new boolean[seriesCollection.getSeriesCount()];
lineWidth = new double[seriesCollection.getSeriesCount()];
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
filled[i] = false;
lineWidth[i] = 0.5;
setFilled(i, false);
}
}
/**
* Creates a new HistogramSeriesCollection instance with the given data
* series data. Bins the elements of data in equally spaced containers
* (the number of bins can be changed using the method {@link #setBins setBins}).
* Only the first numPoints of data will be taken into account.
*
* @param data Point set
*
* @param numPoints Number of points to plot
*
*
*/
public HistogramSeriesCollection (double[] data, int numPoints) {
seriesCollection = new CustomHistogramDataset();
renderer = new XYBarRenderer();
CustomHistogramDataset tempSeriesCollection =
(CustomHistogramDataset)seriesCollection;
numBin = calcNumBins(numPoints);
tempSeriesCollection.addSeries(0, data, numPoints, numBin);
// set default colors
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
renderer.setSeriesPaint(i, getDefaultColor(i));
}
// set default plot style
filled = new boolean[seriesCollection.getSeriesCount()];
lineWidth = new double[seriesCollection.getSeriesCount()];
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
filled[i] = false;
lineWidth[i] = 0.5;
setFilled(i, false);
}
}
/**
* Creates a new HistogramSeriesCollection.
* Bins the elements of data in equally spaced containers (the number of bins
* can be changed using the method {@link #setBins setBins}).
* The input parameter represents a set of data plots. Each
* {@link cern.colt.list.DoubleArrayList DoubleArrayList} variable corresponds
* to a histogram on the chart.
*
* @param data series of observation sets.
*
*
*/
public HistogramSeriesCollection (DoubleArrayList... data) {
seriesCollection = new CustomHistogramDataset();
renderer = new XYBarRenderer();
CustomHistogramDataset tempSeriesCollection =
(CustomHistogramDataset)seriesCollection;
for (int i = 0; i < data.length; i++) {
numBin = calcNumBins(data[i].size());
tempSeriesCollection.addSeries(i, data[i].elements(), data[i].size(), numBin);
}
// set default colors
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
renderer.setSeriesPaint(i, getDefaultColor(i));
}
// set default plot style
filled = new boolean[seriesCollection.getSeriesCount()];
lineWidth = new double[seriesCollection.getSeriesCount()];
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
filled[i] = false;
lineWidth[i] = 0.5;
setFilled(i, false);
}
}
/**
* Creates a new HistogramSeriesCollection instance with default
* parameters and given data. The input parameter represents a collection of
* data observation sets. Each TallyStore input parameter represents
* an observation set.
*
* @param tallies series of point sets.
*
*
*/
public HistogramSeriesCollection (TallyStore... tallies) {
seriesCollection = new CustomHistogramDataset();
renderer = new XYBarRenderer();
CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
double h;
for (int i = 0; i < tallies.length; i++) {
// Scott's formula
h = 3.5*tallies[i].standardDeviation() /
Math.pow(tallies[i].numberObs(), 1.0/3.0);
numBin = (int) ((tallies[i].max() - tallies[i].min()) / (1.5*h));
tempSeriesCollection.addSeries (i, tallies[i].getArray(),
tallies[i].numberObs(), numBin, tallies[i].min(), tallies[i].max());
}
// set default colors
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
renderer.setSeriesPaint(i, getDefaultColor(i));
}
// set default plot style
filled = new boolean[seriesCollection.getSeriesCount()];
lineWidth = new double[seriesCollection.getSeriesCount()];
for (int i = 0; i < tempSeriesCollection.getSeriesCount(); i++) {
filled[i] = false;
lineWidth[i] = 0.5;
setFilled(i, false);
}
}
/**
* Creates a new HistogramSeriesCollection instance.
* The input parameter represents a set of plotting data.
* Each series of the given collection corresponds to a histogram.
*
* @param data series of point sets.
*
*/
public HistogramSeriesCollection (CustomHistogramDataset data) {
renderer = new XYBarRenderer();
seriesCollection = data;
// set default colors
for (int i = 0; i < data.getSeriesCount(); i++) {
renderer.setSeriesPaint(i, getDefaultColor(i));
}
// set default plot style
filled = new boolean[seriesCollection.getSeriesCount()];
lineWidth = new double[seriesCollection.getSeriesCount()];
for (int i = 0; i < data.getSeriesCount(); i++) {
filled[i] = false;
lineWidth[i] = 0.5;
setFilled(i, false);
}
}
/**
* Adds a data series into the series collection.
*
* @param data new series.
*
* @return Integer that represent the new point set.
*
*/
public int add (double[] data) {
return add (data, data.length);
}
/**
* Adds a data series into the series collection. Only the first
* numPoints of data will be added to the new series.
*
* @param data new series.
*
* @param numPoints Number of points to add
*
* @return Integer that represent the new point set.
*
*/
public int add (double[] data, int numPoints) {
CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
tempSeriesCollection.addSeries(tempSeriesCollection.getSeriesCount(),
data, numPoints, numBin);
boolean[] newFilled = new boolean[seriesCollection.getSeriesCount()];
double[] newLineWidth = new double[seriesCollection.getSeriesCount()];
// color
int j = seriesCollection.getSeriesCount() - 1;
renderer.setSeriesPaint(j, getDefaultColor(j));
newFilled[j] = false;
newLineWidth[j] = 0.5;
for (j = 0; j < seriesCollection.getSeriesCount() - 1; j++) {
newFilled[j] = filled[j];
newLineWidth[j] = lineWidth[j];
}
filled = newFilled;
lineWidth = newLineWidth;
return seriesCollection.getSeriesCount() - 1;
}
/**
* Adds a data series into the series collection.
*
* @param observationSet new series values.
*
* @return Integer that represent the new point set's position in the CustomHistogramDataset object.
*
*/
public int add (DoubleArrayList observationSet) {
return add(observationSet.elements(), observationSet.size());
}
/**
* Adds a data series into the series collection.
*
* @param tally {@link umontreal.iro.lecuyer.stat.TallyStore TallyStore} to add values.
*
* @return Integer that represent the new point set's position in the CustomHistogramDataset object.
*
*/
public int add (TallyStore tally) {
return add(tally.getArray(), tally.numberObs());
}
/**
* Returns the CustomHistogramDataset object associated with the current variable.
*
* @return CustomHistogramDataset object associated with the current variable.
*
*/
public CustomHistogramDataset getSeriesCollection() {
return (CustomHistogramDataset)seriesCollection;
}
/**
* Returns the bins for a series.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @return A list of bins.
* IndexOutOfBoundsExceptionif series is outside the specified range.
*
*/
public List getBins (int series) {
return ((CustomHistogramDataset)seriesCollection).getBins(series);
}
/**
* Sets bins periodic bins from the observation minimum values to the observation maximum value for a series.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @param bins the number of periodic bins.
*
* IndexOutOfBoundsExceptionif series is outside the specified range.
*
*/
public void setBins (int series, int bins) {
((CustomHistogramDataset)seriesCollection).setBins(series, bins);
}
/**
* Sets bins periodic bins from minimum to maximum for a series.
* Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @param bins the number of periodic bins.
*
* @param minimum minimum value.
*
* @param maximum maximum value.
*
* IndexOutOfBoundsExceptionif series is outside the specified range.
*
*/
public void setBins (int series, int bins, double minimum, double maximum) {
((CustomHistogramDataset)seriesCollection).setBins(series, bins, minimum, maximum);
}
/**
* Links bins given by table binsTable to a series.
* Values falling on the boundary of adjacent bins will be assigned to the higher indexed bin.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @param binsTable new bins table.
*
* IndexOutOfBoundsExceptionif series is outside the specified range.
*
*/
public void setBins (int series, HistogramBin[] binsTable) {
((CustomHistogramDataset)seriesCollection).setBins(series, binsTable);
}
/**
* Returns the values for a series.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @return A list of values.
* (IndexOutOfBoundsExceptionif <code>series</code> is outside the specified range.
*
*/
public List getValuesList (int series) {
return ((CustomHistogramDataset)seriesCollection).getValuesList(series);
}
/**
* Returns the values for a series.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @return A list of values.
* (IndexOutOfBoundsExceptionif <code>series</code> is outside the specified range.
*
*/
public double[] getValues (int series) {
return ((CustomHistogramDataset)seriesCollection).getValues(series);
}
/**
* Sets a new values set to a series from a List variable.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @param valuesList new values list.
*
*
*/
public void setValues (int series, List valuesList) {
((CustomHistogramDataset)seriesCollection).setValues(series, valuesList);
}
/**
* Sets a new values set to a series from a table.
*
* @param series the series index (in the range 0 to getSeriesCount() - 1).
*
* @param values new values table.
*
*/
public void setValues (int series, double[] values) {
((CustomHistogramDataset)seriesCollection).setValues(series, values);
}
/**
* Returns the filled flag associated with the series-th
* data series.
*
* @param series series index.
*
* @return fill flag.
*
*/
public boolean getFilled (int series) {
return filled[series];
}
/**
* Sets the filled flag. This option fills histogram rectangular.
* The fill color is the current series color, alpha's color parameter will
* be used to draw transparency.
*
* @param series series index.
*
* @param filled fill flag.
*
*
*/
public void setFilled (int series, boolean filled) {
this.filled[series] = filled;
}
/**
* Returns the margin which is a percentage amount by which the bars are trimmed.
*
*/
public double getMargin() {
return ((XYBarRenderer)renderer).getMargin();
}
/**
* Sets the margin which is a percentage amount by which the bars are trimmed for all series.
*
* @param margin margin percentage amount.
*
*
*/
public void setMargin (double margin) {
((XYBarRenderer)renderer).setMargin(margin);
}
/**
* Returns the outline width in pt.
*
* @param series series index.
*
*
*/
public double getOutlineWidth (int series) {
return lineWidth[series];
}
/**
* Sets the outline width in pt.
*
* @param series series index.
*
* @param outline outline width.
*
*
*/
public void setOutlineWidth (int series, double outline) {
this.lineWidth[series] = outline;
}
public String toLatex (double XScale, double YScale, double XShift,
double YShift, double xmin, double xmax,
double ymin, double ymax) {
// Calcule les bornes reelles du graphique, en prenant en compte la position des axes
xmin = Math.min(XShift, xmin);
xmax = Math.max(XShift, xmax);
ymin = Math.min(YShift, ymin);
ymax = Math.max(YShift, ymax);
CustomHistogramDataset tempSeriesCollection = (CustomHistogramDataset)seriesCollection;
Formatter formatter = new Formatter(Locale.US);
double var;
double margin = ((XYBarRenderer)renderer).getMargin();
for (int i = tempSeriesCollection.getSeriesCount() - 1; i >= 0; i--) {
List temp = tempSeriesCollection.getBins(i);
ListIterator iter = temp.listIterator();
Color color = (Color)renderer.getSeriesPaint(i);
String colorString = detectXColorClassic(color);
if (colorString == null) {
colorString = "color"+i;
formatter.format( "\\definecolor{%s}{rgb}{%.2f, %.2f, %.2f}%n",
colorString, color.getRed()/255.0, color.getGreen()/255.0,
color.getBlue()/255.0);
}
HistogramBin currentBin=null;
while(iter.hasNext()) {
double currentMargin;
currentBin = (HistogramBin)iter.next();
currentMargin = ((margin*(currentBin.getEndBoundary()-currentBin.getStartBoundary())))*XScale;
if ((currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
&& (currentBin.getCount() >= ymin && currentBin.getCount() <= ymax) )
{
var = Math.min( currentBin.getEndBoundary(), xmax);
if (filled[i]) {
formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], (color.getAlpha()/255.0), colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
}
else {
formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
currentMargin, (var-XShift)*XScale, (currentBin.getCount()-YShift)*YScale);
}
}
else if ( (currentBin.getStartBoundary() >= xmin && currentBin.getStartBoundary() <= xmax)
&& (currentBin.getCount() >= ymin && currentBin.getCount() > ymax) )
{ // Cas ou notre rectangle ne peut pas etre affiche en entier (trop haut)
var = Math.min( currentBin.getEndBoundary(), xmax);
if (filled[i]) {
formatter.format("\\filldraw [line width=%.2fpt, opacity=%.2f, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], (color.getAlpha()/255.0), colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
}
else {
formatter.format("\\draw [line width=%.2fpt, color=%s] ([xshift=%.4f] %.4f, %.4f) rectangle ([xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, 0.0,
currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
formatter.format("\\draw [line width=%.2fpt, color=%s, style=dotted] ([xshift=%.4f] %.4f, %.4f) rectangle ([yshift=3mm, xshift=-%.4f] %.4f, %.4f); %%%n",
lineWidth[i], colorString,
currentMargin, (currentBin.getStartBoundary()-XShift)*XScale, (ymax-YShift)*YScale,
currentMargin, (var-XShift)*XScale, (ymax-YShift)*YScale);
}
}
}
}
return formatter.toString();
}
}
Ads help maintain this website.