RangeColumn: Show difference between two y values as Label - c#

I have a RangeColumn with two y values per DataPoint. In the chart I would like to see the datapoint labeled with the difference of the two y values.
I tried:
DataPoint p = new DataPoint();
p.SetValueXY(d.Date, prev, prev + biggerResult.Count);
p.Label = biggerResult.Count.ToString();
s_AC_Checked.Points.AddXY(d.Date, prev, prev+biggerResult.Count);
Also this is not bringing me further:
s_AC_Checked.Label = "#VALY1\n#VALY2\n#VALY2-#VAL1\n#LABEL";
No chance to see the difference between the y values.
Here is what appears:
The right column should show "5" as a difference ...
Picture:
https://i.stack.imgur.com/r7Ejn.png

Found it. Doing it after the point was added to the series does the trick ...
DataPoint p = new DataPoint();
p.SetValueXY(d.Date, prev, prev + biggerResult.Count);
int index = s_AC_Checked.Points.AddXY(d.Date, prev, prev+biggerResult.Count);
DataPoint pt = s_AC_Checked.Points[index];
pt.Label = biggerResult.Count.ToString();
pt.SetCustomProperty("LabelStyle", "Left");
pt.LabelForeColor = Color.Black;
pt.IsValueShownAsLabel = true;

Related

generate a trendline from files in a chart

I want to be able to fetch .csv files from a folder and plot them up in a chart.
Currently, I'm just saving the files and displaying an individual curve like this:
RunTest function:
public List<Tuple<double,double>> runTest()
{
_dpg = new Root(this, "english", false);
_dpg.Init();
var filename = "Dpg10Export_" + DateTime.Now.ToString("yyyyMMdd_HHmm") + ".csv";
List<Tuple<double, double>> results = new List<Tuple<double, double>>();
var measurement = new Measurement();
var resultCode = RunMeasurement(60, 1000, 2200, ref measurement, null /* TODO: ref ProgressBar? */);
using (var fileStream = new StreamWriter(filename))
{
var count = measurement.I_inc_Values.Count;
for (var i = 0; i < count; i++)
{
var item = measurement.I_inc_Values[i + 1];
var current = (float)Convert.ToDouble(item);
item = measurement.LI_inc_Values[i + 1];
var inductance = (float)Convert.ToDouble(item);
var line = current.ToString() + ";" + inductance.ToString() + ";";
fileStream.WriteLine(line);
currentList.Add(current);
inductanceList.Add(inductance);
results.Add(new Tuple<double, double>(current,inductance));
if (i == 0 || (i + 1) % 32 == 0)
{
Console.WriteLine((i + 1) + ": " + line);
}
}
}
return results;
}
This code produces a csv-file that looks something like this:
0,22 | 0,44
0,32 | 0,54
0,44 | 0,65
And those values produce a curve that looks like this:
When you click on the "get Waveform" button, the curve above is generated. However, I want to display all the curves that has been generated, and a trendline as well. How would I achieve this?
void BindData(List<Tuple<double,double>> results)
{
chart.Series.Clear();
var series1 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "curr/induc",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = true,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
foreach (var i in results)
{
series1.Points.AddXY(i.Item2,i.Item1);
}
chart.Invalidate();
chart.Series.Add(series1);
}
private void getWaveformBtn_Click(object sender, EventArgs e)
{
Dpg10Client.Dpg10Settings settings = new Dpg10Client.Dpg10Settings();
Dpg10Instrument hej = new Dpg10Instrument(settings);
List<Tuple<double,double>> results = hej.runTest();
double current, inductance;
foreach(var i in results)
{
current = i.Item1;
inductance = i.Item2;
textBoxWaveformInput.Text += current.ToString() + inductance.ToString();
}
BindData(results);
}
TL;DR
Parse the information from the CSV files to generate curves, and create a trendline based on those files.
Marked as duplicate: That answer is regarding a straight line, these values can fluctuate in a curve.
There are many ways to solve most of the various problems in your question.
Let me tackle only the one that actually has to do with calculating an average from multiple series, i.e. will not deal with creating a Series with data from a CSV file.
There is a built-in class that can do all sorts of advanced math, both financial and statistical, but I didn't find one that will help in creating an average line/curve over more than one series.
So let's do it ourselves..
The first issue is that to calculate averages we need not just data but the data, that is their x-values, must be grouped into 'bins' from which we want to get the averages.
Unless the data already are grouped like that, e.g. because they have one value per series per day, we need to create such groups.
Let's first collect all the points from all series we want to handle; you may need to adapt the loop to include just your set of series..:
var allPoints = new List <DataPoint>();
for (int s = 0; s < 3; s++) // I know I have created these three series
{
Series ser = chartGraphic.Series["S" + (s+1)];
allPoints.AddRange(ser.Points);
}
Next we need to decide on a bin range/size, that is a value that determines which x-values shall fall into the same bin/group. I chose to have 10 bins.
So we best get the total range of the x-values and divide by the number of bins we want..:
double xmax = allPoints.Max(x => x.XValue);
double xmin = allPoints.Min(x => x.XValue);
int bins = 10;
double groupSize = (xmax - xmin) / (bins - 1);
Next we do the actual math; for this we order our points, group them and select the average for each grouped set..:
var grouped = allPoints
.OrderBy(x => x.XValue)
.GroupBy(x => groupSize * (int)(x.XValue /groupSize ))
.Select(x => new { xval = x.Key, yavg = x.Average(y => y.YValues[0]) })
.ToList();
Now we can add them to a new series to display the averages in the bins:
foreach (var kv in grouped) avgSeries.Points.AddXY(kv.xval + groupSize/2f, kv.yavg);
I center the average point in the bins.
Here is an example:
A few notes on the example:
The average line doesn't show a real 'trend' because my data are pretty much random.
I have added Markers to all series to make the DataPoints stand out from the lines. Here it is how I did it for the averages series:
avgSeries.MarkerStyle = MarkerStyle.Cross;
avgSeries.MarkerSize = 7;
avgSeries.BorderWidth = 2;
I have added a StripLine to show the bins. Here is how:
StripLine sl = new StripLine();
sl.BackColor = Color.FromArgb(44,155,155,222);
sl.StripWidth = groupSize;
sl.Interval = groupSize * 2;
sl.IntervalOffset = chartGraphic.ChartAreas[0].AxisX.IntervalOffset;
chartGraphic.ChartAreas[0].AxisX.StripLines.Add(sl);
I have also set one point to have a really low value of -300 to demonstrate how this will pull down the average in its bin. To keep the chart still nicely centered on the normal range of data I have added a ScaleBreakStyle to the y-axis:
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.BreakLineStyle = BreakLineStyle.Wave;
chartGraphic.ChartAreas[0].AxisY.ScaleBreakStyle.Enabled = true;

