import { useEffect, useState } from 'react' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, } from 'recharts' import API from '../API' interface DataChartProps { dataset: string } interface Row { metric: string [key: string]: string | number } const MetricSelector = ({ metrics, selectedMetric, onMetricChange, }: { metrics: Set selectedMetric: string | null onMetricChange: (event: React.ChangeEvent) => void }) => { return (
Metric
) } const AttackSelector = ({ attacks, selectedAttack, onAttackChange, }: { attacks: Set selectedAttack: string | null onAttackChange: (event: React.ChangeEvent) => void }) => { return (
Attack
) } const DataChart = ({ dataset }: DataChartProps) => { const [chartData, setChartData] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [metrics, setMetrics] = useState>(new Set()) const [attacks, setAttacks] = useState>(new Set()) const [selectedMetric, setSelectedMetric] = useState(null) const [selectedAttack, setSelectedAttack] = useState(null) useEffect(() => { API.fetchStaticFile(`data/${dataset}_attacks_variations`) .then((response) => { const data = JSON.parse(response) const rows: Row[] = data['all_attacks_df'].map((row: any) => { const newRow: Row = { ...row } // Convert strength value to number if it exists and is a string if (typeof newRow.strength === 'string') { newRow.strength = parseFloat(newRow.strength) } return newRow }) setSelectedMetric(data['metrics'][0]) setMetrics(new Set(data['metrics'])) setSelectedAttack(data['attacks_with_variations'][0]) setAttacks(new Set(data['attacks_with_variations'])) setChartData(rows) setLoading(false) }) .catch((err) => { setError('Failed to fetch JSON: ' + err.message) setLoading(false) }) }, []) const handleMetricChange = (event: React.ChangeEvent) => { setSelectedMetric(event.target.value) } const handleAttackChange = (event: React.ChangeEvent) => { setSelectedAttack(event.target.value) } // Sort the chart data by the 'strength' field before rendering const sortedChartData = chartData .filter((row) => !selectedAttack || row.attack === selectedAttack) .sort((a, b) => (a.strength as number) - (b.strength as number)) return (

Data Visualization

{loading &&
Loading...
} {error &&
{error}
} {!loading && !error && ( <>
{chartData.length > 0 && (
Number(item.strength))), Math.max(...sortedChartData.map((item) => Number(item.strength))), ]} type="number" tickFormatter={(value) => value.toFixed(3)} label={{ value: 'Strength', position: 'insideBottomRight', offset: -5 }} /> value.toFixed(3)} /> value.toFixed(3)} /> {(() => { // Ensure selectedMetric is not null before rendering the Line components if (!selectedMetric) return null // Do not render lines if no metric is selected // Get unique models from the filtered and sorted data const models = new Set(sortedChartData.map((row) => row.model)) // Generate different colors for each model const colors = [ '#8884d8', '#82ca9d', '#ffc658', '#ff8042', '#0088fe', '#00C49F', ] // Return a Line component for each model return [...models].map((model, index) => { return ( row.model === model)} name={model as string} stroke={colors[index % colors.length]} dot={false} /> ) }) })()}
)} )}
) } export default DataChart