ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by ET on Oct-13-2014 19:05
Good day Peter,

Need some advise how I can code to produce following chart:

1) 100 datapoints in array
2) from #1), I manage to get all boxTop, boxBottom, max, min, and mid data -
[perform addBoxWhiskerLayer() here]
3) stuck here - [not sure the code to perform addScatterLayer()]


code sample:

string[] labels = data.Keys.ToArray();
                        List<double> boxTopData = new List<double>();
                        List<double> boxBottomData = new List<double>();
                        List<double> maxData = new List<double>();
                        List<double> minData = new List<double>();
                        List<double> midData = new List<double>();
                        List<double> allData = new List<double>();

                        foreach (KeyValuePair<string, List<double>> bygroupid in data)
                        {
                            boxTopData.Add(new
ArrayMath(bytestlotsid.Value.ToArray()).percentile(75));
                            boxBottomData.Add(new
ArrayMath(bytestlotsid.Value.ToArray()).percentile(25));
                            maxData.Add(new ArrayMath(bygroupid .Value.ToArray()).max());
                            minData.Add(new ArrayMath(bygroupid .Value.ToArray()).min());
                            midData.Add(new ArrayMath(bygroupid .Value.ToArray()).med());
                            allData = bygroupid .Value;
                        }

XYChart c = new XYChart(800, 400, 0xffffc0, 0x000000, 1);
c.setRoundedFrame();
c.setPlotArea(55, 58, 720, 195, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
c.addLegend(50, 30, false, "Arial Bold", 9).setBackground(Chart.Transparent);
c.addTitle("Box Plot - Group ID name [" + param + "]");
c.xAxis().setLabels(labels.ToArray()).setFontAngle(90);


c.addScatterLayer(...) - [NOT SURE THE RIGHT CODE TO ADD SCATTER LAYER]


BoxWhiskerLayer layer = c.addBoxWhiskerLayer(boxTopData.ToArray(),
boxBottomData.ToArray(), maxData.ToArray(), minData.ToArray(), midData.ToArray(),
unchecked((int)0xdfff0000), 0xff0000);
layer.setLineWidth(2);
layer.setBorderColor(0xff0000);
layer.moveFront();

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by Peter Kwan on Oct-14-2014 02:46
Attachments:
Hi ET,

I am not sure what you want to plot in the scatter layer. Is it possible to attach an example
to help me understand what you need?

Anyway, in your code, it seems the allData has some issues. Your code is simply copying a
reference to a List<double> to allData, which destroys the reference to the previous
List<double> in allData. So allData does not contain all the data. It only contains the last
data.

For your reference, I have attached a chart created for one of our customers. Basically, for
each group, it is just using random numbers as the x-coordinates, and the data values as
the y-coordinates. As in that customer's case, there are a lot of data points in each group,
so it looks like a visualization of a "distribution". (If there are only a few points in each
group, then it would not look as nice if we use random x-coordinates.)

Regards
Peter Kwan
boxwhisker_scatter.png

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by ET on Oct-14-2014 07:29
Hi Peter,

Sorry for the confusion, I missed another statement:

Dictionary<string, List<double>> data = new Dictionary<string, List<double>>();

This object used when I iterate it in the foreach loop:

foreach (KeyValuePair<string, List<double>> bygroupid in data)
{
...
}

Thanks for sharing the sample image and that is exactly what I need.

Based on your statement that, "Basically, for
each group, it is just using random numbers as the x-coordinates, and the data values as
the y-coordinates."

I am pretty sure I have all the y-coordinates from each allData, BUT, I do not understand
from where I have to find and get the x-coordinates? My datasource only have the y-
coordinates (aka allData) for each group in x-axis.

Is it a mush to generate random numbers as the x-coordinates for each datapoint for
allData?

Thanks.

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by ET on Oct-14-2014 17:13
Hi Peter,

May be to keep my question simple is that based on your statement, "Basically, for
each group, it is just using random numbers as the x-coordinates, and the data values as
the y-coordinates.";

could you advise with some code sample on how I can generate jitter x-coordinates when I
have array of  data values as the y-coordinates?

Thanks. :)

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by Peter Kwan on Oct-14-2014 19:28
Hi ET,

For your case, instead of:

// when you assign something to allData, it replaces the original values in allData
allData = bygroupid.Value;

I think it should be:

// instead of assigning to allData, we should add values to allData
allData.AddRange(bygroupid.Value);

