Java tutorial
/* * Class: HistogramSeriesCollection * Description: * Environment: Java * Software: SSJ * Copyright (C) 2001 Pierre L'Ecuyer and Universit de Montral * Organization: DIRO, Universit de Montral * @author * @since * SSJ is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License (GPL) as published by the * Free Software Foundation, either version 3 of the License, or * any later version. * SSJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * A copy of the GNU General Public License is available at <a href="http://www.gnu.org/licenses">GPL licence site</a>. */ 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 <TT>HistogramChart</TT>. <TT>HistogramSeriesCollection</TT> 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 <TT>CustomHistogramDataset</TT> to store data plots, * and linked with JFreeChart <TT>XYBarRenderer</TT> to render the plot. * <TT>CustomHistogramDataset</TT> 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 <TT>HistogramSeriesCollection</TT> instance with empty dataset. * */ public HistogramSeriesCollection() { renderer = new XYBarRenderer(); seriesCollection = new CustomHistogramDataset(); } /** * Creates a new <TT>HistogramSeriesCollection</TT> 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 <TT>HistogramSeriesCollection</TT> instance with the given data * series <TT>data</TT>. 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 <TT>numPoints</TT> of <TT>data</TT> 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 <TT>HistogramSeriesCollection</TT>. * 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 <TT>HistogramSeriesCollection</TT> instance with default * parameters and given data. The input parameter represents a collection of * data observation sets. Each <TT>TallyStore</TT> 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 <TT>HistogramSeriesCollection</TT> 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 <SPAN CLASS="textit">the first</SPAN> * <TT>numPoints</TT> of <TT>data</TT> 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 <TT>CustomHistogramDataset</TT> 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 <TT>CustomHistogramDataset</TT> object. * */ public int add(TallyStore tally) { return add(tally.getArray(), tally.numberObs()); } /** * Returns the <TT>CustomHistogramDataset</TT> object associated with the current variable. * * @return <TT>CustomHistogramDataset</TT> 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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @return A list of bins. * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range. * */ public List getBins(int series) { return ((CustomHistogramDataset) seriesCollection).getBins(series); } /** * Sets <TT>bins</TT> periodic bins from the observation minimum values to the observation maximum value for a series. * * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @param bins the number of periodic bins. * * IndexOutOfBoundsExceptionif <TT>series</TT> is outside the specified range. * */ public void setBins(int series, int bins) { ((CustomHistogramDataset) seriesCollection).setBins(series, bins); } /** * Sets <TT>bins</TT> periodic bins from <TT>minimum</TT> to <TT>maximum</TT> 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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @param bins the number of periodic bins. * * @param minimum minimum value. * * @param maximum maximum value. * * IndexOutOfBoundsExceptionif <TT>series</TT> 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 <TT>binsTable</TT> 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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @param binsTable new bins table. * * IndexOutOfBoundsExceptionif <TT>series</TT> 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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @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 <TT>List</TT> variable. * * @param series the series index (in the range <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @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 <TT>0</TT> to <TT>getSeriesCount() - 1</TT>). * * @param values new values table. * */ public void setValues(int series, double[] values) { ((CustomHistogramDataset) seriesCollection).setValues(series, values); } /** * Returns the <TT>filled</TT> flag associated with the <TT>series</TT>-th * data series. * * @param series series index. * * @return fill flag. * */ public boolean getFilled(int series) { return filled[series]; } /** * Sets the <TT>filled</TT> 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(); } }