Vector Motion
AI/ML

Confusion Matrix Card - ML Model Performance

Visualize ML model classification performance with confusion matrices. Analyze true positives, false positives, and model accuracy metrics interactively.

Confusion Matrix Card

The Confusion Matrix Card displays a confusion matrix visualization showing true positives, false positives, true negatives, and false negatives for classification model evaluation.

Preview

Installation

npx shadcn@latest add https://vectormotion.vercel.app/registry/confusion-matrix-card.json
Confusion Matrix Card
'use client'import React from 'react';import { Grid } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"import { ResponsiveContainer, ScatterChart, Scatter, XAxis, YAxis, Tooltip, Cell, ZAxis } from 'recharts';function cn(...inputs: ClassValue[]) {   return twMerge(clsx(inputs))}interface MatrixData {   x: number;   y: number;   value: number;   label: string;   fill: string;   [key: string]: any;}interface ConfusionMatrixCardProps {   className?: string;   title?: string;   subtitle?: string;   data?: MatrixData[];}const DEFAULT_DATA: MatrixData[] = [   { x: 1, y: 1, value: 850, label: 'TP', fill: '#10b981' }, // True Positive (Emerald)   { x: 2, y: 1, value: 42, label: 'FP', fill: '#f43f5e' },  // False Positive (Rose)   { x: 1, y: 2, value: 15, label: 'FN', fill: '#f43f5e' },  // False Negative (Rose)   { x: 2, y: 2, value: 910, label: 'TN', fill: '#10b981' }, // True Negative (Emerald)];const DEFAULT_TITLE = "Confusion Matrix";const DEFAULT_SUBTITLE = "Predicted vs Actual";export const ConfusionMatrixCard: React.FC<ConfusionMatrixCardProps> = ({   className = "",   title = DEFAULT_TITLE,   subtitle = DEFAULT_SUBTITLE,   data = DEFAULT_DATA,}) => {   return (      <motion.div         initial={{ opacity: 0, y: 20 }}         animate={{ opacity: 1, y: 0 }}         transition={{ duration: 0.5, delay: 0.1 }}         className={cn(            "relative overflow-hidden rounded-2xl border border-border bg-card text-card-foreground shadow-sm transition-all hover:border-blue-300 dark:hover:border-blue-700 hover:shadow-md flex flex-col h-full",            className         )}      >         <div className="p-5 flex flex-col h-full relative z-10">            <div className="mb-4 flex items-start justify-between">               <div>                  <h3 className="font-bold text-lg text-foreground">                     {title}                  </h3>                  <p className="text-sm text-muted-foreground font-medium">                     {subtitle}                  </p>               </div>               <div className="rounded-lg border-2 border-blue-100 dark:border-blue-800 p-2 text-blue-500 dark:text-blue-400 flex items-center justify-center">                  <Grid className="h-5 w-5" />               </div>            </div>            <div className="flex-1 w-full min-h-[160px] relative">               <ResponsiveContainer width="100%" height="100%">                  <ScatterChart margin={{ top: 20, right: 20, bottom: 20, left: 20 }}>                     <XAxis type="number" dataKey="x" name="Predicted" hide domain={[0, 3]} />                     <YAxis type="number" dataKey="y" name="Actual" hide domain={[0, 3]} reversed />                     <ZAxis type="number" dataKey="value" range={[4000, 4000]} />                     <Tooltip                        cursor={{ strokeDasharray: '3 3' }}                        content={({ active, payload }) => {                           if (active && payload && payload.length) {                              return (                                 <div className="rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800 p-2 shadow-sm text-xs">                                    <span className="font-bold">{payload[0].payload.label}:</span> {payload[0].payload.value}                                 </div>                              );                           }                           return null;                        }}                     />                     <Scatter data={data} shape={(props: any) => {                        const { cx, cy, payload } = props;                        return (                           <g>                              <rect                                 x={cx - 60}                                 y={cy - 40}                                 width={120}                                 height={80}                                 rx={8}                                 fill={payload.fill}                                 opacity={0.2}                              />                              <text x={cx} y={cy - 10} textAnchor="middle" dominantBaseline="middle" className="text-xs font-bold fill-zinc-700 dark:fill-zinc-300">                                 {payload.label}                              </text>                              <text x={cx} y={cy + 15} textAnchor="middle" dominantBaseline="middle" className="text-lg font-bold fill-zinc-900 dark:fill-zinc-50">                                 {payload.value}                              </text>                           </g>                        );                     }}>                     </Scatter>                  </ScatterChart>               </ResponsiveContainer>            </div>            <div className="absolute -bottom-4 -right-4 z-0 opacity-5 pointer-events-none">               <Grid className="w-40 h-40 text-blue-500" />            </div>         </div>      </motion.div>   );};

Props

Prop

Type

Usage

This component is a demo card displaying confusion matrix data with animated visualizations and dark mode support.