otherwise allData will not contain all the data, but just the last group.

May be you can try the following code:

string[] labels = data.Keys.ToArray();
List<double> boxTopData = new List<double>();
List<double> boxBottomData = new List<double>();
List<double> maxData = new List<double>();
List<double> minData = new List<double>();
List<double> midData = new List<double>();
List<double> allData = new List<double>();
List<double> allX = new List<double>();
RanSeries r = new RanSeries(999);

foreach (KeyValuePair<string, List<double>> bygroupid in data)
{
boxTopData.Add(new ArrayMath(bytestlotsid.Value.ToArray()).percentile(75));
boxBottomData.Add(new ArrayMath(bytestlotsid.Value.ToArray()).percentile(25));
maxData.Add(new ArrayMath(bygroupid .Value.ToArray()).max());
minData.Add(new ArrayMath(bygroupid .Value.ToArray()).min());
midData.Add(new ArrayMath(bygroupid .Value.ToArray()).med());
allData.AddRange(bygroupid .Value);
allX.addRange(r.getSeries(bygroupid.Value.Count, maxData.Count - 1.35,
maxData.Count - 0.65));
}

XYChart c = new XYChart(800, 400, 0xffffc0, 0x000000, 1);
c.setRoundedFrame();
c.setPlotArea(55, 58, 720, 195, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
c.addLegend(50, 30, false, "Arial Bold", 9).setBackground(Chart.Transparent);
c.addTitle("Box Plot - Group ID name [" + param + "]");
c.xAxis().setLinearScale(0, labels.Count - 1, labels.ToArray());
c.xAxis().setLabelFormat("Arial", 8, 0x000000, 90);

c.addScatterLayer(allX.ToArray(), allData.ToArray(), Chart.SquareSymbol, 2, 0x444444,
0x444444);

BoxWhiskerLayer layer = c.addBoxWhiskerLayer(boxTopData.ToArray(),
boxBottomData.ToArray(), maxData.ToArray(), minData.ToArray(), midData.ToArray(),
unchecked((int)0xdfff0000), 0xff0000);
layer.setLineWidth(2);
layer.setBorderColor(0xff0000);


Hope this can help.

Regards
Peter Kwan

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by ET on Oct-15-2014 19:09
Attachments:
Hi Peter,

As usual you solved my problem. Thank you so much!
Also on the same time fixed my logic as well which is very much appreciated.

I modified my codes as your suggestion and it is showing correctly now.
Allow me to ask few questions in order for me to understand more how to deal with
ChartDirector.

1) RanSeries r = new RanSeries(999); - any reason why '999' and not other number?

2) allX.addRange(r.getSeries(bygroupid.Value.Count, maxData.Count - 1.35,
maxData.Count - 0.65)); - again why '1.35' and '0.65' numbers?

3) How can I add additional histogram/distribution on the same chart (tilt 45 degree)
based on all data points from the box plot? Please find attached 'Capture.JPG' file that I try
to visualize it by drawing.

Thanks a lot again!
Capture.JPG

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by Peter Kwan on Oct-16-2014 01:47
Hi ET,

1. As according to the ChartDirector documentation, the argument to the RanSeries constructor is the random number seed. So the "999" is the random number seed. It can be anything you like. See:

http://www.advsofteng.com/doc/cdnet.htm#RanSeries.RanSeries.htm


2. The x-coordinate of the first box is 0, and that of the second box is 1, and so on. So the x-coordinate of the box is (maxData.Couint - 1).

The x-coordinate of the box refers to the center of the box. The scatter dots are randomly distributed to the left and right of the box, but it must be overlap with the next or previous box. So the x-coordinates of the scatter dots must be within +/- 0.5 from the x-coordinate of the box. Allowing for 30% gap between two boxes, the range becomes +/- 0.35. So the x-coordinates is from (maxData.Count - 1 - 0.35) to (maxData.Count - 1 + 0.35), and so we get -1.35 and -0.65.


3.

(a) First, draw an upright normal distribution curve, which can be achieved as a spline curve.  See:

http://www.chartdir.com/forum/download_thread.php?bn=chartdir_general&thread=1153402586#N1153427124

http://www.chartdir.com/forum/download_thread.php?bn=chartdir_support&thread=1257515780

(b) You can then use swapXY() to rotate the curve 90 degrees.

(c) You can configure the axis scale and length so that it marches with that of your box-whisker layer.

