Skip to content

Commit

Permalink
Improve constructors and documentation for Histogram
Browse files Browse the repository at this point in the history
Improves documentation for Histogram, explaining overflow bins and bin
indexing.

Improve the constructors of Histogram to allow the resulting Histogram
to either have the outer bounds or the bin centers of the first and last
bin on the given min/max value. previously, the values where shifted
from the given range by one half of the bin size.

adresses #246
  • Loading branch information
wirew0rm authored and RalphSteinhagen committed Sep 7, 2020
1 parent 970a8b1 commit 75c630e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 34 deletions.
10 changes: 8 additions & 2 deletions chartfx-dataset/src/main/java/de/gsi/dataset/Histogram.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package de.gsi.dataset;

/**
* @author rstein
* Specialized DataSet interface for storing and processing Histogram data.
*
* A histogram with bins 1...N is defined by n+1 values defining the steps between the bins.
* The bins can be equidistant or not.
*
* The bins 0 and N+1 have a special meaning and contain the data for data which is out of range.
* They are not drawn by default.
*
* @author rstein
*/
public interface Histogram extends DataSet, DataSetMetaData {

/**
* Increment bin content by 1. More...
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,61 @@ public abstract class AbstractHistogram extends AbstractDataSet<AbstractHistogra
protected final double[] data;

/**
* Creates histogram with name and range [minX, maxX]
*
* Defines how the lower and upper bound of equidistant Histograms should be treated
*/
public enum HistogramOuterBounds {
/**
* The min and max value defines the center of the bins
* <pre>
* nBins = 3
* ^ ___
* | ___| |
* | | | |___
* |_|___|___|___|____
* ' '
* min max
* </pre>
*/
BINS_CENTERED_ON_BOUNDARY,
/**
* The min and max value defines the outer bounds of the bins
* <pre>
* nBins = 3
* ^ __
* | __| |
* | | | |__
* |___|__|__|__|____
* ' '
* min max
* </pre>
*/
BINS_ALIGNED_WITH_BOUNDARY
}

/**
* Creates a non equidistant histogram with name and range [minX, maxX]
* <p>
* NOTE: since chartfx's default ErrorDataSetRenderer cannot access the bin boundaries, it is currently unable to
* correctly render non equidistant Histograms.
* </p>
*
* @param name of the data sets
* @param xBins the initial bin array (defines [minX, maxX] and nBins)
* @param xBins the bin boundary array (defines [minX, maxX] and nBins)
*/
public AbstractHistogram(final String name, final double[] xBins) {
super(name, 2);
final int nBins = xBins.length;
nAxisBins = new int[2];
final int nBins = xBins.length - 1; // NB: bin boundaries
nAxisBins = new int[1];
nAxisBins[0] = nBins + 2; // N.B. one bin for underflow, one bin for overflow
nAxisBins[1] = nBins + 2;
data = new double[nAxisBins[0]];
axisBins = new double[1][];
axisBins[0] = new double[nAxisBins[0]];
axisBins[0][0] = -Double.MAX_VALUE;
axisBins[0][nAxisBins[0] - 1] = +Double.MAX_VALUE;
final double[] xBinsSorted = Arrays.copyOf(xBins, xBins.length);
Arrays.sort(xBinsSorted);
for (int i = 0; i < nBins; i++) {
axisBins[0][i + 1] = xBinsSorted[i];
getAxisDescription(DIM_X).add(xBinsSorted[i]);
}
System.arraycopy(xBinsSorted, 0, axisBins[0], 1, xBinsSorted.length);
getAxisDescription(DIM_X).set(axisBins[0][1], axisBins[0][nBins + 1]);
getAxisDescription(DIM_Y).clear();
equidistant = false;
}
Expand All @@ -49,14 +82,23 @@ public AbstractHistogram(final String name, final double[] xBins) {
* @param nBins number of bins
* @param minX minimum of range
* @param maxX maximum of range
* @param boundsType how the min/max value should be interpreted
*/
public AbstractHistogram(final String name, final int nBins, final double minX, final double maxX) {
public AbstractHistogram(final String name, final int nBins, final double minX, final double maxX, final HistogramOuterBounds boundsType) {
super(name, 2);
nAxisBins = new int[2];
nAxisBins[0] = nBins + 2; // N.B. one bin for underflow, one bin for overflow
nAxisBins[1] = nBins + 2;
data = new double[nAxisBins[0]];
getAxisDescription(DIM_X).set(minX, maxX);
switch (boundsType) {
case BINS_CENTERED_ON_BOUNDARY:
final double halfBin = (maxX - minX) / ((nBins - 1) * 2);
getAxisDescription(DIM_X).set(minX - halfBin, maxX + halfBin);
break;
case BINS_ALIGNED_WITH_BOUNDARY:
getAxisDescription(DIM_X).set(minX, maxX);
break;
}
getAxisDescription(DIM_Y).clear();
equidistant = true;
}
Expand All @@ -71,18 +113,29 @@ public AbstractHistogram(final String name, final int nBins, final double minX,
* @param nBinsY number of vertical bins
* @param minY minimum of vertical range
* @param maxY maximum of vertical range
* @param boundsType How the min and max value should be interpreted
*/
public AbstractHistogram(final String name, final int nBinsX, final double minX, final double maxX,
final int nBinsY, final double minY, final double maxY) {
final int nBinsY, final double minY, final double maxY, final HistogramOuterBounds boundsType) {
super(name, 3);
nAxisBins = new int[3];
nAxisBins[0] = nBinsX + 2; // N.B. one bin for underflow, one bin for overflow
nAxisBins[1] = nBinsY + 2;
nAxisBins[2] = nBinsX * nBinsY + 2;
data = new double[nAxisBins[2]];
getAxisDescription(DIM_X).set(minX, maxX);
getAxisDescription(DIM_Y).set(minY, maxY);
getAxisDescription(2).clear();
switch (boundsType) {
case BINS_CENTERED_ON_BOUNDARY:
final double halfBinX = (maxX - minX) / ((nBinsX - 1) * 2);
getAxisDescription(DIM_X).set(minX - halfBinX, maxX + halfBinX);
final double halfBinY = (maxY - minY) / ((nBinsY - 1) * 2);
getAxisDescription(DIM_Y).set(minY - halfBinY, maxY + halfBinY);
break;
case BINS_ALIGNED_WITH_BOUNDARY:
getAxisDescription(DIM_X).set(minX, maxX);
getAxisDescription(DIM_X).set(minY, maxY);
break;
}
getAxisDescription(DIM_Z).clear();
equidistant = true;
}

Expand Down Expand Up @@ -129,7 +182,7 @@ public int findBin(final int dimIndex, final double x) {
if (isEquiDistant()) {
final double diff = x - getAxisDescription(dimIndex).getMin();
final double delta = getAxisDescription(dimIndex).getLength() / (getDataCount() - 2);
return (int) Math.round(diff / delta);
return (int) Math.floor(diff / delta);
}

return findNextLargerIndex(axisBins[dimIndex], x);
Expand Down Expand Up @@ -157,7 +210,7 @@ public double getBinCenter(final int dimIndex, final int binIndex) {

if (isEquiDistant()) {
final double delta = getAxisDescription(dimIndex).getLength() / (getDataCount());
return getAxisDescription(dimIndex).getMin() + ((binIndex - 1) * delta);
return getAxisDescription(dimIndex).getMin() + ((binIndex - 0.5) * delta);
}

return axisBins[dimIndex][binIndex] + (0.5 * (axisBins[dimIndex][binIndex + 1] - axisBins[dimIndex][binIndex]));
Expand Down
12 changes: 7 additions & 5 deletions chartfx-dataset/src/main/java/de/gsi/dataset/spi/Histogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ public Histogram(final String name, final double[] xBins, final boolean horizont
* @param nBins number of bins
* @param minX minimum of range
* @param maxX maximum of range
* @param boundsType How the min and max value should be interpreted
*/
public Histogram(String name, int nBins, double minX, double maxX) {
this(name, nBins, minX, maxX, true);
public Histogram(String name, int nBins, double minX, double maxX, final HistogramOuterBounds boundsType) {
this(name, nBins, minX, maxX, true, boundsType);
}

/**
Expand All @@ -64,13 +65,14 @@ public Histogram(String name, int nBins, double minX, double maxX) {
* @param minX minimum of range
* @param maxX maximum of range
* @param horizontal whether binning is performed in X
* @param boundsType How the min and max value should be interpreted
*/
public Histogram(String name, int nBins, double minX, double maxX, final boolean horizontal) {
super(name, nBins, minX, maxX);
public Histogram(String name, int nBins, double minX, double maxX, final boolean horizontal, final HistogramOuterBounds boundsType) {
super(name, nBins, minX, maxX, boundsType);
isHorizontal = horizontal;
if (!isHorizontal) {
getAxisDescription(DIM_Y).set(getAxisDescription(DIM_X));
getAxisDescription(DIM_X).clear();
getAxisDescription(DIM_Y).set(minX, maxX);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ public class Histogram2 extends AbstractHistogram implements Histogram2D {
* @param minY minimum of vertical range
* @param maxY maximum of vertical range
*/
public Histogram2(String name, int nBinsX, double minX, double maxX, final int nBinsY, final double minY, final double maxY) {
super(name, nBinsX, minX, maxX, nBinsY, minY, maxY);
xProjection = new Histogram(name + "-Proj-X", nBinsX, minX, maxX, true);
yProjection = new Histogram(name + "-Proj-Y", nBinsY, minY, maxY, false);
public Histogram2(String name, int nBinsX, double minX, double maxX, final int nBinsY, final double minY, final double maxY, final HistogramOuterBounds boundsType) {
super(name, nBinsX, minX, maxX, nBinsY, minY, maxY, boundsType);
xProjection = new Histogram(name + "-Proj-X", nBinsX, minX, maxX, true, boundsType);
yProjection = new Histogram(name + "-Proj-Y", nBinsY, minY, maxY, false, boundsType);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static de.gsi.dataset.DataSet.DIM_X;
import static de.gsi.dataset.DataSet.DIM_Y;
import static de.gsi.dataset.spi.AbstractHistogram.HistogramOuterBounds.BINS_ALIGNED_WITH_BOUNDARY;

import java.util.Arrays;

Expand Down Expand Up @@ -42,7 +43,7 @@ public void testDataSetEquality() {
assertEquals(new DoubleErrorDataSet("default"), new DoubleErrorDataSet("default"));
assertEquals(new FifoDoubleErrorDataSet("default", 10), new FifoDoubleErrorDataSet("default", 11));
assertEquals(new FragmentedDataSet("default"), new FragmentedDataSet("default"));
assertEquals(new Histogram("default", 10, 0.0, 1.0), new Histogram("default", 10, 0.0, 1.0));
assertEquals(new Histogram("default", 10, 0.0, 1.0, BINS_ALIGNED_WITH_BOUNDARY), new Histogram("default", 10, 0.0, 1.0, BINS_ALIGNED_WITH_BOUNDARY));
// assertEquals(new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0),
// new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0));
assertEquals(new LabelledMarkerDataSet("default"), new LabelledMarkerDataSet("default"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.gsi.chart.samples;

import static de.gsi.dataset.spi.AbstractHistogram.HistogramOuterBounds.BINS_ALIGNED_WITH_BOUNDARY;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
Expand Down Expand Up @@ -31,8 +33,8 @@ public class Histogram2DimSample extends Application {
private static final int N_BINS_Y = 120;
private final Random rnd = new Random();

private final Histogram2 histogram1 = new Histogram2("hist1", N_BINS_X, 0.0, 20.0, N_BINS_Y, 0.0, 30.0);
private final Histogram2 histogram2 = new Histogram2("hist2", N_BINS_X, 0.0, 20.0, N_BINS_Y, 0.0, 30.0);
private final Histogram2 histogram1 = new Histogram2("hist1", N_BINS_X, 0.0, 20.0, N_BINS_Y, 0.0, 30.0, BINS_ALIGNED_WITH_BOUNDARY);
private final Histogram2 histogram2 = new Histogram2("hist2", N_BINS_X, 0.0, 20.0, N_BINS_Y, 0.0, 30.0, BINS_ALIGNED_WITH_BOUNDARY);
private int counter;

private void fillData() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.gsi.chart.samples;

import static de.gsi.dataset.spi.AbstractHistogram.HistogramOuterBounds.BINS_ALIGNED_WITH_BOUNDARY;

import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -34,8 +36,8 @@ public class HistogramSample extends Application {
private static final int N_BINS = 30;
private final double[] xBins = { 0.0, 0.1, 0.2, 0.3, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 16.0, 17.0, 18.0, 19.0,
19.7, 19.8, 19.9, 20.0 };
private final Histogram dataSet1 = new Histogram("myHistogram1", N_BINS, 0.0, 20.0);
private final Histogram dataSet2 = new Histogram("myHistogram2", N_BINS, 0.0, 20.0);
private final Histogram dataSet1 = new Histogram("myHistogram1", N_BINS, 0.0, 20.0, BINS_ALIGNED_WITH_BOUNDARY);
private final Histogram dataSet2 = new Histogram("myHistogram2", N_BINS, 0.0, 20.0, BINS_ALIGNED_WITH_BOUNDARY);
private final Histogram dataSet3 = new Histogram("myHistogram3", xBins); // custom, non-equidistant histogram

private int counter;
Expand Down

0 comments on commit 75c630e

Please sign in to comment.