提交 477f0884 编写于 作者: K kohsuke

implemented an algorithm that does the smarter Y-axis auto ranging. (#1246)


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@7319 71c3de6d-444a-0410-be80-ed276b4c234a
上级 5812c171
......@@ -859,6 +859,7 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
domainAxis.setCategoryMargin(0.0);
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
ChartUtil.adjustChebyshev(dataset,rangeAxis);
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
StackedAreaRenderer ar = new StackedAreaRenderer2() {
......
......@@ -4,6 +4,8 @@ import hudson.model.AbstractBuild;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.category.CategoryDataset;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
......@@ -97,6 +99,85 @@ public class ChartUtil {
rsp.getWriter().println(ChartUtilities.getImageMap( "map", info ));
}
/**
* Adjusts the Y-axis so that abnormally large value won't spoil the whole chart
* by making everything look virtually 0.
*
* <p>
* The algorithm is based on <a href="http://en.wikipedia.org/wiki/Chebyshev%27s_inequality">Chebyshev's inequality</a>,
* which states that given any number sequence, nore more than 1/(N^2) values are more than N x stddev away
* from the average.
*
* <p>
* So the algorithm is to set Y-axis range so that we can see all data points that are within N x stddev
* of the average. Most of the time, Cebyshev's inequality is very conservative, so it shouldn't do
* much harm.
*
* <p>
* When the algorithm does kick in, however, we can kick out at most 1 in N^2 data points.
* (So for example if N=3 then we can "fix" the graph as long as we only have less than 1/(3*3)=11.111...% bad data.
*
* <p>
* Also see issue #1246.
*/
public static void adjustChebyshev(CategoryDataset dataset, NumberAxis yAxis) {
// first compute E(X) and Var(X)
double sum=0,sum2=0;
final int nColumns = dataset.getColumnCount();
final int nRows = dataset.getRowCount();
for (int i=0; i<nRows; i++ ) {
Comparable rowKey = dataset.getRowKey(i);
for( int j=0; j<nColumns; j++) {
Comparable columnKey = dataset.getColumnKey(j);
double n = dataset.getValue(rowKey,columnKey).doubleValue();
sum += n;
sum2 +=n*n;
}
}
double average = sum/(nColumns*nRows);
double stddev = Math.sqrt(sum2/(nColumns*nRows)-average*average);
double rangeMin = average-stddev*CHEBYSHEV_N;
double rangeMax = average+stddev*CHEBYSHEV_N;
// now see if there are any data points that fall outside (rangeMin,rangeMax)
boolean found = false;
double min=0,max=0;
for (int i=0; i<nRows; i++ ) {
Comparable rowKey = dataset.getRowKey(i);
for( int j=0; j<nColumns; j++) {
Comparable columnKey = dataset.getColumnKey(j);
double n = dataset.getValue(rowKey,columnKey).doubleValue();
if(n<rangeMin || rangeMax<n) {
found = true;
continue; // ignore this value
}
min = Math.min(min,n);
max = Math.max(max,n);
}
}
if(!found)
return; // no adjustment was necessary
// some values fell outside the range, so adjust the Y-axis
// if we are ever to extend this method to handle negative value ranges correctly,
// the code after this needs modifications
min = Math.min(0,min); // always include 0 in the graph
max += yAxis.getUpperMargin()*(max-min);
yAxis.setRange(min,max);
}
public static double CHEBYSHEV_N = 3;
static {
try {
new Font("SansSerif",Font.BOLD,18).toString();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册