Get the inner points which are on a straight line

The question I'm about to ask might seem a Geometry question at the first glance, but it actually can be solved by using LINQ, at least I hope so!
I have 5 points on a straight line two of them are at the ends of the line. How can I select the ones that are in the interior of the line (not at the end points) using LINQ?
public class Point
{
public double X;
public double Y;
}
var listOfPointsOnALine = new List<Point>
{
new Point { X = 2500, Y = 50 },
new Point { X = 2540, Y = 112.5 },
new Point { X = 2580, Y = 175 },
new Point { X = 2620, Y = 237.5 },
new Point { X = 2660, Y = 300 },
}
So using some LINQ on the list above should give me the list below:
innerPointsOnALine: {(2540, 112.5), (2580, 175), (2620, 237.5)}
The points are ordered in the original list
The points should be in the same order they appear in the original list (listOfPointsOnALine)
If I understand it correctly then I think you are looking for:
var newList = listOfPointsOnALine
.Skip(1)
.Take(listOfPointsOnALine.Count - 2)
.ToList();
You may have to check for length of list before doing this.

Add title to my series line [mschart]

I am using Chart control to display my network statistics (download, upload):
chart1.Titles.Add("Test Chart");
Series seriesDownload = new Series("KB/s");
seriesDownload.Color = Color.DarkBlue;
seriesDownload.ChartType = SeriesChartType.Spline;
seriesDownload.BorderWidth = 2;
chart1.Series.Add(seriesDownload);
Series seriesPps = new Series("pps");
seriesPps.Color = Color.Black;
seriesPps.ChartType = SeriesChartType.Spline;
seriesPps.BorderWidth = 2;
chart1.Series.Add(seriesPps);
Is it possible to add text near each line in order to distinguish between both colors ?
private void chartTimer_Tick(object sender, EventArgs e)
{
chart1.Series[1].LegendText = chart1.Series[1].Name = str + " KB/s";
DataPoint Point = chart1.Series[1].Points[chart1.Series[1].Points.Count - 1];
Point.Label = chart1.Series[1].Name;
DataPoint _point = default(DataPoint);
foreach (DataPoint item in chart1.Series[1].Points)
{
item.Label = "";
item.MarkerStyle = MarkerStyle.None;
}
}
Something like this will add a label with the series name to the last point on your series. This is VB.NET but you should be able to parse it to C#
'remove all previous datapoint labels
Dim _point As DataPoint
For Each _point In Chart1.Series(i).Points
_point.Label = ""
_point.MarkerStyle = MarkerStyle.None
Next
'add label to last point
Dim Point As DataPoint = Chart1.Series(i).Points(Chart1.Series(i).Points.Count - 1)
Point.Label = Chart1.Series(i).Name
Point.MarkerStyle = MarkerStyle.Circle
Or are you looking to populate the legend with the series name?? then
Chart1.Series(i).LegendText = Chart1.Series(i).Name
EDIT based on the askee submitted code in C#
private void chartTimer_Tick(object sender, EventArgs e)
{
foreach (DataPoint item in chart1.Series[1].Points)
{
item.Label = "";
}
chart1.Series[1].LegendText = chart1.Series[1].Name = str + " KB/s";
DataPoint Point = chart1.Series[1].Points[chart1.Series[1].Points.Count - 1];
Point.Label = chart1.Series[1].Name;
}
Changing the name of the series is not good practice, as the SeriesCollection can be indexed by name (e.g., chart1.Series["MySeries"]) which might then fail after the name change.
If you're trying to add a static label at the end of the code, you can do it as a smart label. Look into the "#VALX#", "#VAL#" modifiers to use in labels:
DataPoint Point = chart1.Series[1].Points[chart1.Series[1].Points.Count - 1];
Point.Label = "#VAL" + " kB/s";
which will automatically add the current x or y value to the label. See http://msdn.microsoft.com/en-us/library/dd456687(v=vs.110).aspx
As an aside, it's very helpful to put the names of your Series into const variables in the class (or static variables in another class), so you can index them without fear of typos. Makes your code much easier to understand, too.
private const string _downloadSeries = "download";
// in some initialization method
Series seriesDownload = new Series(_downloadSeries);
chart1.Series.Add(seriesDownload);
// Access the series
DataPoint point = chart1.Series[_downloadSeries].Points[0];
for example.
EDIT: If you just want to distinguish the two colors, that's exactly what a legend is for. If you want to show the latest value as well, then this code will do that

