Rounding up for a Graph Maximum - c#

I'm graphing some statistics which can be percentages, currency values or plain numbers.
I need to set the maximum value of the graph control's axis to a nice, round number just a bit above the maximum value in the data set. (The graph control's default value is not what I want).
Two things to note:
The value I set for the axis maximum should be minimum 5% above the dataset's maximum value (the less above this the better).
I have 4 horizontal lines above the 0 Y-axis; so ideally the Y-axis maximum should divide nicely by 4.
Sample data might be:
200%, 100%, 100%, 100%, 75%, 50%, 9%
In this case, 220% would be acceptable as the maximum value.
$3500161, $1825223, $1671232, $110112
In this case, $3680000 might be ok. Or $3700000 I suppose.
Can anyone suggest a nice formula for doing this? I might need to adjust settings, like the 5% margin might be changed to 10%, or I might need to change the 4 horizontal lines to 5.

Here is the code I use to create graph axes.
/// <summary>
/// Axis scales a min/max value appropriately for the purpose of graphs
/// <remarks>Code taken and modified from http://peltiertech.com/WordPress/calculate-nice-axis-scales-in-excel-vba/</remarks>
/// </summary>
public struct Axis
{
public readonly float min_value;
public readonly float max_value;
public readonly float major_step;
public readonly float minor_step;
public readonly int major_count;
public readonly int minor_count;
/// <summary>
/// Initialize Axis from range of values.
/// </summary>
/// <param name="x_min">Low end of range to be included</param>
/// <param name="x_max">High end of range to be included</param>
public Axis(float x_min, float x_max)
{
//Check if the max and min are the same
if(x_min==x_max)
{
x_max*=1.01f;
x_min/=1.01f;
}
//Check if dMax is bigger than dMin - swap them if not
if(x_max<x_min)
{
float temp = x_min;
x_min = x_max;
x_max = temp;
}
//Make dMax a little bigger and dMin a little smaller (by 1% of their difference)
float delta=(x_max-x_min)/2;
float x_mid=(x_max+x_min)/2;
x_max=x_mid+1.01f*delta;
x_min=x_mid-1.01f*delta;
//What if they are both 0?
if(x_max==0&&x_min==0)
{
x_max=1;
}
//This bit rounds the maximum and minimum values to reasonable values
//to chart. If not done, the axis numbers will look very silly
//Find the range of values covered
double pwr=Math.Log(x_max-x_min)/Math.Log(10);
double scl=Math.Pow(10, pwr-Math.Floor(pwr));
//Find the scaling factor
if(scl>0&&scl<=2.5)
{
major_step=0.2f;
minor_step=0.05f;
}
else if(scl>2.5&&scl<5)
{
major_step=0.5f;
minor_step=0.1f;
}
else if(scl>5&&scl<7.5)
{
major_step=1f;
minor_step=0.2f;
}
else
{
major_step=2f;
minor_step=0.5f;
}
this.major_step=(float)(Math.Pow(10, Math.Floor(pwr))*major_step);
this.minor_step=(float)(Math.Pow(10, Math.Floor(pwr))*minor_step);
this.major_count=(int)Math.Ceiling((x_max-x_min)/major_step);
this.minor_count=(int)Math.Ceiling((x_max-x_min)/minor_step);
int i_1=(int)Math.Floor(x_min/major_step);
int i_2=(int)Math.Ceiling(x_max/major_step);
this.min_value=i_1*major_step;
this.max_value=i_2*major_step;
}
public float[] MajorRange
{
get
{
float[] res=new float[major_count+1];
for(int i=0; i<res.Length; i++)
{
res[i]=min_value+major_step*i;
}
return res;
}
}
public float[] MinorRange
{
get
{
float[] res=new float[minor_count+1];
for(int i=0; i<res.Length; i++)
{
res[i]=min_value+minor_step*i;
}
return res;
}
}
}
You can the nice max_value and min_value as calculated from the initialized for Axis given the mathematical min. max. values in x_min and x_max.
Example:
new Axis(0,3500161) calculates max_value = 4000000.0
new Axis(0,1825223) calculates max_value = 2000000.0
new Axis(0,1671232) calculates max_value = 1800000.0
new Axis(0, 110112) calculates max_value = 120000.0

For your 1st query use:
DataView data = new DataView(dt);
string strTarget = dt.Compute("MAX(target)", string.Empty).ToString();// target is your column name.
int tTarget = int.Parse(strTarget.Equals("") ? "0" : strTarget); // Just in case if your string is empty.
myChart.ChartAreas[0].AxisY.Maximum = myChart.ChartAreas[0].AxisY2.Maximum = Math.Ceiling(tTarget * 1.1); // This will give a 10% plus to max value.
For the 2nd point, i guess you can figure this out with minor/major axis interlaced and offset properties.

