188 lines
7.9 KiB
C#
188 lines
7.9 KiB
C#
//=================================================================
|
|
// File: SpikeRemoval.cs
|
|
//
|
|
// Namespace: System.Web.UI.DataVisualization.Charting.Utilities
|
|
//
|
|
// Classes: SpikeRemoval
|
|
//
|
|
// Purpose: Removes spikes from data
|
|
//
|
|
//
|
|
//===================================================================
|
|
// Chart Control for ASP.Net
|
|
// Copyright ?Microsoft Corporation, all rights reserved
|
|
//===================================================================
|
|
|
|
using System;
|
|
using System.Data;
|
|
using System.Configuration;
|
|
using System.Drawing;
|
|
using System.Web.UI.DataVisualization.Charting;
|
|
|
|
namespace System.Web.UI.DataVisualization.Charting.Utilities
|
|
{
|
|
/// <summary>
|
|
/// Spike removal is a utility used to remove high and low spikes from a graph. This means that the
|
|
/// chart axis scaling will be changed so that data that was difficult to see and analyze will be easier
|
|
/// to see after anomaly spikes have been removed.
|
|
/// </summary>
|
|
public class SpikeRemoval
|
|
{
|
|
#region Members
|
|
private bool mySetCutoffLabels;
|
|
private MarkerStyle myRemovedPointStyle;
|
|
private float myMaximum;
|
|
private float myMinimum;
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// Sets whether or not labels are set on each cut off point. If they are, they will show up on the chart and provide
|
|
/// extra clarification if the tooltip is not enough.
|
|
/// </summary>
|
|
public bool SetCutoffLabels
|
|
{
|
|
get { return mySetCutoffLabels; }
|
|
set { mySetCutoffLabels = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Holds the style that is used for the marker of any deleted points.
|
|
/// </summary>
|
|
public MarkerStyle RemovedPointStyle
|
|
{
|
|
get { return myRemovedPointStyle; }
|
|
set { myRemovedPointStyle = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the maximum value of the data after it has had the spikes removed. This value also has
|
|
/// the tolerance factored in.
|
|
/// </summary>
|
|
public float Maximum
|
|
{
|
|
get { return myMaximum; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the minimum value of the data after it has had the spikes removed. This value also has
|
|
/// the tolerance factored in.
|
|
/// </summary>
|
|
public float Minimum
|
|
{
|
|
get { return myMinimum; }
|
|
}
|
|
#endregion
|
|
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Default Constructor.
|
|
/// </summary>
|
|
public SpikeRemoval()
|
|
{
|
|
//Default removed point style to a diamond.
|
|
myRemovedPointStyle = MarkerStyle.Diamond;
|
|
|
|
//Default labels to off.
|
|
mySetCutoffLabels = false;
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
/// <summary>
|
|
/// RemoveSpikes will remove the high and low spikes off of a graph. The data within the series
|
|
/// provided will be modified, and for best results, the chart containing it should have axis
|
|
/// scaled automatically.
|
|
/// </summary>
|
|
/// <param name="dataseries">The series which contains the data to be analyzed and modified. It is assumed that
|
|
/// Y-values are contained in YValues[0]. Cases contrary to this will produce unexpected results.</param>
|
|
/// <param name="range">The percentage range of data to be kept. Anything that lies outside of the range
|
|
/// will be considered a spike.</param>
|
|
/// <param name="tolerance">The percentage a spike can be outside of the range but still included. The percentage
|
|
/// is based on the maximum or minimum value in the range.</param>
|
|
public void RemoveSpikes(Series dataseries, int range, int tolerance)
|
|
{
|
|
//Assure range and tolerance are a percentage.
|
|
//Range is more strict in that at least 1% of the data must be included in the range, whereas it is
|
|
//possible to have 0% tolerance.
|
|
if ((range < 1 || range > 100))
|
|
throw new ArgumentOutOfRangeException("range", "Range must be a percentage between 1 and 100");
|
|
if ((tolerance < 0 || tolerance > 100))
|
|
throw new ArgumentOutOfRangeException("tolerance", "Tolerance must be a percentage between 0 and 100");
|
|
|
|
//Data values and indices hold the y values and the indices of the points in arrays.
|
|
float[] datavalues = new float[dataseries.Points.Count];
|
|
int[] indices = new int[dataseries.Points.Count];
|
|
|
|
//Percent and number hold the actual values calculated from the range.
|
|
float percent = 0.0f;
|
|
int number = 0;
|
|
|
|
//Copy all y values into an array and store the indices.
|
|
for (int i = 0; i < dataseries.Points.Count; i++)
|
|
{
|
|
datavalues[i] = (float)dataseries.Points[i].YValues[0];
|
|
indices[i] = i;
|
|
}
|
|
|
|
//Sort the array and indices.
|
|
Array.Sort(datavalues, indices);
|
|
|
|
//Calculate the percent that has to come off each side of the data.
|
|
//ie. With a range of 80%, 20% of the data is being cut off, and 10% is coming off each side.
|
|
percent = ((100 - (float)range) / 2) / 100;
|
|
|
|
//Calculate the actual number of points coming off of each side.
|
|
//ie. With a percent of 10% and 100 data points, 10 points are coming off of each side.
|
|
number = (int)System.Math.Round(dataseries.Points.Count * percent, 0);
|
|
|
|
//Set the maximum and minimum values.
|
|
myMinimum = (float)(dataseries.Points[indices[number]].YValues[0] - System.Math.Abs(dataseries.Points[indices[number]].YValues[0] * (((float)tolerance / 100))));
|
|
myMaximum = (float)(dataseries.Points[indices[dataseries.Points.Count - number - 1]].YValues[0] + System.Math.Abs(dataseries.Points[indices[dataseries.Points.Count - number - 1]].YValues[0] * (((float)tolerance / 100))));
|
|
|
|
//Cut the low spikes off.
|
|
for (int i = 0; i < number; i++)
|
|
{
|
|
//Don't cut the spike if it's within the tolerance value.
|
|
if (dataseries.Points[indices[i]].YValues[0] < (myMinimum))
|
|
{
|
|
//Assign the tooltip to the point
|
|
dataseries.Points[indices[i]].ToolTip = "Value: " + dataseries.Points[indices[i]].YValues[0];
|
|
|
|
//Assign the label to the point
|
|
if (mySetCutoffLabels)
|
|
dataseries.Points[indices[i]].Label = "Value: " + dataseries.Points[indices[i]].YValues[0];
|
|
|
|
//Reassign the value to the minimum number allowed
|
|
dataseries.Points[indices[i]].YValues[0] = myMinimum;
|
|
|
|
//Set the marker point
|
|
dataseries.Points[indices[i]].MarkerStyle = myRemovedPointStyle;
|
|
}
|
|
}
|
|
|
|
//Cut the high spikes off.
|
|
for (int i = dataseries.Points.Count - number; i < dataseries.Points.Count; i++)
|
|
{
|
|
//Don't cut the spike if it's within the tolerance value.
|
|
if (dataseries.Points[indices[i]].YValues[0] > (myMaximum))
|
|
{
|
|
//Assign the tooltip to the point
|
|
dataseries.Points[indices[i]].ToolTip = "Value: " + dataseries.Points[indices[i]].YValues[0];
|
|
|
|
//Assign the label to the point
|
|
if (mySetCutoffLabels)
|
|
dataseries.Points[indices[i]].Label = "Value: " + dataseries.Points[indices[i]].YValues[0];
|
|
|
|
//Reassign the value to the maximum number allowed
|
|
dataseries.Points[indices[i]].YValues[0] = myMaximum;
|
|
|
|
//Set the marker point
|
|
dataseries.Points[indices[i]].MarkerStyle = myRemovedPointStyle;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|