import { useState } from "react"; import Plot from "react-plotly.js"; interface BenchmarkBarChartProps { title: string; data: ChartData[]; dataKey: keyof Pick< ChartData, "compression_ratio" | "encode_speed" | "decode_speed" | "rmse" | "max_error" >; color: string; xAxisTitle: string; normalize?: boolean; } function BenchmarkBarChart({ title, data, dataKey, color, xAxisTitle, normalize, }: BenchmarkBarChartProps) { const normalizedData = normalize && dataKey === "compression_ratio" ? data.map((d) => ({ ...d, compression_ratio: d.reference_compression_ratio ? d.compression_ratio / d.reference_compression_ratio : d.compression_ratio, reference_compression_ratio: d.reference_compression_ratio ? 1 : null, })) : data; return (

{title}

d.algorithmOrDataset), x: normalizedData.map((d) => { const value = d[dataKey]; return value !== undefined ? value : 0; }), marker: { color }, name: title, hovertemplate: normalize && dataKey === "compression_ratio" ? "%{x:.3f}×" : "%{x:.2f}", }, ...(dataKey === "compression_ratio" && normalizedData.some((d) => d.reference_compression_ratio !== null) ? [ ...normalizedData .filter((d) => d.reference_compression_ratio !== null) .flatMap((d) => [ { type: "scatter" as const, mode: "lines" as const, y: [d.algorithmOrDataset, d.algorithmOrDataset], x: [0, d.reference_compression_ratio], line: { color, width: 1 }, showlegend: false, hoverinfo: "skip" as const, }, { type: "scatter" as const, mode: "markers" as const, y: [d.algorithmOrDataset], x: [d.reference_compression_ratio], marker: { color: "#aaaaaa", size: 8 }, name: "Best Compression", hovertemplate: normalize ? "Best: 1.000×" : "Best: %{x:.2f}", showlegend: d.algorithmOrDataset === normalizedData[0].algorithmOrDataset, }, ]), ] : []), ]} layout={{ width: 700, height: Math.max(300, data.length * 23 + 40), margin: { t: 5, r: 30, l: 200, b: 30 }, xaxis: { title: xAxisTitle }, yaxis: { automargin: true, ticksuffix: " ", tickmode: "array", tickvals: normalizedData.map((d) => d.algorithmOrDataset), ticktext: normalizedData.map((d) => d.tags.includes("lossy") ? `${d.algorithmOrDataset}*` : d.algorithmOrDataset ), }, dragmode: false, }} config={{ displayModeBar: false }} />
); } interface ChartData { algorithmOrDataset: string; compression_ratio: number; reference_compression_ratio: number | null; // the highest compression ratio for the dataset (if algorithmOrDataset is a dataset) encode_speed: number; decode_speed: number; rmse?: number; max_error?: number; tags: string[]; } interface BenchmarkChartsProps { chartData: ChartData[]; showSortByCompressionRatio?: boolean; showNormalizeByReference?: boolean; } export function BenchmarkCharts({ chartData, showSortByCompressionRatio, showNormalizeByReference, }: BenchmarkChartsProps) { const [sortByRatio, setSortByRatio] = useState( showSortByCompressionRatio ? true : false, ); const [normalize, setNormalize] = useState(false); const [showLossyAlgs, setShowLossyAlgs] = useState(true); const [errorMetric, setErrorMetric] = useState<"rmse" | "max_error">("rmse"); if (!chartData.length) return null; // Filter data based on showLossyAlgs // If showLossyAlgs is true, show all algorithms (both lossy and lossless) // If showLossyAlgs is false, only show lossless algorithms const filteredData = showLossyAlgs ? chartData : chartData.filter((d) => !d.tags.includes("lossy")); const sortedData = sortByRatio ? [...filteredData].sort((a, b) => a.compression_ratio - b.compression_ratio) : filteredData; // For Error chart, only show lossy algorithms with error values const lossyData = chartData.filter( (d) => d.tags.includes("lossy") && (errorMetric === "rmse" ? d.rmse !== undefined : d.max_error !== undefined) ); const sortedLossyData = sortByRatio ? [...lossyData].sort((a, b) => a.compression_ratio - b.compression_ratio) : lossyData; return (
{showSortByCompressionRatio && (
)} {showNormalizeByReference && (
)}
{sortedLossyData.length > 0 && (
)}
); }