First, you'll need to decide on a range for (top of graph)/(max data point). You have this bounded on the lower end as 1.05; reasonable upper bounds might be 1.1 or 1.15. The wider the range, the more empty space may appear at the top of the graph, but the "nicer" the numbers may be. Alternatively, you can pick a "niceness" criterion first and then pick the smallest sufficiently nice number where the above ratio is at least 1.05.
You can also improve the "niceness" of the intervals by loosening that lower bound, for instance lowering it to 1.02 or even 1.0.
EDIT: In response to comment.
What you'll have to do to find a good max size is take your max value plus margin, divide it by the number of intervals, round it upward to the nearest "nice" value, and multiply by the number of intervals. A reasonable definition of "nice" might be "multiple of 10^(floor(log_10(max value)) - 2)" A looser definition of niceness will give you (on average) less extra margin at the top.

Related

AdaBoost repeatedly chooses same weak learners

I have implemented a version of the AdaBoost boosting algorithm, where I use decision stumps as weak learners. However often I find that after training the AdaBoost algorithm, a series of weak learners is created, such that this series is recurring in the whole set. For example, after training, the set of weak learners looks like A,B,C,D,E,D,E,D,E,D,E,F,E,D,E,D,E etc.
I believe I am updating the weights of the data properly after each assignment of a new weak learner. Here I classify each data point and then set the weight of this data point.
// After we have chosen the weak learner which reduces the weighted sum error by the most, we need to update the weights of each data point.
double sumWeights = 0.0f; // This is our normalisation value so we can normalise the weights after we have finished updating them
foreach (DataPoint dataP in trainData) {
int y = dataP.getY(); // Where Y is the desired output
Object[] x = dataP.getX();
// Classify the data input using the weak learner. Then check to see if this classification is correct/incorrect and adjust the weights accordingly.
int classified = newLearner.classify(x);
dataP.updateWeight(y, finalLearners[algorithmIt].getAlpha(), classified);
sumWeights += dataP.getWeight();
}
Here is my classify method in the WeakLearner class
// Method in the WeakLearner class
public int classify(Object[] xs) {
if (xs[splitFeature].Equals(splitValue))
return 1;
else return -1;
}
Then I have a method which updates the weight of a DataPoint
public void updateWeight(int y, double alpha, int classified) {
weight = (weight * (Math.Pow(e, (-y * alpha * classified))));
}
And I'm not sure why this is happening, are there any common factors why the same weak learners would generally be chosen?
You could increase the value of alpha and check. Maybe, not enough weight is being given to the misclassified samples, hence ,they are showing up again and again.

c# Random number from weighted average of values

I'd like to take a range of four random integers between 1-64, and generate a random value somewhere within the range but leaning towards a more weighted average.
The practical application is that you take a pixel, grab the 4 surrounding pixels and use those plus the current pixel to generate a value that can then be used as the base weight for a Gaussian random number generator. So you have a pixel of 10 brightness, surrounded by 8,8,9,9. Add them all up, average out to 8.8. 8.8 is then the weight for the Gaussian random number generator. So you have a random result within a range, but close to the average brightness which is 8.8, and still with some element of randomness.
The issue comes when you have wide variations because of random noise.
To give a pseudo example of how I would like it to work..
Input = [1,16,19,21]
The average of this is 14.25, but that has too much movement because of the "1" bringing the average way down. The average of this should be more around the 18 mark, because more of the numbers are clustered around that area.
I would like to see a random result coming out that is between 1 and 64, but heavily weighted between 15 and 22, with a lower possibility of it being towards the 1(Because it is still within the range as a whole) and a much lower possibility of it being over 22(Because that is completely outside of the range).
Additional The purpose of this is to generate a galactic map. I have got to the point where I have a good set of galaxy shaped data, giving me the rough density of each sector on the map. Now I need to generate specific sets of data and generate exact numbers of stars in each sector. Taking the average of the 4 surrounding sectors and using that to work out how "dense" this sector should be is the main purpose. The main thing I want to avoid is that sectors bordering an empty region of space do not also end up mostly empty, as this does not fit with general observations of galaxies.
You could imagine that the four numbers are points in a line, the x axis. Around that points there is a sphere of probability with a radius of 64, with the probability more concentrated in the proximity of the points rather than on the edges. Pick randomly one of the four points, calculate a random point inside the sphere of that number and take its x coordinate. Repeat if it is out of the range 1..64.
using System;
using System.Collections.Generic;
namespace ProbabilityDistribution1
{
class Program
{
// This derived class converts the uniformly distributed random
// numbers generated by base.Sample( ) to another distribution.
class RandomProportional : Random
{
// The Sample method generates a distribution more concentrated around the 0, in the range [0.0, 1.0].
protected override double Sample()
{
double BSample = base.Sample();
const double concentrationAroundInputs = 5;//more concentrated when greater
double result = Math.Pow(BSample, concentrationAroundInputs);
return result;
}
}
static double XCoordinateOfRandomUnitInsideSphere(Random aRandom)
{
//Even with uniform distribution the probability of exiting is greater than 0.5 on each iteration
while (true)
{
double x = aRandom.NextDouble();
double y = aRandom.NextDouble();
double z = aRandom.NextDouble();
if ((x * x + y * y + z * z) < 1) //inside the sphere
{
return x;
}
}
}
static void TestDistribution()
{
double[] Input = { 1, 16, 19, 21 };
List<int> sampleValues = new List<int>();
Random aRandom = new Random();
RandomProportional aRandomProportinal = new RandomProportional();
for (int i = 0; i < 100; i++)
{
int value = 0;
do
{
int indexChosen = aRandom.Next(4);
double xCoordinate = XCoordinateOfRandomUnitInsideSphere(aRandomProportinal);
if (aRandom.Next(2)==0)
{
xCoordinate = -xCoordinate;
}
double xRandomResult = xCoordinate * 64;
value = (int)(Input[indexChosen] + xRandomResult);
} while (value < 1 || value > 64);
sampleValues.Add((int)value);
}
sampleValues.Sort();
Console.WriteLine();
foreach (int i in sampleValues)
{
Console.Write(" {0:00} ", i);
}
Console.WriteLine();
}
static void Main(string[] args)
{
TestDistribution();
Console.WriteLine("end");
Console.ReadLine();
}
}
}