(d) You can then put that chart next to your box-whisker chart, so your can use a MultiChart to combine the two charts into one chart.

Hope this can help.

Regards
Peter Kwan

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by ET on Oct-17-2014 11:11
Attachments:
Hi Peter,

Thank you for the in detail explanation.

I did tried and manage to get both chart in MultiChart. Guess, my last hurdle is to get the
axis marches between both chart.

I attached the code and multichart. Hope you could help me further. Thanks!
Capture.JPG
code.txt
Dictionary<string, List<double>> data = new Dictionary<string, List<double>>();

data.Add("ID1", new List<double>() { 0.4, 0.4, 0.6, 0.3, 0.4, 0.3, 0.2, 0.5, 0.4, 0.4, 0.9, 0.4, 0.5, 0.2, 0.6, 0.1, 0.8, 0.3, 0.6, 0.7, 0.6, 0.4, 0.7, 0.7, 0.5, 0.3, 0.3, 0.1, 0.9, 0.5, 0.7, 0.7, 0.1, 0.1, 0.2, 0.6, 0.3, 0.9, 0.5, 0.1, 0.9, 0.2, 0.6, 0.4, 0.9, 0.7, 0.5, 0.9, 0.4, 0.3 });
data.Add("ID2", new List<double>() { 0.5, 0.8, 1.2, 0.7, 0.9, 0.7, 0.7, 0.3, 0.7, 1.2, 0.7, 0.8, 1.0, 1.2, 0.3, 0.3, 0.6, 1.0, 1.0, 0.7, 0.4, 1.0, 0.4, 1.1, 0.4, 0.2, 1.1, 0.5, 1.1, 0.6, 0.9, 0.3, 1.0, 0.7, 0.7, 0.7, 1.1, 1.0, 0.8, 0.6, 1.1, 1.2, 0.6, 0.5, 0.8, 1.0, 1.2, 1.1, 0.3, 1.0 });
data.Add("ID3", new List<double>() { 0.8, 0.1, 0.4, 1.4, 1.2, 1.3, 0.6, 1.3, 0.4, 0.6, 1.2, 0.5, 0.8, 0.1, 1.5, 1.1, 1.5, 1.3, 0.2, 0.9, 0.2, 0.4, 0.8, 1.2, 0.4, 0.9, 1.2, 1.0, 0.1, 0.9, 0.5, 0.8, 1.1, 0.2, 0.4, 0.8, 0.6, 0.2, 1.3, 1.2, 1.1, 1.0, 1.2, 1.0, 0.8, 0.5, 0.3, 1.4, 1.3, 0.8 });
data.Add("ID4", new List<double>() { 0.8, 0.6, 0.6, 0.6, 0.5, 0.5, 0.8, 0.9, 0.7, 0.7, 0.6, 0.7, 0.7, 0.5, 0.9, 0.5, 0.5, 0.6, 0.9, 0.7, 0.8, 0.5, 0.5, 0.6, 0.9, 0.7, 0.8, 0.6, 0.8, 0.6, 0.7, 0.7, 0.8, 0.9, 0.8, 0.8, 0.8, 0.9, 0.5, 0.5, 0.7, 0.5, 0.9, 0.5, 0.5, 0.8, 0.5, 0.9, 0.8, 0.7 });
data.Add("ID5", new List<double>() { 1.6, 1.9, 1.8, 1.7, 1.7, 1.6, 1.7, 1.7, 1.8, 1.5, 1.8, 1.9, 1.6, 1.7, 1.5, 1.5, 1.9, 1.8, 1.8, 1.9, 1.8, 1.5, 1.5, 1.7, 1.9, 1.7, 1.6, 1.7, 1.8, 1.8, 1.6, 1.5, 1.8, 1.5, 1.6, 1.7, 1.5, 1.7, 1.7, 1.5, 1.9, 1.5, 1.9, 1.9, 1.8, 1.6, 1.9, 1.7, 1.9, 1.7 });
data.Add("ID6", new List<double>() { 1.4, 1.3, 0.6, 1.3, 1.0, 0.5, 1.5, 1.0, 0.5, 0.8, 1.8, 0.6, 1.7, 1.0, 0.6, 1.9, 1.6, 0.9, 0.9, 1.7, 1.6, 1.3, 1.4, 0.6, 0.5, 0.8, 1.1, 1.4, 1.9, 1.8, 0.9, 0.6, 1.8, 1.9, 0.5, 1.4, 1.1, 1.8, 1.3, 1.1, 1.1, 1.0, 1.7, 1.8, 1.8, 0.7, 1.0, 0.6, 0.5, 1.4 });

