/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* AttributeVisualizationPanel.java
* Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui;
import weka.core.Attribute;
import weka.core.AttributeStats;
import weka.core.FastVector;
import weka.core.Instances;
import weka.core.SparseInstance;
import weka.core.Utils;
import weka.gui.visualize.PrintableComponent;
import weka.gui.visualize.PrintablePanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.FileReader;
import javax.swing.JComboBox;
import javax.swing.JFrame;
/**
* Creates a panel that shows a visualization of an
* attribute in a dataset. For nominal attribute it
* shows a bar plot, with each bar corresponding to
* each nominal value of the attribute with its height
* equal to the frequecy that value appears in the
* dataset. For numeric attributes, it displays a
* histogram. The width of an interval in the
* histogram is calculated using Scott's(1979)
* method:
* intervalWidth = Max(1, 3.49*Std.Dev*numInstances^(1/3))
* Then the number of intervals is calculated by:
* intervals = max(1, Math.round(Range/intervalWidth);
*
* @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz)
* @version $Revision: 5721 $
*/
public class AttributeVisualizationPanel
extends PrintablePanel {
/** for serialization */
private static final long serialVersionUID = -8650490488825371193L;
/** This holds the current set of instances */
protected Instances m_data;
/**
* This holds the attribute stats of the current attribute on display. It is
* calculated in setAttribute(int idx) when it is called to set a new
* attribute index.
*/
protected AttributeStats m_as;
/** This holds the index of the current attribute on display and should be
* set through setAttribute(int idx).
*/
protected int m_attribIndex;
/**
* This holds the max value of the current attribute. In case of nominal
* attribute it is the highest count that a nominal value has in the
* attribute (given by m_as.nominalWeights[i]), otherwise in case of numeric
* attribute it is simply the maximum value present in the attribute (given by
* m_as.numericStats.max). It is used to calculate the ratio of the height of
* the bars with respect to the height of the display area.
*/
protected double m_maxValue;
/**
* This array holds the count (or height) for the each of the bars in a
* barplot or a histogram. In case of barplots (and current attribute being
* nominal) its length (and the number of bars) is equal to the number of
* nominal values in the current attribute, with each field of the array being
* equal to the count of each nominal that it represents (the count of ith
* nominal value of an attribute is given by m_as.nominalWeights[i]). Whereas,
* in case of histograms (and current attribute being numeric) the width of
* its intervals is calculated by Scott's(1979) method:
* intervalWidth = Max(1, 3.49*Std.Dev*numInstances^(1/3))
* And the number of intervals by:
* intervals = max(1, Math.round(Range/intervalWidth);
* Then each field of this array contains the number of values of the current
* attribute that fall in the histogram interval that it represents.
* NOTE: The values of this array are only calculated if the class attribute
* is not set or if it is numeric.
*/
protected double[] m_histBarCounts;
/**
* This array holds the per class count (or per class height) of the each of
* the bars in a barplot or a histogram.
* For nominal attributes the format is:
* m_histBarClassCounts[nominalValue][classValue+1].
* For numeric attributes the format is:
* m_histBarClassCounts[interval][classValues+1],
* where the number of intervals is calculated by the Scott's method as
* mentioned above.
* The array is initialized to have 1+numClasses to accomodate for instances
* with missing class value. The ones with missing class value are displayed
* as a black sub par in a histogram or a barplot.
*
* NOTE: The values of this array are only calculated if the class attribute
* is set and it is nominal.
*/
SparseInstance m_histBarClassCounts[];
/**
* Contains the range of each bar in a histogram. It is used to work out the
* range of bar the mouse pointer is on in getToolTipText().
*/
protected double m_barRange;
/** Contains the current class index. */
protected int m_classIndex;
/** This stores the BarCalc or HistCalc thread while a new barplot or
* histogram is being calculated. */
private Thread m_hc;
/** True if the thread m_hc above is running. */
private boolean m_threadRun=false;
private boolean m_doneCurrentAttribute = false;
private boolean m_displayCurrentAttribute = false;
/** This stores and lets the user select a class attribute. It also has
* an entry "No Class" if the user does not want to set a class attribute
* for colouring.
*/
protected JComboBox m_colorAttrib;
/**
* Fontmetrics used to get the font size which is required for calculating
* displayable area size, bar height ratio and width of strings that are
* displayed on top of bars indicating their count.
*/
private FontMetrics m_fm;
/**
* Lock variable to synchronize the different threads running currently in
* this class. There are two to three threads in this class, AWT paint thread
* which is handled differently in paintComponent() which checks on
* m_threadRun to determine if it can perform full paint or not, the second
* thread is the main execution thread and the third is the one represented by
* m_hc which we start when we want to calculate the internal fields for a bar
* plot or a histogram.
*/
private Integer m_locker = new Integer(1);
//Image img;
/** Contains discrete colours for colouring of subbars of histograms and
* bar plots when the class attribute is set and is nominal
*/
private FastVector m_colorList = new FastVector();
/** default colour list */
private static final Color [] m_defaultColors = {Color.blue,
Color.red,
Color.cyan,
new Color(75, 123, 130),
Color.pink,
Color.green,
Color.orange,
new Color(255, 0, 255),
new Color(255, 0, 0),
new Color(0, 255, 0),
};
/**
* Constructor - If used then the class will not show the class selection
* combo box.
*/
public AttributeVisualizationPanel() {
this(false);
}
/**
* Constructor.
* @param showColouringOption - should be true if the class selection combo
* box is to be displayed with the histogram/barplot, or false otherwise.
* P.S: the combo box is always created it just won't be shown if
* showColouringOption is false.
*/
public AttributeVisualizationPanel(boolean showColouringOption) {
this.setFont( new Font("Default", Font.PLAIN, 9) );
m_fm = this.getFontMetrics( this.getFont() );
this.setToolTipText("");
FlowLayout fl= new FlowLayout(FlowLayout.LEFT);
this.setLayout(fl);
this.addComponentListener( new ComponentAdapter() {
public void componentResized(ComponentEvent ce) {
if(m_data!=null) {
// calcGraph();
}
}
});
m_colorAttrib = new JComboBox();
m_colorAttrib.addItemListener( new ItemListener() {
public void itemStateChanged(ItemEvent ie) {
if(ie.getStateChange()==ItemEvent.SELECTED) {
m_classIndex = m_colorAttrib.getSelectedIndex() - 1;
if (m_as != null) {
setAttribute(m_attribIndex);
}
}
}
});
if(showColouringOption) {
//m_colorAttrib.setVisible(false);
this.add(m_colorAttrib);
validate();
}
}
/**
* Sets the instances for use
*
* @param newins a set of Instances
*/
public void setInstances(Instances newins) {
m_attribIndex = 0;
m_as = null;
m_data = new Instances(newins);
if(m_colorAttrib!=null) {
m_colorAttrib.removeAllItems();
m_colorAttrib.addItem("No class");
for(int i=0; i= 0) {
m_colorAttrib.setSelectedIndex(m_data.classIndex() + 1);
} else {
m_colorAttrib.setSelectedIndex(m_data.numAttributes());
}
//if (m_data.classIndex() >= 0) {
// m_colorAttrib.setSelectedIndex(m_data.classIndex());
//}
}
if (m_data.classIndex() >= 0) {
m_classIndex = m_data.classIndex();
} else {
m_classIndex = m_data.numAttributes()-1;
}
}
/**
* Returns the class selection combo box if the parent component wants to
* place it in itself or in some component other than this component.
*/
public JComboBox getColorBox() {
return m_colorAttrib;
}
/**
* Get the coloring (class) index for the plot
*
* @return an int
value
*/
public int getColoringIndex() {
return m_classIndex; //m_colorAttrib.getSelectedIndex();
}
/**
* Set the coloring (class) index for the plot
*
* @param ci an int
value
*/
public void setColoringIndex(int ci) {
m_classIndex = ci;
if(m_colorAttrib!=null)
m_colorAttrib.setSelectedIndex(ci + 1);
else
setAttribute(m_attribIndex);
}
/**
* Tells the panel which attribute to visualize.
*
* @param index The index of the attribute
*/
public void setAttribute(int index) {
synchronized (m_locker) {
//m_threadRun = true;
m_threadRun = false;
m_doneCurrentAttribute = false;
m_displayCurrentAttribute = true;
//if(m_hc!=null && m_hc.isAlive()) m_hc.stop();
m_attribIndex = index;
m_as = m_data.attributeStats(m_attribIndex);
//m_classIndex = m_colorAttrib.getSelectedIndex();
}
this.repaint();
// calcGraph();
}
/**
* Recalculates the barplot or histogram to display, required usually when the
* attribute is changed or the component is resized.
*/
public void calcGraph(int panelWidth, int panelHeight) {
synchronized (m_locker) {
m_threadRun = true;
if(m_as.nominalWeights!=null) {
m_hc = new BarCalc(panelWidth, panelHeight);
m_hc.setPriority(m_hc.MIN_PRIORITY);
m_hc.start();
}
else if(m_as.numericStats!=null) {
m_hc = new HistCalc();
m_hc.setPriority(m_hc.MIN_PRIORITY);
m_hc.start();
} else {
m_histBarCounts = null;
m_histBarClassCounts = null;
m_doneCurrentAttribute = true;
m_threadRun = false;
this.repaint();
}
}
}
/**
* Internal class that calculates the barplot to display, in a separate
* thread. In particular it initializes some of the crucial internal fields
* required by paintComponent() to display the histogram for the current
* attribute. These include: m_histBarCounts or m_histBarClassCounts,
* m_maxValue and m_colorList.
*/
private class BarCalc extends Thread {
private int m_panelWidth;
private int m_panelHeight;
public BarCalc(int panelWidth, int panelHeight) {
m_panelWidth = panelWidth;
m_panelHeight = panelHeight;
}
public void run() {
synchronized (m_locker) {
// there is no use doing/displaying anything if the resolution
// of the panel is less than the number of values for this attribute
if (m_data.attribute(m_attribIndex).numValues() > m_panelWidth) {
m_histBarClassCounts = null;
m_threadRun = false;
m_doneCurrentAttribute = true;
m_displayCurrentAttribute = false;
AttributeVisualizationPanel.this.repaint();
return;
}
if((m_classIndex >= 0) &&
(m_data.attribute(m_classIndex).isNominal())) {
SparseInstance histClassCounts[];
histClassCounts = new SparseInstance[m_data.attribute(m_attribIndex).numValues()];
//[m_data.attribute(m_classIndex).numValues()+1];
if (m_as.nominalWeights.length > 0) {
m_maxValue = m_as.nominalWeights[0];
for(int i=0; im_maxValue)
m_maxValue = m_as.nominalWeights[i];
}
}
else {
m_maxValue = 0;
}
if(m_colorList.size()==0)
m_colorList.addElement(Color.black);
for(int i=m_colorList.size();
i < m_data.attribute(m_classIndex).numValues()+1; i++) {
Color pc = m_defaultColors[(i-1) % 10];
int ija = (i-1) / 10;
ija *= 2;
for (int j=0;j 0) {
numNonZero++;
}
}
double[] nonZeroVals = new double[numNonZero];
int[] nonZeroIndices = new int[numNonZero];
int count = 0;
for (int z = 0; z < tempClassCounts.length; z++) {
if (tempClassCounts[z] > 0) {
nonZeroVals[count] = tempClassCounts[z];
nonZeroIndices[count++] = z;
}
}
SparseInstance tempS =
new SparseInstance(1.0, nonZeroVals, nonZeroIndices, tempClassCounts.length);
histClassCounts[tempAttValueIndex] = tempS;
}
tempClassCounts = new double[m_data.attribute(m_classIndex).numValues() + 1];
tempAttValueIndex = (int)m_data.instance(k).value(m_attribIndex);
/* histClassCounts[(int)m_data.instance(k).value(m_attribIndex)] =
new double[m_data.attribute(m_classIndex).numValues()+1]; */
}
if(m_data.instance(k).isMissing(m_classIndex)) {
/* histClassCounts[(int)m_data.instance(k).value(m_attribIndex)]
[0] += m_data.instance(k).weight(); */
tempClassCounts[0] += m_data.instance(k).weight();
} else {
tempClassCounts[(int)m_data.instance(k).value(m_classIndex)+1]
+= m_data.instance(k).weight();
/*histClassCounts[(int)m_data.instance(k).value(m_attribIndex)]
[(int)m_data.instance(k).value(m_classIndex)+1] += m_data.instance(k).weight();*/
}
}
}
// set up sparse instance for last bar?
if (tempClassCounts != null) {
// set up the sparse instance for the previous bar (if any)
int numNonZero = 0;
for (int z = 0; z < tempClassCounts.length; z++) {
if (tempClassCounts[z] > 0) {
numNonZero++;
}
}
double[] nonZeroVals = new double[numNonZero];
int[] nonZeroIndices = new int[numNonZero];
int count = 0;
for (int z = 0; z < tempClassCounts.length; z++) {
if (tempClassCounts[z] > 0) {
nonZeroVals[count] = tempClassCounts[z];
nonZeroIndices[count++] = z;
}
}
SparseInstance tempS =
new SparseInstance(1.0, nonZeroVals, nonZeroIndices, tempClassCounts.length);
histClassCounts[tempAttValueIndex] = tempS;
}
//for(int i=0; i 0) {
m_maxValue = m_as.nominalWeights[0];
for(int i=0; im_maxValue)
m_maxValue = m_as.nominalWeights[i];
}
}
else {
m_maxValue = 0;
}
for(int k=0; k= 0) &&
(m_data.attribute(m_classIndex).isNominal())) {
int intervals; double intervalWidth=0.0;
//This uses the M.P.Wand's method to calculate the histogram's
//interval width. See "Data-Based Choice of Histogram Bin Width", in
//The American Statistician, Vol. 51, No. 1, Feb., 1997, pp. 59-64.
//intervalWidth = Math.pow(6D/( -psi(2, g21())*m_data.numInstances()),
// 1/3D );
//This uses the Scott's method to calculate the histogram's interval
//width. See "On optimal and data-based histograms".
// See Biometrika, 66, 605-610 OR see the same paper mentioned above.
intervalWidth = 3.49 * m_as.numericStats.stdDev *
Math.pow(m_data.numInstances(), -1/3D);
//The Math.max is introduced to remove the possibility of
//intervals=0 and =NAN that can happen if respectively all the numeric
//values are the same or the interval width is evaluated to zero.
intervals = Math.max(1,
(int)Math.round( (m_as.numericStats.max - m_as.numericStats.min) /
intervalWidth) );
//System.out.println("Max: "+m_as.numericStats.max+
// " Min: "+m_as.numericStats.min+
// " stdDev: "+m_as.numericStats.stdDev+
// "intervalWidth: "+intervalWidth);
//The number 4 below actually represents a padding of 3 pixels on
//each side of the histogram, and is also reflected in other parts of
//the code in the shape of numerical constants like "6" here.
if(intervals > AttributeVisualizationPanel.this.getWidth()) {
intervals = AttributeVisualizationPanel.this.getWidth()-6;
if(intervals<1)//if width is too small then use 1 and forget padding
intervals = 1;
}
double histClassCounts[][] =
new double[intervals]
[m_data.attribute(m_classIndex).numValues()+1];
double barRange = (m_as.numericStats.max - m_as.numericStats.min) /
(double)histClassCounts.length;
m_maxValue = 0;
if(m_colorList.size()==0)
m_colorList.addElement(Color.black);
for(int i = m_colorList.size();
i < m_data.attribute(m_classIndex).numValues()+1; i++) {
Color pc = m_defaultColors[(i-1) % 10];
int ija = (i-1) / 10;
ija *= 2;
for (int j=0;jm_maxValue)
// m_maxValue = histCounts[t];
}
else {
if(m_data.instance(k).isMissing(m_classIndex))
histClassCounts[t-1][0] += m_data.instance(k).weight();
else
histClassCounts[t-1][(int)m_data.instance(k).value(m_classIndex)+1] +=
m_data.instance(k).weight();
//if(histCounts[t-1]>m_maxValue)
// m_maxValue = histCounts[t-1];
}
}
}
catch(ArrayIndexOutOfBoundsException ae) {
System.out.println("t:"+(t)+
" barRange:"+barRange+
" histLength:"+histClassCounts.length+
" value:"+m_data.instance(k).value(m_attribIndex)+
" min:"+m_as.numericStats.min+
" sumResult:"+
(m_data.instance(k).value(m_attribIndex) -
m_as.numericStats.min)+
" divideResult:"+
(float)((m_data.instance(k).value(m_attribIndex) -
m_as.numericStats.min) / barRange)+
" finalResult:"+
Math.ceil((float)((m_data.instance(k).value(m_attribIndex)-
m_as.numericStats.min) / barRange)) );
}
}
for(int i=0; i 0) {
numSparseValues++;
}
}
double[] sparseValues = new double[numSparseValues];
int[] sparseIndices = new int[numSparseValues];
int count = 0;
for (int j = 0; j < histClassCounts[i].length; j++) {
if (histClassCounts[i][j] > 0) {
sparseValues[count] = histClassCounts[i][j];
sparseIndices[count++] = j;
}
}
SparseInstance tempS =
new SparseInstance(1.0, sparseValues, sparseIndices,
histClassCounts[i].length);
histClassCountsSparse[i] = tempS;
}
m_histBarClassCounts = histClassCountsSparse;
m_barRange = barRange;
}
else { //else if the class attribute is numeric or the class is not set
int intervals; double intervalWidth;
//At the time of this coding the
//possibility of datasets with zero instances
//was being dealt with in the
//PreProcessPanel of weka Explorer.
//old method of calculating number of intervals
//intervals = m_as.totalCount>10 ?
// (int)(m_as.totalCount*0.1):(int)m_as.totalCount;
//This uses the M.P.Wand's method to calculate the histogram's
//interval width. See "Data-Based Choice of Histogram Bin Width", in
//The American Statistician, Vol. 51, No. 1, Feb., 1997, pp. 59-64.
//intervalWidth = Math.pow(6D/(-psi(2, g21())*m_data.numInstances() ),
// 1/3D );
//This uses the Scott's method to calculate the histogram's interval
//width. See "On optimal and data-based histograms".
// See Biometrika, 66, 605-610 OR see the same paper mentioned above.
intervalWidth = 3.49 * m_as.numericStats.stdDev *
Math.pow(m_data.numInstances(), -1/3D);
//The Math.max is introduced to remove the possibility of
//intervals=0 and =NAN that can happen if respectively all the numeric
//values are the same or the interval width is evaluated to zero.
intervals = Math.max(1,
(int)Math.round( (m_as.numericStats.max - m_as.numericStats.min) /
intervalWidth) );
//The number 4 below actually represents a padding of 3 pixels on
//each side of the histogram, and is also reflected in other parts of
//the code in the shape of numerical constants like "6" here.
if(intervals > AttributeVisualizationPanel.this.getWidth()) {
intervals = AttributeVisualizationPanel.this.getWidth()-6;
if(intervals<1)
intervals = 1;
}
double[] histCounts = new double[intervals];
double barRange = (m_as.numericStats.max - m_as.numericStats.min) /
(double)histCounts.length;
m_maxValue = 0;
for(int k=0; km_maxValue)
m_maxValue = histCounts[t];
}
else {
histCounts[t-1] += m_data.instance(k).weight();
if(histCounts[t-1]>m_maxValue)
m_maxValue = histCounts[t-1];
}
}
catch(ArrayIndexOutOfBoundsException ae) {
ae.printStackTrace();
System.out.println("t:"+(t)+
" barRange:"+barRange+
" histLength:"+histCounts.length+
" value:"+m_data.instance(k).value(m_attribIndex)+
" min:"+m_as.numericStats.min+
" sumResult:"+
(m_data.instance(k).value(m_attribIndex)-m_as.numericStats.min)+
" divideResult:"+
(float)((m_data.instance(k).value(m_attribIndex) -
m_as.numericStats.min)/barRange)+
" finalResult:"+
Math.ceil( (float)((m_data.instance(k).value(m_attribIndex) -
m_as.numericStats.min) / barRange)) );
}
}
m_histBarCounts = histCounts;
m_barRange = barRange;
}
m_threadRun=false;
m_displayCurrentAttribute = true;
m_doneCurrentAttribute = true;
//Image tmpImg = new BufferedImage(getWidth(), getHeight(),
// BufferedImage.TYPE_INT_RGB);
//drawGraph( tmpImg.getGraphics() );
//img = tmpImg;
AttributeVisualizationPanel.this.repaint();
}
}
/****Code for M.P.Wand's method of histogram bin width selection.
* There is some problem with it. It always comes up -ve value
* which is raised to the power 1/3 and gives an NAN.
* private static final int M=400;
* private double psi(int r, double g) {
* double val;
*
* double sum=0.0;
* for(int i=0; ireturns "count <br> [<bars Range>]" if mouse is
* on the first bar.
* returns "count <br> (<bar's Range>]" if mouse is
* on some bar other than the first one.
* Otherwise it returns ""
*
* @param ev The mouse event
*/
public String getToolTipText(MouseEvent ev) {
if(m_as!=null && m_as.nominalWeights!=null) { //if current attrib is nominal
float intervalWidth = this.getWidth() / (float)m_as.nominalWeights.length;
double heightRatio;
int barWidth, x=0, y=0;
//if intervalWidth is at least six then bar width is 80% of intervalwidth
if(intervalWidth>5) //the rest is padding
barWidth = (int)Math.floor(intervalWidth*0.8F);
else
barWidth = 1; //Otherwise barwidth is 1 & padding would be at least 1.
//initializing x to maximum of 1 or 10% of interval width (i.e. half of
//the padding which is 20% of interval width, as there is 10% on each
//side of the bar) so that it points to the start of the 1st bar
x = x + (int)( (Math.floor(intervalWidth*0.1F))<1 ?
1:(Math.floor(intervalWidth*0.1F)) );
//Adding to x the appropriate value so that it points to the 1st bar of
//our "centered" barplot. If subtracting barplots width from panel width
//gives <=2 then the barplot is not centered.
if(this.getWidth() - (m_as.nominalWeights.length*barWidth+
(int)( (Math.floor(intervalWidth*0.2F))<1 ?
1:(Math.floor(intervalWidth*0.2F)) ) *
m_as.nominalWeights.length) > 2 ) {
//The following amounts to adding to x the half of the area left after
//subtracting from the components width the width of the whole barplot
//(i.e. width of all the bars plus the width of all the bar paddings,
//thereby equaling to the whole barplot), since our barplot is centered.
x += ( this.getWidth() - (m_as.nominalWeights.length*barWidth +
(int)( (Math.floor(intervalWidth*0.2F))<1 ?
1:(Math.floor(intervalWidth*0.2F)) ) *
m_as.nominalWeights.length) ) / 2;
}
for(int i=0; i= x && ev.getX()<=x+barWidth &&
ev.getY() >= this.getHeight() -
Math.round(m_as.nominalWeights[i]*heightRatio) )
return(m_data.attribute(m_attribIndex).value(i)+
" ["+Utils.doubleToString(m_as.nominalWeights[i], 3)+"]");
//otherwise advance x to next bar and check that. Add barwidth to x
//and padding which is max(1, 20% of interval width)
x = x+barWidth+(int)( (Math.floor(intervalWidth*0.2F))<1 ?
1:(Math.floor(intervalWidth*0.2F)) );
}
}
else if(m_threadRun==false && //if attrib is numeric
(m_histBarCounts!=null || m_histBarClassCounts!=null)) {
double heightRatio, intervalWidth;
int x=0, y=0, barWidth;
double bar = m_as.numericStats.min;
//if the class attribute is set and it is nominal
if((m_classIndex >= 0) && (m_data.attribute(m_classIndex).isNominal())) {
//there is 3 pixels of padding on each side of the histogram
//the barwidth is 1 if after removing the padding its width is less
//then the displayable width
barWidth = ((this.getWidth()-6)/m_histBarClassCounts.length)<1 ?
1:((this.getWidth()-6)/m_histBarClassCounts.length);
//initializing x to 3 adding appropriate value to make it point to the
//start of the 1st bar of our "centered" histogram.
x = 3;
if( (this.getWidth() - (x + m_histBarClassCounts.length*barWidth)) > 5 )
x += (this.getWidth() - (x + m_histBarClassCounts.length*barWidth))/2;
heightRatio = (this.getHeight()-(double)m_fm.getHeight())/m_maxValue;
if( ev.getX()-x >= 0) {
//The temp holds the index of the current interval that we are looking
//at
int temp = (int)((ev.getX()-x)/(barWidth+0.0000000001));
if(temp == 0){ //handle the special case temp==0. see footnote 1
double sum=0;
for(int k=0; k" +
Utils.doubleToString(sum, 3) + "
"+
"["+Utils.doubleToString(bar+m_barRange*temp,3)+
", "+Utils.doubleToString((bar+m_barRange*(temp+1)),3)+
"]"+"