Get the most used and most "different" colors from image

I've been browsing the Web for an algorithm which analyzes an image and returns the most different and used colors -- but with no luck.
For instance;
If I'd have a bitmap with 25% red (255,0,0) in RGB, 50% fully blue (0,0,255) and 25% pink (255,0,255)
I would want the algorithm to return these three (or more, depending on avalible colors) colors sorted in the usage, so:
1. Blue
2. Red / Pink
3. Red / Pink
Anyone know some way I could start this? Maybe some articles to read etc. I've never used images in C# like this.
If I understood the question correctly, this may help you (of course you have to read the data according to your needs then):
/// <summary>
/// Gets the bitmap image color statistics
/// </summary>
/// <param name="bit">The bitmap image you want to analyze</param>
/// <returns></returns>
public static List<KeyValuePair<Color, int>> GetStatistics(Bitmap bit)
{
Dictionary<Color, int> countDictionary = new Dictionary<Color, int>();
for (int wid = 0; wid < bit.Width; wid++)
{//for every column in the image
for (int he = 0; he < bit.Height; he++)
{//for every row in the image
//Get the color of the pixel reached (i.e. at the current column and row)
Color currentColor = bit.GetPixel(wid, he);
//If a record already exists for this color, set the count, otherwise just set it as 0
int currentCount = (countDictionary.ContainsKey(currentColor) ? countDictionary[currentColor] : 0);
if (currentCount == 0)
{//If this color doesnt already exists in the dictionary, add it
countDictionary.Add(currentColor, 1);
}
else
{//If it exists, increment the value and update it
countDictionary[currentColor] = currentCount + 1;
}
}
}
//order the list from most used to least used before returning
List<KeyValuePair<Color, int>> l = countDictionary.OrderByDescending(o => o.Value).ToList();
return l;
}
}
most used colors
With a little google effort you would find Histogram. If you want to use shades as separate color then you have 256^3 colors. So either use some dynamic list instead or ignore few least significant bits to lower the number a bit. You can also change the dynamic range by normalization of colors.
black is black
and for everything else change vector size to Max for example
Max = 2^5-1 = 31
normalized color = color * Max / |color|
Now the algorithm:
create a counter table cnt for all combinations of colors
for Max = 31 the size would be 2^15 = 32768. Set the whole table to zero.
int cnt[32768];
for (int i=0;i<32768;i++) cnt[i]=0;
go through entire image and for each pixel
normalize its color
convert it to address (for example adr = (R) | (G<<5) | (B<<10))
increment its counter cnt[adr]++;
After this you have histogram in cnt[]. so now index sort it by value of cnt and you have obtained the colors sorted by their usage
most different color
How would you define it? I would use data from histogram and do search for max distance between 2 colors in it (after normalization)
d = |color1 - color2|
Do not need to sqrt it ... if you use d^2 you will obtain the same results. Ignore all entries where cnt[adr]==0 (that is unused color). This is still O(n^2) ... more like ~T(n*n/2) ... in runtime terms but n is not the number of pixels in image. instead it is just number of different used colors in image which is far less ... Also after index sort of histogram and removing/ignoring all the cnt[adr]==0 entries even lover.