string[] labels = data.Keys.ToArray();
List<double> boxTopData = new List<double>();
List<double> boxBottomData = new List<double>();
List<double> maxData = new List<double>();
List<double> minData = new List<double>();
List<double> midData = new List<double>();
List<double> allData = new List<double>();
List<double> allX = new List<double>();

RanSeries r = new RanSeries(999);

foreach (KeyValuePair<string, List<double>> bygroupid in data)
{
	boxTopData.Add(new ArrayMath(bygroupid.Value.ToArray()).percentile(75));
	boxBottomData.Add(new ArrayMath(bygroupid.Value.ToArray()).percentile(25));
	maxData.Add(new ArrayMath(bygroupid.Value.ToArray()).max());
	minData.Add(new ArrayMath(bygroupid.Value.ToArray()).min());
	midData.Add(new ArrayMath(bygroupid.Value.ToArray()).med());
	allData.AddRange(bygroupid.Value);
	allX.AddRange(r.getSeries(bygroupid.Value.Count, maxData.Count - 1.35, maxData.Count - 0.65));
}

XYChart c = new XYChart(800, 400, Chart.Transparent, Chart.Transparent);
  c.setPlotArea(55, 58, 720, 195, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);

ChartDirector.TextBox title = c.addTitle("Analysis");

c.xAxis().setLabels(labels.ToArray()).setFontAngle(90);
c.addScatterLayer(allX.ToArray(), allData.ToArray(), "", Chart.SquareSymbol, 3, 0x444444, 0x444444);

BoxWhiskerLayer layer = c.addBoxWhiskerLayer(boxTopData.ToArray(), boxBottomData.ToArray(), maxData.ToArray(), minData.ToArray(), midData.ToArray(), unchecked((int)0xdfff0000), 0xff0000);
layer.setLineWidth(1);
layer.setBorderColor(0xff0000);
layer.moveFront();

c.packPlotArea(10, title.getHeight(), c.getWidth() - 10, c.getHeight() - 10);

Dictionary<double, int> res = allData.GroupBy(x => x).Select(x => new { Value = x.Key, Count = x.Count() }).OrderBy(x=>x.Value).ToDictionary(x => x.Value, x => x.Count);

XYChart sc = new XYChart(300, 400, Chart.Transparent, Chart.Transparent);
sc.setPlotArea(55, 58, 250, 195, Chart.Transparent, Chart.Transparent, Chart.Transparent, Chart.Transparent, Chart.Transparent);
BarLayer bl = sc.addBarLayer(res.Select(x => Convert.ToDouble(x.Value)).ToArray(), 0x809980);

bl.setBarGap(Chart.TouchBar);
sc.xAxis().setLabels(res.Select(x => Convert.ToDouble(x.Key)).ToArray());
sc.yAxis().setColors(Chart.Transparent, Chart.Transparent);
sc.swapXY();

//NOT SURE HOW TO CONFIGURE THE AXIS SCALE AND LENGTH SO THAT IT MARCHES WITH BOX-WHISKER LAYER. 
//I NEED THE BARLAYER XAXIS(AFTER SWAP) TO FOLLOW BOXWHISKER YAXIS SCALE, MIN VALUE AND MAX VALUE

MultiChart mc = new MultiChart(1600, 400, 0xffffc0, 0x000000, 1);
mc.addChart(0, 0, c);
mc.addChart(801, 0, sc);
mc.makeChart(@"C:\\Users\\test\\Desktop\\t\\multi.png");

  Re: XYChart with BoxWhiskerLayer and ScatterLayer.
Posted by Peter Kwan on Oct-18-2014 02:24
Hi ET,

To make the two charts match, first, you need to make the plot area size and position match. The top-y coordinates and the height of both plot areas should be the same.

Then you need to make the x-axis scale match of the bar chart matches with the y-axis of the box-whisker chart. You can use Axis.syncAxis to do this.

The x-values for the bar chart are not labels. They are the x-coordinates to specify the position of the bars, so they should be passed to Layer.setXData. They are not the x-axis labels. The x-axis of the bar chart is determined by the y-axis of the box-whisker layer, and so it is not dependent to the data on the bar chart.

