/*
* 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;
import rapaio.data.*;
import rapaio.graphics.Plotter;
import rapaio.graphics.base.HostFigure;
import rapaio.graphics.base.Range;
import rapaio.graphics.opt.ColorPalette;
import rapaio.graphics.opt.GOpt;
import rapaio.graphics.opt.GOpts;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* @author <a href="mailto:padreati@yahoo.com">Aurelian Tutuianu</a>
*/
@Deprecated
public class BarChart extends HostFigure {
private static final long serialVersionUID = -3953248625109450364L;
private final Var category;
private final Var condition;
private final Var numeric;
private final GOpts options = new GOpts().apply(Plotter.color(0));
private boolean density = false;
private Range range;
private int[] sel;
private double[][] hits;
private double[] totals;
private SortType sort = SortType.NONE;
private int top = Integer.MAX_VALUE;
public BarChart(Var category, GOpt... opts) {
this(category, null, opts);
}
public BarChart(Var category, Var condition, GOpt... opts) {
this(category, condition, null, opts);
}
public BarChart(Var category, Var condition, Var numeric, GOpt... opts) {
List<VarType> varTypes = Arrays.asList(VarType.BINARY, VarType.NOMINAL, VarType.ORDINAL);
if (!varTypes.contains(category.type())) {
throw new IllegalArgumentException("categories are nominal only");
}
if (condition == null) {
condition = Nominal.empty(category.rowCount(), new ArrayList<>());
}
if (!condition.type().isNominal()) {
throw new IllegalArgumentException("conditions are nominal only");
}
if (numeric == null) {
numeric = Numeric.fill(category.rowCount(), 1);
}
if (!numeric.type().isNumeric()) {
throw new IllegalArgumentException("Numeric var must be .. isNumeric");
}
this.category = category;
this.condition = condition;
this.numeric = numeric;
leftThick(true);
leftMarkers(true);
bottomThick(true);
bottomMarkers(true);
int shift = 9;
options.apply(Plotter.color(Index.seq(shift, condition.levels().length)));
options.apply(opts);
}
public BarChart useSortType(SortType sort) {
this.sort = sort;
return this;
}
public BarChart useTop(int top) {
this.top = top;
return this;
}
public void useDensity(boolean density) {
this.density = density;
}
@Override
public Range buildRange() {
if (range == null) {
// learn preliminaries
int width = category.levels().length;
int height = condition.levels().length;
totals = new double[width];
hits = new double[width][height];
int len = Integer.MAX_VALUE;
len = Math.min(len, category.rowCount());
len = Math.min(len, condition.rowCount());
len = Math.min(len, numeric.rowCount());
for (int i = 0; i < len; i++) {
hits[category.index(i)][condition.index(i)] += numeric.value(i);
totals[category.index(i)] += numeric.value(i);
}
// this needs reworking since it does not work for negative values
if (density) {
double t = 0;
for (double total : totals) {
t += total;
}
for (int i = 0; i < hits.length; i++) {
for (int j = 0; j < hits[i].length; j++) {
hits[i][j] /= t;
}
totals[i] /= t;
}
}
// now restrict values
List<Integer> list = new ArrayList<>();
for (int i = 0; i < totals.length; i++) {
list.add(i);
}
Collections.sort(list, (o1, o2) -> {
if (totals[o1] == totals[o2])
return 0;
int sign = (SortType.ASC.equals(sort)) ? 1 : -1;
return totals[o1] < totals[o2] ? -sign : sign;
});
if (top < list.size()) {
list = list.subList(0, top);
}
sel = new int[list.size()];
if (SortType.NONE.equals(sort)) {
Set<Integer> set = new HashSet<>(list);
int pos = 0;
for (int i = 0; i < totals.length; i++) {
if (set.contains(i)) {
sel[pos++] = i;
}
}
} else {
for (int i = 0; i < sel.length; i++) {
sel[i] = list.get(i);
}
}
// now learn range
range = new Range();
for (int aSel1 : sel) {
range.union(Double.NaN, totals[aSel1]);
range.union(Double.NaN, 0);
}
range.union(-0.5, 0);
range.union(sel.length + 0.5, 0);
}
return range;
}
@Override
public void buildLeftMarkers() {
buildNumericLeftMarkers();
}
@Override
public void buildBottomMarkers() {
bottomMarkersPos.clear();
bottomMarkersMsg.clear();
int xspots = sel.length;
double xspotwidth = getViewport().width / (1. * xspots);
int cnt = 0;
for (int aSel : sel) {
if (totals[aSel] == 0)
continue;
bottomMarkersPos.add(xspotwidth * (0.5 + cnt));
bottomMarkersMsg.add(category.levels()[aSel]);
cnt++;
}
}
@Override
public void paint(Graphics2D g2d, Rectangle rect) {
super.paint(g2d, rect);
int col = 0;
for (int aSel : sel) {
if (totals[aSel] == 0) {
continue;
}
double ystart = 0;
for (int j = 0; j < condition.levels().length; j++) {
double yend = ystart + hits[aSel][j];
int sign = ((yend > 0) ? 1 : -1);
int[] x = {
(int) xScale(col - 0.4),
(int) xScale(col - 0.4),
(int) xScale(col + 0.4),
(int) xScale(col + 0.4),
(int) xScale(col - 0.4)};
int[] y = {
(int) yScale(ystart),
(int) yScale(yend),
(int) yScale(yend),
(int) yScale(ystart),
(int) yScale(ystart)};
g2d.setColor(ColorPalette.STANDARD.getColor(0));
g2d.drawPolygon(x, y, 4);
x = new int[]{
(int) xScale(col - 0.4) + 1,
(int) xScale(col - 0.4) + 1,
(int) xScale(col + 0.4),
(int) xScale(col + 0.4),
(int) xScale(col - 0.4) + 1};
y = new int[]{
(int) yScale(ystart),
(int) yScale(yend) + sign,
(int) yScale(yend) + sign,
(int) yScale(ystart),
(int) yScale(ystart)};
g2d.setColor(options.getColor(j));
g2d.fillPolygon(x, y, 4);
ystart = yend;
}
col++;
}
}
public enum SortType {
NONE, ASC, DESC
}
}