C# Chart AxisY logarithmic is not working

I'm trying to draw a signal response on a chart and I need a logarithmic scale for X and Y.
I defined two functions, one for X axis
private void Configure_Axis_X(bool Logaritmic, double Maximum, double Minimum, double Interval)
{
CH_EQ_Chart.ChartAreas[0].AxisX.IsLogarithmic = Logaritmic;
CH_EQ_Chart.ChartAreas[0].AxisX.Minimum = Minimum;
CH_EQ_Chart.ChartAreas[0].AxisX.Maximum = Maximum;
CH_EQ_Chart.ChartAreas[0].AxisX.Interval = Interval;
CH_EQ_Chart.ChartAreas[0].AxisX.MajorGrid.LineColor = Color.Black;
CH_EQ_Chart.ChartAreas[0].AxisX.MajorGrid.LineDashStyle = Graph.ChartDashStyle.Dash;
}
and one for Y axis
private void Configure_Axis_Y(bool Logaritmic, double Maximum, double Minimum, double Interval)
{
CH_EQ_Chart.ChartAreas[0].AxisY.IsLogarithmic = Logaritmic;
CH_EQ_Chart.ChartAreas[0].AxisY.Minimum = Minimum;
CH_EQ_Chart.ChartAreas[0].AxisY.Maximum = Maximum;
CH_EQ_Chart.ChartAreas[0].AxisY.Interval = Interval;
CH_EQ_Chart.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.Black;
CH_EQ_Chart.ChartAreas[0].AxisY.MajorGrid.LineDashStyle = Graph.ChartDashStyle.Dash;
}
the response to draw is expressed in Decibel and I'd like to have logarithmic scale also for Y.
When I have the array with values, I get minimum and maxim value and I try to use the function above with
double Abs_Max = Math.Max(y.Max(), z.Max());
double Abs_Min = Math.Min(y.Min(), z.Min());
Configure_Axis_Y(true, Abs_Max + Abs_Max/10, Abs_Min + Abs_Min/10, 20);
but when I select islogaritmic = true a red cross appears instead the graph.
If I set islogaritmic = false the picture appears right.
The red cross is the chart's way of handling an exception during drawing. The most likely culprit when dealing with log scale is that one or more data points have zero or negative values. Check in the debugger what Abs_Min is when the method is called, as it's likely you're somehow getting zero or negative values in there.

Invert windows forms vertical scrollbar

I'm making a winforms app c#. The vertical scroll bar min value is at the top and max at the bottom, and scrolling down increases the value and vice versa. Is there a way to invert it, so that up is higher and down is lower.
You cannot actually "see" the value of the scroll bar just by looking at it, so, in other words, there is no actual difference between having min at the top, max at the bottom, and then just inverting the value when you access it:
private void ScrollBar_Scroll(object sender, ScrollEventArgs e)
{
// get the value (0 -> 100)
int value = scrollBar.Value;
// invert it (100 -> 0)
value = 100 - value;
// display it
someLabel.Text = value.ToString();
}
Of course, you can also override the VScrollBar class and add your own "inverted value" property:
public class InvertedScrollBar : VScrollBar
{
/// <summary>
/// Gets or sets the "inverted" scrollbar value.
/// </summary>
/// <value>The inverted value.</value>
public int InvertedValue
{
get
{
int offset = this.Value - this.Minimum;
return this.Maximum - offset;
}
set
{
int offset = this.Maximum - value;
this.Value = this.Minimum + offset;
}
}
}
Note that Maximum still has to be larger than Minimum when configuring it.
The values returned by the Value property of a ScrollBar go from scrollBar.Minimum to scrollBar.Maximum - scrollBar.LargeChange.
Thus if a scroll bar has Minimum of 5, Maximum of 15, and LargeChange (which doubles as the visible portion of the scrolling range) is 3, then the possible return values go from 5 to 12.
So, to invert the value, you actually want to use:
scrollBar.Minimum + scrollBar.Maximum - scrollBar.LargeChange - scrollBar.Value
(Normally you can think of Value as position of the left or top edge of the thumb. The formula above will give you the bottom edge of the thumb. If you still want the top edge (i.e. values going from 8 to 15 in the example above), then use:
scrollBar.Minimum + scrollBar.Maximum - scrollBar.Value

Categories

Resources