Spaces:
Configuration error
Configuration error
| # components/visualization.py | |
| class D3Visualizer: | |
| """D3.js visualization component""" | |
| def create_interactive_plot(plot_type: str, data: dict) -> str: | |
| """Create interactive D3 visualization""" | |
| # Base CSS for visualizations | |
| base_css = """ | |
| <style> | |
| .visualization-container { width: 100%; height: 500px; } | |
| .bar { fill: steelblue; } | |
| .bar:hover { fill: brown; } | |
| .line { fill: none; stroke: steelblue; stroke-width: 2; } | |
| .area { fill: steelblue; opacity: 0.2; } | |
| .tooltip { position: absolute; padding: 8px; background: white; border: 1px solid #ddd; border-radius: 4px; } | |
| .axis-label { font-size: 12px; } | |
| </style> | |
| """ | |
| if plot_type == "distribution": | |
| return base_css + f""" | |
| <div id="distribution-plot" class="visualization-container"></div> | |
| <script src="https://d3js.org/d3.v7.min.js"></script> | |
| <script> | |
| (function() {{ | |
| const values = {data['values']}; | |
| const margin = {{top: 40, right: 40, bottom: 60, left: 60}}; | |
| const width = 800 - margin.left - margin.right; | |
| const height = 400 - margin.top - margin.bottom; | |
| // Create SVG | |
| const svg = d3.select("#distribution-plot") | |
| .append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", `translate(${{margin.left}},${{margin.top}})`); | |
| // Create scales | |
| const x = d3.scaleLinear() | |
| .domain([d3.min(values), d3.max(values)]) | |
| .range([0, width]); | |
| const histogram = d3.histogram() | |
| .domain(x.domain()) | |
| .thresholds(x.ticks(20)); | |
| const bins = histogram(values); | |
| const y = d3.scaleLinear() | |
| .domain([0, d3.max(bins, d => d.length)]) | |
| .range([height, 0]); | |
| // Create bars | |
| svg.selectAll(".bar") | |
| .data(bins) | |
| .enter() | |
| .append("rect") | |
| .attr("class", "bar") | |
| .attr("x", d => x(d.x0)) | |
| .attr("y", d => y(d.length)) | |
| .attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1)) | |
| .attr("height", d => height - y(d.length)) | |
| .on("mouseover", function(event, d) {{ | |
| tooltip.transition() | |
| .duration(200) | |
| .style("opacity", .9); | |
| tooltip.html( | |
| `Range: ${{d.x0.toFixed(2)}} - ${{d.x1.toFixed(2)}}<br/>` + | |
| `Count: ${{d.length}}` | |
| ) | |
| .style("left", (event.pageX + 5) + "px") | |
| .style("top", (event.pageY - 28) + "px"); | |
| }}) | |
| .on("mouseout", function(d) {{ | |
| tooltip.transition() | |
| .duration(500) | |
| .style("opacity", 0); | |
| }}); | |
| // Add axes | |
| svg.append("g") | |
| .attr("class", "x-axis") | |
| .attr("transform", `translate(0,${{height}})`) | |
| .call(d3.axisBottom(x)) | |
| .append("text") | |
| .attr("class", "axis-label") | |
| .attr("x", width/2) | |
| .attr("y", 40) | |
| .text("Value"); | |
| svg.append("g") | |
| .attr("class", "y-axis") | |
| .call(d3.axisLeft(y)) | |
| .append("text") | |
| .attr("class", "axis-label") | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", -40) | |
| .attr("x", -height/2) | |
| .style("text-anchor", "middle") | |
| .text("Frequency"); | |
| // Add tooltip | |
| const tooltip = d3.select("#distribution-plot") | |
| .append("div") | |
| .attr("class", "tooltip") | |
| .style("opacity", 0); | |
| }})(); | |
| </script> | |
| """ | |
| elif plot_type == "forecast": | |
| return base_css + f""" | |
| <div id="forecast-plot" class="visualization-container"></div> | |
| <script src="https://d3js.org/d3.v7.min.js"></script> | |
| <script> | |
| (function() {{ | |
| const data = {data}; | |
| const margin = {{top: 40, right: 40, bottom: 60, left: 60}}; | |
| const width = 800 - margin.left - margin.right; | |
| const height = 400 - margin.top - margin.bottom; | |
| // Create SVG | |
| const svg = d3.select("#forecast-plot") | |
| .append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", `translate(${{margin.left}},${{margin.top}})`); | |
| // Create scales | |
| const x = d3.scaleLinear() | |
| .domain([0, data.time.length-1]) | |
| .range([0, width]); | |
| const y = d3.scaleLinear() | |
| .domain([ | |
| d3.min(data.lower), | |
| d3.max(data.upper) | |
| ]) | |
| .range([height, 0]); | |
| // Create area | |
| const area = d3.area() | |
| .x((d, i) => x(i)) | |
| .y0(d => y(d[0])) | |
| .y1(d => y(d[1])); | |
| // Add confidence interval area | |
| svg.append("path") | |
| .datum(data.time.map((t, i) => [data.lower[i], data.upper[i]])) | |
| .attr("class", "area") | |
| .attr("d", area); | |
| // Add mean line | |
| const line = d3.line() | |
| .x((d, i) => x(i)) | |
| .y(d => y(d)); | |
| svg.append("path") | |
| .datum(data.mean) | |
| .attr("class", "line") | |
| .attr("d", line); | |
| // Add axes | |
| svg.append("g") | |
| .attr("class", "x-axis") | |
| .attr("transform", `translate(0,${{height}})`) | |
| .call(d3.axisBottom(x)) | |
| .append("text") | |
| .attr("class", "axis-label") | |
| .attr("x", width/2) | |
| .attr("y", 40) | |
| .text("Time Period"); | |
| svg.append("g") | |
| .attr("class", "y-axis") | |
| .call(d3.axisLeft(y)) | |
| .append("text") | |
| .attr("class", "axis-label") | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", -40) | |
| .attr("x", -height/2) | |
| .style("text-anchor", "middle") | |
| .text("Value"); | |
| // Add tooltip for hover | |
| const tooltip = d3.select("#forecast-plot") | |
| .append("div") | |
| .attr("class", "tooltip") | |
| .style("opacity", 0); | |
| // Add hover interaction | |
| const bisect = d3.bisector(d => d).left; | |
| svg.append("rect") | |
| .attr("class", "overlay") | |
| .attr("width", width) | |
| .attr("height", height) | |
| .style("fill", "none") | |
| .style("pointer-events", "all") | |
| .on("mousemove", function(event) {{ | |
| const x0 = x.invert(d3.pointer(event)[0]); | |
| const i = Math.round(x0); | |
| if (i >= 0 && i < data.time.length) {{ | |
| tooltip.transition() | |
| .duration(200) | |
| .style("opacity", .9); | |
| tooltip.html( | |
| `Time: ${{data.time[i]}}<br/>` + | |
| `Mean: ${{data.mean[i].toFixed(2)}}<br/>` + | |
| `Range: [${{data.lower[i].toFixed(2)}}, ${{data.upper[i].toFixed(2)}}]` | |
| ) | |
| .style("left", (event.pageX + 5) + "px") | |
| .style("top", (event.pageY - 28) + "px"); | |
| }} | |
| }}) | |
| .on("mouseout", function() {{ | |
| tooltip.transition() | |
| .duration(500) | |
| .style("opacity", 0); | |
| }}); | |
| }})(); | |
| </script> | |
| """ | |
| return "Unsupported visualization type" |