Confusion Matrices

Oystein Bjorke 10 years ago 0
This discussion was imported from CodePlex

pcp16 wrote at 2013-12-10 15:13:

Dear all!

Hi, I'm new here. This is first thread. I had a look at the other threads before posting: there's none about "confusion matrices".

What I'm trying to do is use the HeatMapSeries in order to have something like:

http://t1.gstatic.com/images?q=tbn:ANd9GcRpKII2C_wm5ZOiazRv1WL2eZsg9gKmtlnxeUiMQPCIThWL6SWR

I now have something which looks good (I have "categories" axes, with tilted labels).

The two issues I'm having are:

1) I'd like to have a grayscale that goes from white=0 to black=1.

2) I'd like to print the values in the center of the cells (like in the example I provided).

I tried googling (should I say binging? ;P) this around, but could not find an answer.

Regards,

Pau.

P.S: I noticed that you can invert the axis (say the vertical one startPosition=1; endPosition=0) so that the categories look "symmetrical" like in my example, ... but is there a way to automatically invert the data accordingly?

objo wrote at 2013-12-11 07:35:

This would be a great example to add to the library!

1) I think you can use OxyPalette.Interpolate(100, OxyColors.White, OxyColors.Black) where the value is the number of colors of the palette.
2) This is new functionality. You can create a subclass of HeatMapSeries and override the Render method, or add the new functionality to the HeatMapSeries (I see this can be useful in other cases where you don't have a lot of cells)

The inverted axes should be handled by HeatMapSeries when creating the image to be rendered. It is possible this is not supported yet. But this should absolutely be fixed. The image renderers do not support flipping/scaling of images, so this must be handled when generating the image.

pcp16 wrote at 2013-12-11 12:46:

Wow, that was fast, thanks!

1) Solved, yes, you can use that and it works.

2) I have created a modified version of HeatMapSeries, that does exactly what I need:
2.a) Renders the numbers in the center of the cells by using rc.DrawText() (and scales them appropriately)
2.b) Renders the matrix in "inverse order" to make it coincide with the "symmetrical" axes.

This is how it looks:

Image
[EDIT: Yes, it shows commas, instead of dots for number separations, due to my locale]

And this is the code:

ConfusionMatrixSeries.cs

And, finally, this is how I generate the model:
public static PlotModel ConfMatrix()
        {
            var data = new double[3, 3];

            data[0, 0] = 1;
            data[1, 1] = 0.8;
            data[1, 2] = 0.2;
            data[2, 2] = 1;
            
            string[] cat1 = { "class A", "class B", "class C" };
            
            var model = new PlotModel("Confusion Matrix");

            var palette = OxyPalette.Interpolate(50, OxyColors.White, OxyColors.Black);
            
            var lca = new LinearColorAxis { Position = AxisPosition.Right, Palette = palette, HighColor = OxyColors.White, LowColor = OxyColors.White };
            model.Axes.Add(lca);
            
            var axis1 = new CategoryAxis(AxisPosition.Top, "Actual class", cat1);
            model.Axes.Add(axis1);
            
            // We invert this axis, so that they look "symmetrical"
            var axis2 = new CategoryAxis(AxisPosition.Left, "Predicted class", cat1);
            axis2.Angle = -90;
            axis2.StartPosition = 1;
            axis2.EndPosition = 0;
            
            model.Axes.Add(axis2);
            
            var hms = new ConfusionMatrixSeries(data);
            
            model.Series.Add(hms);
            return model;
        }
Enjoy!

Pau.

objo wrote at 2013-12-11 21:09:

Very cool! I merged the label rendering code into the HeatMapSeries and added the confusion matrix to the examples!

pcp16 wrote at 2013-12-11 22:53:

I think an option should be included in HeatMapSeries, so that the data matrix can be rendered starting from the "bottom, left" (as in a Cartesian Space style) or from the "top, right" (as in a Matrix style). It could be that this behaviour is required/desirable in other graphs as well, thus, it's possible that it needs to be added to XYAxisSeries or somewhere else.

objo wrote at 2013-12-12 07:44:

I think the flipping of the matrix can be handled by reversing the axes or setting the X0, X1, Y0 and Y1 coordinates.
But there should be an option that determines if the indices of the array are [ix,iy] or [iy,ix] (where ix is the index along the x-axis and iy is the index along the y-axis).

pcp16 wrote at 2013-12-12 10:58:

Well, what I did is change the UpdateImage function in HeatMapSeries.cs like this:
/// <summary>
        /// Updates the image.
        /// </summary>
        private void UpdateImage()
        {
            int m = this.Data.GetLength(0);
            int n = this.Data.GetLength(1);
            var buffer = new OxyColor[m, n];
            for (int i = 0; i < m; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    buffer[i, j] = this.ColorAxis.GetColor(this.Data[j, i]);
                }
            }

            this.image = OxyImage.Create(buffer, ImageFormat.Png);
        }
I'm not sure if this is the correct procedure, or this could be done by changing X0, X1, etc. But I don't think so ... this code doesn't take X0,X1 into account at all, right?

Pau.

objo wrote at 2013-12-12 20:32:

I noticed you are transposing the matrix, this is not handled by the current implementation in the HeatMapSeries. I applied a Transpose method to your data set in the example!
The transpose could be implemented in the HeatMapSeries with a new option, I suggest a bool property Transpose or an enum property IndexOrder defined as { XY, YX }