Chart control data series

I have a chart control.I am plotting price along y axis and month-year along x axis.
I add series1 1st and then series2 to the same chart area.
Then I plot the points for series 1 and 2 using the below code
curveChart.Series.Add("Series1");
curveChart.Series["Series1"].XValueType = ChartValueType.DateTime;
curveChart.Series["Series1"].Points.DataBind(list1, "MonthYear", "PriceValue", null);
curveChart.Series["Series1"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
curveChart.Series["Series1"].BorderWidth = 3;
curveChart.ChartAreas["0"].AxisX.Interval = 1;
curveChart.Series.Add("Series2");
curveChart.Series["Series2"].XValueType = ChartValueType.DateTime;
curveChart.Series["Series2"].Points.DataBind(list2, "MonthYear", "PriceValue", null);
curveChart.Series["Series2"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
curveChart.Series["Series2"].BorderWidth = 3;
curveChart.ChartAreas["0"].AxisX.Interval = 1;
The problem I am facing is that list2 contains data only till Dec-2015 and list1 contains data till Dec-2016 but when the graph is plotted both the lines in the graph extend upto Dec-2016 though list2 doesnt have data till Dec-2016.How can I solve this?
I tried to simulate your problem. I added two data series one with 3 points, one with 2 points. The chart rendered correctly. This makes me think you are going to have to massage your data before you bind it.
curveChart.Series.Clear();
curveChart.Series.Add("Series1");
curveChart.Series["Series1"].XValueType = ChartValueType.DateTime;
curveChart.Series["Series1"].Points.AddXY(DateTime.Now, 12.00m);
curveChart.Series["Series1"].Points.AddXY(DateTime.Now.AddDays(1), 13m);
curveChart.Series["Series1"].Points.AddXY(DateTime.Now.AddDays(2), 8m);
curveChart.Series["Series1"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
curveChart.Series["Series1"].BorderWidth = 3;
curveChart.ChartAreas["0"].AxisX.Interval = 1;
curveChart.Series.Add("Series2");
curveChart.Series["Series2"].XValueType = ChartValueType.DateTime;
curveChart.Series["Series2"].Points.AddXY(DateTime.Now, 5.00m);
curveChart.Series["Series2"].Points.AddXY(DateTime.Now.AddDays(1), 7m);
curveChart.Series["Series2"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
curveChart.Series["Series2"].BorderWidth = 3;
curveChart.ChartAreas["0"].AxisX.Interval = 1;

Getting Values of other Series through tooltip

I'm having a Chart whose data is coming from a list.
This class has id and count1 and count2 as Properties...
Now, i have a list of class...where the values are...
Id Count1 Count2
1 -10 20
2 -15 15
Now,
i do a simple bind...with multiple series
Chart1.DataSource = ListObjOfThatClass
Chart1.Series[0].XValueMember = "Id";
Chart1.Series[0].YValueMembers = "Count1";
Chart1.Series[1].YValueMembers = "Count2";
Chart1.DataBind();
Now, everthing works fine..
My Que: When i hover over the DataSeries, i show a tooltip for that particular YValueMember as "#VALY";
Chart1.Series[0].ToolTip = "#VALY";
Is there any way that I can show the value present in the other series?
i.e
Count2 value, of the series[1].YValueMember which I initialized earlier...??
Thanks
The easier way is too create your own DataPoint for the series, and not use the datasource. Then you can put whatever you want in the tooltip:
foreach (var o in ListObjOfThatClass)
{
var p1 = new DataPoint();
p1.SetValueXY(o.Id, o.Count1);
p1.ToolTip = string.Format("{0}", o.Count2);
Chart1.Series[0].Points.Add(p1);
var p2 = new DataPoint();
p2.SetValueXY(o.Id, o.Count2);
Chart1.Series[1].Points.Add(p2);
}

Categories