/* * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * Copyright 2013 Aurelian Tutuianu * Copyright 2014 Aurelian Tutuianu * Copyright 2015 Aurelian Tutuianu * Copyright 2016 Aurelian Tutuianu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package rapaio.graphics.plot.plotcomp; import rapaio.core.stat.Quantiles; import rapaio.data.Var; import rapaio.graphics.base.Range; import rapaio.graphics.opt.ColorPalette; import rapaio.graphics.opt.GOpt; import rapaio.graphics.plot.Plot; import rapaio.graphics.plot.PlotComponent; import java.awt.*; import java.awt.geom.Rectangle2D; import java.util.Arrays; import static rapaio.graphics.Plotter.bins; /** * Plot component which allows one to add a histogram to a plot. * * @author <a href="mailto:padreati@yahoo.com">Aurelian Tutuianu</a> */ public class Histogram extends PlotComponent { private static final long serialVersionUID = -7990247895216501553L; private final Var v; double[] freqTable; double minValue = Double.NaN; double maxValue = Double.NaN; public Histogram(Var v, GOpt... opts) { this(v, Double.NaN, Double.NaN, opts); } public Histogram(Var v, double minValue, double maxValue, GOpt... opts) { this.v = v; this.minValue = minValue; this.maxValue = maxValue; // default values for histogram options.setColorDefault(gOpts -> new Color[]{gOpts.getPalette().getColor(7)}); options.apply(opts); } private int computeFreedmanDiaconisEstimation(Var v) { double[] q = Quantiles.from(v, 0, 0.25, 0.75, 1).values(); double iqr = q[2] - q[1]; return (int) Math.min(1024, Math.ceil((q[3] - q[0]) / (2 * iqr * Math.pow(v.stream().complete().count(), -1.0 / 3.0)))); } @Override public void initialize(Plot parent) { super.initialize(parent); parent.yLab(options.getProb() ? "density" : "frequency"); parent.xLab(v.name()); parent.leftThick(true); parent.leftMarkers(true); parent.bottomThick(true); parent.bottomMarkers(true); if (options.getBins() == -1) { options.apply(bins(computeFreedmanDiaconisEstimation(v))); } } public Histogram minValue(double minValue) { this.minValue = minValue; return this; } public Histogram maxValue(double maxValue) { this.maxValue = maxValue; return this; } private void rebuild() { if (minValue != minValue) { for (int i = 0; i < v.rowCount(); i++) { if (v.missing(i)) { continue; } if (minValue != minValue) { minValue = v.value(i); } else { minValue = Math.min(minValue, v.value(i)); } if (maxValue != maxValue) { maxValue = v.value(i); } else { maxValue = Math.max(maxValue, v.value(i)); } } } double step = (maxValue - minValue) / (1. * options.getBins()); freqTable = new double[options.getBins()]; double total = 0; for (int i = 0; i < v.rowCount(); i++) { if (v.missing(i)) { continue; } total++; if (v.value(i) < minValue || v.value(i) > maxValue) { continue; } int index = (int) ((v.value(i) - minValue) / step); if (index == freqTable.length) index--; freqTable[index]++; } if (options.getProb() && (total != 0)) { for (int i = 0; i < freqTable.length; i++) { freqTable[i] /= (total * step); } } } @Override public Range buildRange() { rebuild(); Range range = new Range(); range.union(minValue, Double.NaN); range.union(maxValue, Double.NaN); Arrays.stream(freqTable).sequential().forEach(t -> range.union(Double.NaN, t)); range.union(Double.NaN, 0); return range; } @Override public void paint(Graphics2D g2d) { rebuild(); g2d.setColor(ColorPalette.STANDARD.getColor(0)); for (int i = 0; i < freqTable.length; i++) { double d = freqTable[i]; double mind = Math.min(d, parent.getRange().y2()); if (!parent.getRange().contains(binStart(i), 0)) { continue; } Composite old = g2d.getComposite(); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, options.getAlpha())); double x = parent.xScale(binStart(i)); double y = parent.yScale(mind); double w = parent.xScale(binStart(i + 1)) - parent.xScale(binStart(i)); double h = parent.yScale(0) - parent.yScale(mind); if (d != 0) { g2d.setColor(options.getColor(i)); g2d.fill(new Rectangle2D.Double(x, y, w, h)); } g2d.setColor(ColorPalette.STANDARD.getColor(0)); g2d.draw(new Rectangle2D.Double(x, y, w, h)); g2d.setComposite(old); } } private double binStart(int i) { double fraction = (maxValue - minValue) / (1. * options.getBins()); return minValue + fraction * i; } }