Below please find the modified code to synchronous the axis of the two charts.

Note that the method you determine the x-coordinates of the bars may have some issues. For your current case, the data values are from 0.1, 0.2, 0.3, ... 1.9. But in real data, they can be less regularly. For example, you may only have values at x = 0.2, 0.6, 1.0, 1.6, 1.9124. But then how wide are the bars? Should they be 0.1 x-unit wide or 0.2 unit or something else? It is not possible to determine from the data.

In a real histogram, the x-coordinates are usually independent variables (which means they do not come from the data). For example, you may set the x-coordinates of the bars to 0.05, 0.15, 0.25, 0.35, ...., with each bar 0.1 unit apart. So the first bar at 0.05 would represent the range 0 to 0.1, and the second bar represent 0.1 to 0.2, and so on. Your code then count the number of points within each range and use them as the y-coordinates. Note that a x-range exists even if there is no data in that range, in which case the count is 0. Using this method, you can create bars of regular width.

Regards
Peter Kwan

========================================================

Dictionary<string, List<double>> data = new Dictionary<string, List<double>>();

data.Add("ID1", new List<double>() { 0.4, 0.4, 0.6, 0.3, 0.4, 0.3, 0.2, 0.5, 0.4, 0.4, 0.9, 0.4, 0.5, 0.2, 0.6, 0.1, 0.8, 0.3, 0.6, 0.7, 0.6, 0.4, 0.7, 0.7, 0.5, 0.3, 0.3, 0.1, 0.9, 0.5, 0.7, 0.7, 0.1, 0.1, 0.2, 0.6, 0.3, 0.9, 0.5, 0.1, 0.9, 0.2, 0.6, 0.4, 0.9, 0.7, 0.5, 0.9, 0.4, 0.3 });
data.Add("ID2", new List<double>() { 0.5, 0.8, 1.2, 0.7, 0.9, 0.7, 0.7, 0.3, 0.7, 1.2, 0.7, 0.8, 1.0, 1.2, 0.3, 0.3, 0.6, 1.0, 1.0, 0.7, 0.4, 1.0, 0.4, 1.1, 0.4, 0.2, 1.1, 0.5, 1.1, 0.6, 0.9, 0.3, 1.0, 0.7, 0.7, 0.7, 1.1, 1.0, 0.8, 0.6, 1.1, 1.2, 0.6, 0.5, 0.8, 1.0, 1.2, 1.1, 0.3, 1.0 });
data.Add("ID3", new List<double>() { 0.8, 0.1, 0.4, 1.4, 1.2, 1.3, 0.6, 1.3, 0.4, 0.6, 1.2, 0.5, 0.8, 0.1, 1.5, 1.1, 1.5, 1.3, 0.2, 0.9, 0.2, 0.4, 0.8, 1.2, 0.4, 0.9, 1.2, 1.0, 0.1, 0.9, 0.5, 0.8, 1.1, 0.2, 0.4, 0.8, 0.6, 0.2, 1.3, 1.2, 1.1, 1.0, 1.2, 1.0, 0.8, 0.5, 0.3, 1.4, 1.3, 0.8 });
data.Add("ID4", new List<double>() { 0.8, 0.6, 0.6, 0.6, 0.5, 0.5, 0.8, 0.9, 0.7, 0.7, 0.6, 0.7, 0.7, 0.5, 0.9, 0.5, 0.5, 0.6, 0.9, 0.7, 0.8, 0.5, 0.5, 0.6, 0.9, 0.7, 0.8, 0.6, 0.8, 0.6, 0.7, 0.7, 0.8, 0.9, 0.8, 0.8, 0.8, 0.9, 0.5, 0.5, 0.7, 0.5, 0.9, 0.5, 0.5, 0.8, 0.5, 0.9, 0.8, 0.7 });
data.Add("ID5", new List<double>() { 1.6, 1.9, 1.8, 1.7, 1.7, 1.6, 1.7, 1.7, 1.8, 1.5, 1.8, 1.9, 1.6, 1.7, 1.5, 1.5, 1.9, 1.8, 1.8, 1.9, 1.8, 1.5, 1.5, 1.7, 1.9, 1.7, 1.6, 1.7, 1.8, 1.8, 1.6, 1.5, 1.8, 1.5, 1.6, 1.7, 1.5, 1.7, 1.7, 1.5, 1.9, 1.5, 1.9, 1.9, 1.8, 1.6, 1.9, 1.7, 1.9, 1.7 });
data.Add("ID6", new List<double>() { 1.4, 1.3, 0.6, 1.3, 1.0, 0.5, 1.5, 1.0, 0.5, 0.8, 1.8, 0.6, 1.7, 1.0, 0.6, 1.9, 1.6, 0.9, 0.9, 1.7, 1.6, 1.3, 1.4, 0.6, 0.5, 0.8, 1.1, 1.4, 1.9, 1.8, 0.9, 0.6, 1.8, 1.9, 0.5, 1.4, 1.1, 1.8, 1.3, 1.1, 1.1, 1.0, 1.7, 1.8, 1.8, 0.7, 1.0, 0.6, 0.5, 1.4 });

