agentsclimarketplace

Run3 GenerateD3VisualizationLogic

Skill cxcscmu/SkillLearnBench/skills/b3-teacher-feedback-gemini-3-flash-preview/stock-data-visualization/run3_GenerateD3VisualizationLogic

Creates the core D3.js logic in visualization.js. This includes data loading, custom market cap formatting (supporting "T" for Trillions), force simulation for sector clustering, bubble generation, and bidirectional table highlighting.From its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill run3_GenerateD3VisualizationLogic

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

4.8 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

# Write JS to /root/output/js/visualization.js
cat << 'EOF' > /root/output/js/visualization.js
async function init() {
    const data = await d3.csv("data/stock-descriptions.csv");
    
    // Formatting logic: Convert to T, B, M
    const formatCap = (val) => {
        if (!val || isNaN(val)) return "N/A";
        const n = parseFloat(val);
        if (n >= 1e12) return (n / 1e12).toFixed(2) + "T";
        if (n >= 1e9) return (n / 1e9).toFixed(2) + "B";
        if (n >= 1e6) return (n / 1e6).toFixed(2) + "M";
        return n.toLocaleString();
    };

    const sectors = Array.from(new Set(data.map(d => d.sector))).filter(s => s);
    const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(sectors);
    
    // SVG Setup
    const width = 800, height = 600;
    const svg = d3.select("#bubble-chart").append("svg")
        .attr("viewBox", `0 0 ${width} ${height}`)
        .attr("width", "100%")
        .attr("height", "100%");

    // Map sectors to center points for force clustering
    const sectorCenters = {};
    sectors.forEach((s, i) => {
        const angle = (i / sectors.length) * 2 * Math.PI;
        sectorCenters[s] = {
            x: width / 2 + Math.cos(angle) * 150,
            y: height / 2 + Math.sin(angle) * 150
        };
    });

    // Size scale: Market Cap vs uniform for ETFs
    const radiusScale = d3.scaleSqrt()
        .domain([0, d3.max(data, d => parseFloat(d.marketCap) || 0)])
        .range([10, 50]);

    const nodes = data.map(d => ({
        ...d,
        radius: d.marketCap ? radiusScale(parseFloat(d.marketCap)) : 15,
        x: Math.random() * width,
        y: Math.random() * height
    }));

    const simulation = d3.forceSimulation(nodes)
        .force("x", d3.forceX(d => d.sector ? sectorCenters[d.sector].x : width / 2).strength(0.15))
        .force("y", d3.forceY(d => d.sector ? sectorCenters[d.sector].y : height / 2).strength(0.15))
        .force("collide", d3.forceCollide(d => d.radius + 2))
        .force("center", d3.forceCenter(width / 2, height / 2));

    const bubbleGroup = svg.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", "node")
        .attr("id", d => `node-${d.ticker}`)
        .on("click", (event, d) => highlightStock(d.ticker));

    const circles = bubbleGroup.append("circle")
        .attr("class", "bubble")
        .attr("r", d => d.radius)
        .attr("fill", d => d.sector ? colorScale(d.sector) : "#ccc")
        .on("mouseover", (event, d) => {
            if (d.marketCap) {
                const tt = d3.select("#tooltip");
                tt.classed("hidden", false)
                  .style("left", (event.pageX + 10) + "px")
                  .style("top", (event.pageY + 10) + "px");
                d3.select("#tooltip-ticker").text(d.ticker);
                d3.select("#tooltip-name").text(d.name);
                d3.select("#tooltip-sector").text(d.sector);
            }
        })
        .on("mouseout", () => d3.select("#tooltip").classed("hidden", true));

    bubbleGroup.append("text")
        .attr("class", "label")
        .text(d => d.ticker);

    simulation.on("tick", () => {
        bubbleGroup.attr("transform", d => `translate(${d.x},${d.y})`);
    });

    // Render Table
    const tableBody = d3.select("#table-body");
    const rows = tableBody.selectAll("tr")
        .data(data)
        .enter().append("tr")
        .attr("id", d => `row-${d.ticker}`)
        .on("click", (event, d) => highlightStock(d.ticker));

    rows.append("td").text(d => d.ticker);
    rows.append("td").text(d => d.name);
    rows.append("td").text(d => d.sector || "N/A");
    rows.append("td").text(d => formatCap(d.marketCap));

    // Render Legend
    const legend = d3.select("#legend-container");
    sectors.forEach(s => {
        const item = legend.append("div").attr("class", "legend-item");
        item.append("div").attr("class", "legend-box").style("background", colorScale(s));
        item.append("span").text(s);
    });

    // Interaction Logic
    function highlightStock(ticker) {
        d3.selectAll("tr").classed("highlight", false);
        d3.selectAll("circle").style("stroke", "#fff").style("stroke-width", "1.5px");

        const selectedRow = d3.select(`#row-${ticker}`);
        selectedRow.classed("highlight", true);
        selectedRow.node().scrollIntoView({ behavior: 'smooth', block: 'center' });

        const selectedCircle = d3.select(`#node-${ticker} circle`);
        selectedCircle.style("stroke", "#000").style("stroke-width", "3px");
    }
}
init();
EOF

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.