Search code examples
c#data-bindingboxplotoxyplot

C# - Creating a Boxplot in Oxyplot


I try to get used to Oxyplot to create some charts in my C# application. So I wanted to create a Boxplot diagram with OxyPlot, but I am not able to get any results. I tried the "Getting Started" example with no problems but now I have no clue how to bind data to a BoxplotSeries. I read the documentation on GitHub for BoxPlotSeries but there are not much information listed and the examples help me neither. So can somebody can give me an example how to use BoxPlotSeries and bind data to it?

Thank you so much.


Solution

  • So I found a way to create a Boxplot with OxyPlot. Just use the Class BoxPlotSeries of OxyPlot, a List object which holds the Boxplots and a little bit of math. I used CategoryAxes for x axis to distinguish the Boxplots.

    So for the future, here is the Code if somebody struggles with the same problem.

     public class Item
    {
        #region Public Properties
    
        public string Label { get; set; }
    
        #endregion Public Properties
    }
    
    public class BoxPlotSeriesExample
    {
        #region Public Constructors
    
        public BoxPlotSeriesExample()
        {
        }
    
        #endregion Public Constructors
    
        #region Public Methods
    
        public PlotModel createBoxPlot()
        {
            const int boxes = 16;
            var plot = new PlotModel();
            var items = new Collection<Item>();
    
            for (int i = 1; i < boxes + 1; i++)
            {
                items.Add(new Item { Label = i.ToString() });
            }
    
            plot.Axes.Add(new LinearAxis
            {
                Position = AxisPosition.Left,
                MajorStep = 1,
                MinorStep = 0.25,
                TickStyle = TickStyle.Crossing,
                AbsoluteMaximum = 5.25,
                AbsoluteMinimum = -0.25
            });
    
            plot.Axes.Add(new CategoryAxis
            {
                Position = AxisPosition.Bottom,
                ItemsSource = items,
                LabelField = "Label",
                IsTickCentered = true,
                TickStyle = TickStyle.None,
                AbsoluteMinimum = -1,
                AbsoluteMaximum = 17,
                IsZoomEnabled = false
            });
    
            var lineAnnotation = new LineAnnotation
            {
                Type = LineAnnotationType.Horizontal,
                Y = 5,
                LineStyle = LineStyle.Dash,
                StrokeThickness = 2,
                Color = OxyColor.FromArgb(50, 0, 0, 0)
            };
            plot.Annotations.Add(lineAnnotation);
    
            lineAnnotation = new LineAnnotation
            {
                Type = LineAnnotationType.Horizontal,
                Y = 1,
                LineStyle = LineStyle.Dash,
                StrokeThickness = 1.5,
                Color = OxyColor.FromArgb(50, 0, 0, 0)
            };
            plot.Annotations.Add(lineAnnotation);
    
            lineAnnotation = new LineAnnotation
            {
                Type = LineAnnotationType.Horizontal,
                Y = 4,
                LineStyle = LineStyle.Solid,
                StrokeThickness = 1.5,
                Color = OxyColor.FromArgb(50, 0, 0, 0)
            };
            plot.Annotations.Add(lineAnnotation);
    
            lineAnnotation = new LineAnnotation
            {
                Type = LineAnnotationType.Horizontal,
                Y = 2,
                LineStyle = LineStyle.Solid,
                StrokeThickness = 1.5,
                Color = OxyColor.FromArgb(50, 0, 0, 0)
            };
            plot.Annotations.Add(lineAnnotation);
    
            var s1 = new BoxPlotSeries();
            s1.Fill = OxyColor.FromRgb(0x1e, 0xb4, 0xda);
            s1.StrokeThickness = 1.1;
            s1.WhiskerWidth = 1;
            var random = new Random();
            for (int i = 0; i < boxes; i++)
            {
                double x = i;
                var points = 5 + random.Next(15);
                var values = new List<double>();
                for (int j = 0; j < points; j++)
                {
                    values.Add((random.NextDouble()) * 5);
                }
    
                values.Sort();
                var median = getMedian(values);
                int r = values.Count % 2;
                double firstQuartil = getMedian(values.Take((values.Count + r) / 2)); // 25%-Quartil
                double thirdQuartil = getMedian(values.Skip((values.Count - r) / 2)); // 75%-Quartil
    
                var iqr = thirdQuartil - firstQuartil; // Quartilabstand
                var step = 1.5 * iqr;
                var upperWhisker = thirdQuartil + step;
                upperWhisker = values.Where(v => v <= upperWhisker).Max();
                var lowerWhisker = firstQuartil - step;
                lowerWhisker = values.Where(v => v >= lowerWhisker).Min();
                var outliers = values.Where(v => v > upperWhisker || v < lowerWhisker).ToList();
    
                s1.Items.Add(new BoxPlotItem(x, lowerWhisker, firstQuartil, median, thirdQuartil, upperWhisker, outliers));
            }
    
            plot.Series.Add(s1);
            return plot;
        }
    
        #endregion Public Methods
    
        #region Private Methods
    
        private static double getMedian(IEnumerable<double> values)
        {
            var sortedInterval = new List<double>(values);
            sortedInterval.Sort();
            var count = sortedInterval.Count;
            if (count % 2 == 1)
            {
                return sortedInterval[(count - 1) / 2];
            }
    
            return 0.5 * sortedInterval[count / 2] + 0.5 * sortedInterval[(count / 2) - 1];
        }
    
        #endregion Private Methods
    }