string[] labels = data.Keys.ToArray();
List<double> boxTopData = new List<double>();
List<double> boxBottomData = new List<double>();
List<double> maxData = new List<double>();
List<double> minData = new List<double>();
List<double> midData = new List<double>();
List<double> allData = new List<double>();
List<double> allX = new List<double>();

RanSeries r = new RanSeries(999);

foreach (KeyValuePair<string, List<double>> bygroupid in data)
{
boxTopData.Add(new ArrayMath(bygroupid.Value.ToArray()).percentile(75));
boxBottomData.Add(new ArrayMath(bygroupid.Value.ToArray()).percentile(25));
maxData.Add(new ArrayMath(bygroupid.Value.ToArray()).max());
minData.Add(new ArrayMath(bygroupid.Value.ToArray()).min());
midData.Add(new ArrayMath(bygroupid.Value.ToArray()).med());
allData.AddRange(bygroupid.Value);
allX.AddRange(r.getSeries(bygroupid.Value.Count, maxData.Count - 1.35, maxData.Count - 0.65));
}

XYChart c = new XYChart(800, 400, Chart.Transparent, Chart.Transparent);
  c.setPlotArea(55, 58, 720, 195, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);

ChartDirector.TextBox title = c.addTitle("Analysis");

c.xAxis().setLabels(labels.ToArray()).setFontAngle(90);
c.addScatterLayer(allX.ToArray(), allData.ToArray(), "", Chart.SquareSymbol, 3, 0x444444, 0x444444);

BoxWhiskerLayer layer = c.addBoxWhiskerLayer(boxTopData.ToArray(), boxBottomData.ToArray(), maxData.ToArray(), minData.ToArray(), midData.ToArray(), unchecked((int)0xdfff0000), 0xff0000);
layer.setLineWidth(1);
layer.setBorderColor(0xff0000);
layer.moveFront();

c.packPlotArea(10, title.getHeight(), c.getWidth() - 10, c.getHeight() - 10);

Dictionary<double, int> res = allData.GroupBy(x => x).Select(x => new { Value = x.Key, Count = x.Count() }).OrderBy(x=>x.Value).ToDictionary(x => x.Value, x => x.Count);

XYChart sc = new XYChart(300, 400, Chart.Transparent, Chart.Transparent);
sc.setPlotArea(55, c.getPlotArea().getTopY(), 250, c.getPlotArea().getHeight(), Chart.Transparent, Chart.Transparent, Chart.Transparent, Chart.Transparent, Chart.Transparent);
BarLayer bl = sc.addBarLayer(res.Select(x => Convert.ToDouble(x.Value)).ToArray(), 0x809980);

bl.setBarGap(Chart.TouchBar);
bl.setXData(res.Select(x => Convert.ToDouble(x.Key)).ToArray());
sc.yAxis().setColors(Chart.Transparent, Chart.Transparent);
sc.swapXY();

//NOT SURE HOW TO CONFIGURE THE AXIS SCALE AND LENGTH SO THAT IT MARCHES WITH BOX-WHISKER LAYER.
//I NEED THE BARLAYER XAXIS(AFTER SWAP) TO FOLLOW BOXWHISKER YAXIS SCALE, MIN VALUE AND MAX VALUE
sc.xAxis().syncAxis(c.yAxis());
sc.xAxis().setIndent(false);


MultiChart mc = new MultiChart(1600, 400, 0xffffc0, 0x000000, 1);
mc.addChart(0, 0, c);
mc.addChart(801, 0, sc);
mc.makeChart(@"C:\\Users\\test\\Desktop\\t\\multi.png");