Livecharts showing "-100000000000000 μ" instead of -100 M in a StackedColumnSeries - c#

When a value of -1e8 is added to a StackedColumnSeries in a SKCartesianChart, the value is labelled with -100000000000000 μ instead of -100 M as expected.
I suspect this is a bug in the prerelease version of livecharts. I've reported it, but thought to ask here in case anyone's seen the same problem and has a workaround.
var chart = new SKCartesianChart
{
Series = new List<ISeries>
{
new StackedColumnSeries<decimal> {Values = new [] {1e8m}},
new StackedColumnSeries<decimal> {Values = new [] {-1e8m}}, // -100000000000000 μ
}
};

Thanks for the report on Github, this is a bug, it is already fixed, and this fix fill be included in the next version of the library.
For now, you can build your own formatter in the YAxis:
var chart = new SKCartesianChart
{
Series = new List<ISeries>
{
new StackedColumnSeries<decimal> {Values = new [] {1e8m}},
new StackedColumnSeries<decimal> {Values = new [] {-1e8m}}
},
YAxes = new[]
{
new Axis
{
Labeler = value =>
{
var l = value == 0 ? 0 : (int)Math.Log10(Math.Abs(value));
if (l >= 6)
{
value /= Math.Pow(10, 6);
return value.ToString($"######0.####### M");
}
return value.ToString($"######0.#######");
}
}
}
};

Related

C# data structure to get months

I've got a (month of a) date written as e.g. 'MARS' which would need to return 03 (as it is the 3rd month).
Below are all my options for each month (3 languages):
{ "JAN", };
{ "FEV", "FEB", };
{ "MARS", "MAAR", "MÄR" };
{ "AVR", "APR" };
{ "MAI", "MEI" };
{ "JUIN", "JUN" };
{ "JUIL", "JUL" };
{ "AOUT", "AUG" };
{ "SEPT", "SEP" };
{ "OCT", "OKT" };
{ "NOV", };
{ "DEC", "DEZ" };
Unfortunately the C# DateTime parser doesn't recognize e.g. "MAAR" (it does recognize "MARS") so I guess I'd have to write something myself.
What is the proper way to data structure this? I was thinking of a jagged array or a list within list.
With a jagged array:
string[][] jagged_array = new string[12][];
jagged_array[0] = new string[1];
jagged_array[0][0] = "01";
jagged_array[0][1] = "JAN";
jagged_array[1] = new string[2];
jagged_array[1][0] = "02";
jagged_array[1][1] = "FEV";
jagged_array[1][2] = "FEB";
jagged_array[2] = new string[3];
jagged_array[2][0] = "03";
jagged_array[2][1] = "MARS";
jagged_array[2][2] = "MAAR";
jagged_array[2][3] = "MÄR";
jagged_array[3] = new string[2];
jagged_array[3][0] = "04";
jagged_array[3][1] = "AVR";
jagged_array[3][2] = "APR";
jagged_array[4] = new string[2];
jagged_array[4][0] = "05";
jagged_array[4][1] = "MAI";
jagged_array[4][2] = "MEI";
(...)
Is this the recommended way of structuring the data?
How do I access the month number (well, month string, which can be casted to number)?
Something like get_month("MAAR") --> should return "03". Is there an easy way to get this or do I need to loop over the individual items?
A simple Dictionary can do the job
int GetMonthFromString(string str)
{
var monthNames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
monthNames.Add("JAN", 1);
monthNames.Add("FEB", 2);
monthNames.Add("FEV", 2);
....
if (!monthNames.ContainsKey(str))
throw new ApplicationExcepion("invalid string for month"); //handle somehow an invalid string
return monthNames[str];
}

Display Linestring and Trackpoints with Mapsui

my intention is to display a gps track and the corresponding trackpoints with Mapsui (wpf) on a map. I tried the following code. The result is that the blue linestring is displayed (ok), the red track points (ok) but for any reason you see white track points which are very large and I do not want them to appear at the map and I do not know where the white dots are coming from. Any idea what I am doing wrong?
protected ILayer CreateLineStringLayer(String name, List<GeoWaypoint> geoWaypoints)
{
var lineString = new LineString();
List<Feature> featureList = new List<Feature>();
IStyle pointStyle = new SymbolStyle()
{
SymbolScale = 0.30,
Fill = new Brush(Mapsui.Styles.Color.FromString("Red"))
};
foreach (var wp in geoWaypoints)
{
var point = SphericalMercator.FromLonLat(wp.Longitude, wp.Latitude);
lineString.Vertices.Add(point);
var p2 = SphericalMercator.FromLonLat(wp.Longitude, wp.Latitude);
var pointFeature = new Feature();
pointFeature.Geometry = p2;
pointFeature.Styles.Add(pointStyle);
featureList.Add(pointFeature);
}
IStyle linestringStyle = new VectorStyle()
{
Fill = null,
Outline = null,
Line = { Color = Mapsui.Styles.Color.FromString("Blue"), Width = 4 }
};
Feature lineStringFeature = new Feature()
{
Geometry = lineString
};
lineStringFeature.Styles.Add(linestringStyle);
featureList.Add(lineStringFeature);
MemoryProvider memoryProvider = new MemoryProvider(featureList);
return new MemoryLayer
{
DataSource = memoryProvider,
Name = name
};
}
so for everyone who is interest in the answer
return new MemoryLayer
{
DataSource = memoryProvider,
Name = name ,
Style = null
};
You need to set the value for Style to null for the Memorylayer

Chartsjs (blazor) BarChart not alinged

Im not sure im doing something fairly simple wrong. Im getting below plot when using the below code. I was expecting to get the B values in its own column like you would in excel.
EDIT: I have added my config in the post also, if there are some properties that im missing
/Thomas
BarDataset<double> _barDataSet3 = new BarDataset<double>
{
Label = "A",
BackgroundColor = ColorUtil.RandomColorString(),
BorderWidth = 0,
HoverBackgroundColor = ColorUtil.RandomColorString(),
HoverBorderColor = ColorUtil.RandomColorString(),
HoverBorderWidth = 1,
BorderColor = "#ffffff"
};
_barChartConfig.Data.Labels.AddRange(new[] { "A"});
_barDataSet3.Add(2.6);
_barChartConfig.Data.Datasets.Add(_barDataSet3);
BarDataset<double> _barDataSet4 = new BarDataset<double>
{
Label = "B",
BackgroundColor = ColorUtil.RandomColorString(),
BorderWidth = 0,
HoverBackgroundColor = ColorUtil.RandomColorString(),
HoverBorderColor = ColorUtil.RandomColorString(),
HoverBorderWidth = 1,
BorderColor = "#ffffff"
};
_barChartConfig.Data.Labels.AddRange(new[] { "B" });
_barDataSet4.Add(4.5);
_barChartConfig.Data.Datasets.Add(_barDataSet4);
EDIT: My config - is there a property that im missing?:
_barChartConfig = new BarConfig
{
Options = new BarOptions
{
Title = new OptionsTitle
{
Display = true,
Text = "Simple Bar Chart"
},
Scales = new BarScales
{
XAxes = new List<CartesianAxis>
{
new BarCategoryAxis
{
BarPercentage = 0.5,
BarThickness = BarThickness.Flex
}
},
YAxes = new List<CartesianAxis>
{
new BarLinearCartesianAxis
{
Ticks = new LinearCartesianTicks
{
BeginAtZero = true
}
}
}
}
}
};

Blank chart when using candlesticks

When trying to plot candlestick chart using Oxyplot library, it is empty, despite the fact that I assigned model to the plot view.
var plotModel1 = new PlotModel { Title = "Large Data Set (wide window)" };
var timeSpanAxis1 = new DateTimeAxis { Position = AxisPosition.Bottom };
plotModel1.Axes.Add(timeSpanAxis1);
var linearAxis1 = new LinearAxis { Position = AxisPosition.Left };
plotModel1.Axes.Add(linearAxis1);
var n = 10000;
var items = HighLowItemGenerator.MRProcess(n).ToArray();
var series = new CandleStickSeries
{
Color = OxyColors.Black,
IncreasingColor = OxyColors.DarkGreen,
DecreasingColor = OxyColors.Red,
DataFieldX = "Time",
DataFieldHigh = "H",
DataFieldLow = "L",
DataFieldOpen = "O",
DataFieldClose = "C",
TrackerFormatString =
"High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}",
ItemsSource = items
};
timeSpanAxis1.Minimum = items[n - 200].X;
timeSpanAxis1.Maximum = items[n - 130].X;
linearAxis1.Minimum = items.Skip(n - 200).Take(70).Select(x => x.Low).Min();
linearAxis1.Maximum = items.Skip(n - 200).Take(70).Select(x => x.High).Max();
plotModel1.Series.Add(series);
timeSpanAxis1.AxisChanged += (sender, e) => AdjustYExtent(series, timeSpanAxis1, linearAxis1);
var controller = new PlotController();
controller.UnbindAll();
controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt);
plotView1.Model = plotModel1;
Strange thing is that I've just copied few things from the Oxyplot series example. I've also created minimal project with the problem described.
The objects generated by the HighLowItemGenerator have different names of properties than defined in the CandleStickSeries definition. Check the items objects in the debugger to see it. Maybe the sample is a bit out of date. The solution is to change the series definition to use the correct properties this is how it should look like:
var series = new CandleStickSeries
{
Color = OxyColors.Black,
IncreasingColor = OxyColors.DarkGreen,
DecreasingColor = OxyColors.Red,
DataFieldX = "X",
DataFieldHigh = "High",
DataFieldLow = "Low",
DataFieldOpen = "Open",
DataFieldClose = "Close",
TrackerFormatString =
"High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}",
ItemsSource = items
};

LINQ group by sum of property

I'm trying to create a LINQ query which is a derivative of SelectMany.
I have N items:
new {
{ Text = "Hello", Width = 2 },
{ Text = "Something else", Width = 1 },
{ Text = "Another", Width = 1 },
{ Text = "Extra-wide", Width = 3 },
{ Text = "Random", Width = 1 }
}
I would like the result to be a List<List<object>>(), where:
List<List<object>> = new {
// first "row"
{
{ Text = "Hello", Width = 2 },
{ Text = "Something else", Width = 1 },
{ Text = "Another", Width = 1 }
},
// second "row"
{
{ Text = "Extra-wide", Width = 3 },
{ Text = "Random", Width = 1 }
}
}
So the items are grouped into "rows" where Sum(width) in the internal List is less than or equal to a number (maxWidth - in my instance, 4).
It's kinda a derivative of GroupBy, but the GroupBy is dependent on earlier values in the array - which is where I get stumped.
Any ideas would be appreciated.
We can combine the ideas of LINQ's Aggregate method with a GroupWhile method to group consecutive items while a condition is met to build an aggregate value for the current group to be used in the predicate:
public static IEnumerable<IEnumerable<T>> GroupWhileAggregating<T, TAccume>(
this IEnumerable<T> source,
TAccume seed,
Func<TAccume, T, TAccume> accumulator,
Func<TAccume, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> list = new List<T>() { iterator.Current };
TAccume accume = accumulator(seed, iterator.Current);
while (iterator.MoveNext())
{
accume = accumulator(accume, iterator.Current);
if (predicate(accume, iterator.Current))
{
list.Add(iterator.Current);
}
else
{
yield return list;
list = new List<T>() { iterator.Current };
accume = accumulator(seed, iterator.Current);
}
}
yield return list;
}
}
Using this grouping method we can now write:
var query = data.GroupWhileAggregating(0,
(sum, item) => sum + item.Width,
(sum, item) => sum <= 4);
You can sort of do that with the Batch method from MoreLinq library which is available as a NuGet package. The result is a List<IEnumerable<object>>. Here is the code:
class Obj
{
public string Text {get;set;}
public int Width {get;set;}
}
void Main()
{
var data = new [] {
new Obj { Text = "Hello", Width = 2 },
new Obj { Text = "Something else", Width = 1 },
new Obj { Text = "Another", Width = 1 },
new Obj { Text = "Extra-wide", Width = 3 },
new Obj { Text = "Random", Width = 1 }
};
var maxWidth = data.Max (d => d.Width );
var result = data.Batch(maxWidth).ToList();
result.Dump(); // Dump is a linqpad method
Output
I don't think you can do that with LINQ. One alternative approach would be the following:
var data = ... // original data
var newdata = new List<List<object>>();
int csum = 0;
var crow = new List<object>();
foreach (var o in data) {
if (csum + o.Width > 4) { //check if the current element fits into current row
newdata.Add(crow); //if not add current row to list
csum = 0;
crow = new List<object>(); //and create new row
}
crow.Add(o); //add current object to current row
csum += o.Width;
}
if (crow.Count() > 0) //last row
newData.Add(c);
EDIT: The other answer suggests to use Batch from the MoreLinq Library. In fact, the above source code, is more or less the same, what Batch does, but not only counting the elements in each batch but summing up the desired property. One could possibly generalize my code with a custom selector to be more flexible in terms of "batch size".

Categories

Resources