Download public/Figure_8a_barchart.html from latentcanon/code: direct link, hf CLI and curl.
- Browser
- Download file 7.27 kB
-
https://huggingface.co/latentcanon/code/resolve/main/public/Figure_8a_barchart.html
- Command line
-
hf download hf://latentcanon/code/public/Figure_8a_barchart.html
-
curl -L -o Figure_8a_barchart.html https://huggingface.co/latentcanon/code/resolve/main/public/Figure_8a_barchart.html
7.27 kB
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| font-family: "Segoe UI", sans-serif; | |
| margin: 20px; | |
| } | |
| svg { | |
| font: 12px sans-serif; | |
| } | |
| .bar { | |
| cursor: pointer; | |
| } | |
| .bar:hover { | |
| opacity: 0.8; | |
| } | |
| .axis text { | |
| font-size: 11px; | |
| } | |
| .legend { | |
| font-size: 12px; | |
| } | |
| .legend rect { | |
| stroke-width: 1; | |
| stroke: #000; | |
| } | |
| h2 { | |
| text-align: center; | |
| } | |
| #downloadBtn { | |
| display: block; | |
| margin: 0 auto 20px; | |
| } | |
| </style> | |
| <body> | |
| <h2>Bar Chart: Gender → Profession</h2> | |
| <button id="downloadBtn">Download SVG</button> | |
| <svg id="chart"></svg> | |
| <script src="https://d3js.org/d3.v6.min.js"></script> | |
| <script> | |
| /* -------------------------------------------------------- | |
| 1. PROFESSION COLORS | |
| -------------------------------------------------------- */ | |
| const colorMap = { | |
| "Adult Performer": "#8A2BE2", | |
| "Model": "#DC143C", | |
| "Actor": "#FF7F50", | |
| "Public Figure": "#20B2AA", | |
| "Singer, Musician": "wheat", | |
| "Sports Professional": "gold", | |
| "Voice Actor": "lightgreen", | |
| "Online Personality": "#4682B4", | |
| "Other": "#ccc" | |
| }; | |
| /* -------------------------------------------------------- | |
| 2. GENDER ORDER | |
| -------------------------------------------------------- */ | |
| const genderOrder = ["Female", "Male", "Other"]; | |
| /* -------------------------------------------------------- | |
| 3. LOAD JSON + TRANSFORM IT | |
| -------------------------------------------------------- */ | |
| d3.json("json/sunburst_gender_A.json").then(data => { | |
| const flatData = []; | |
| data.children.forEach(gender => { | |
| if (gender.children) { | |
| gender.children.forEach(prof => { | |
| flatData.push({ | |
| gender: gender.name, | |
| profession: prof.name, | |
| value: prof.value | |
| }); | |
| }); | |
| } | |
| }); | |
| /* -------------------------------------------------------- | |
| 3a. CALCULATE PROFESSION ORDER BY TOTAL OCCURRENCE | |
| -------------------------------------------------------- */ | |
| const professionCounts = {}; | |
| flatData.forEach(d => { | |
| professionCounts[d.profession] = (professionCounts[d.profession] || 0) + d.value; | |
| }); | |
| // Sort professions by count (descending), but put "Other" last | |
| const professionOrder = Object.entries(professionCounts) | |
| .sort((a, b) => { | |
| // If either is "Other", it goes last | |
| if (a[0] === "Other") return 1; | |
| if (b[0] === "Other") return -1; | |
| // Otherwise sort by count descending | |
| return b[1] - a[1]; | |
| }) | |
| .map(entry => entry[0]); | |
| // Group per gender | |
| const genderData = d3.rollup( | |
| flatData, | |
| v => ({ | |
| total: d3.sum(v, d => d.value), | |
| professions: v | |
| }), | |
| d => d.gender | |
| ); | |
| // Sort according to gender ordering | |
| const genders = Array.from(genderData.keys()).sort((a, b) => { | |
| const ai = genderOrder.indexOf(a); | |
| const bi = genderOrder.indexOf(b); | |
| return (ai === -1 ? Infinity : ai) - (bi === -1 ? Infinity : bi); | |
| }); | |
| /* -------------------------------------------------------- | |
| 4. SVG SETUP | |
| -------------------------------------------------------- */ | |
| const margin = {top: 40, right: 200, bottom: 80, left: 80}; | |
| const width = 800 - margin.left - margin.right; | |
| const height = 600 - margin.top - margin.bottom; | |
| const svg = d3.select("#chart") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", `translate(${margin.left},${margin.top})`); | |
| /* -------------------------------------------------------- | |
| 5. PREPARE DATA WITH SORTED SEGMENTS PER GENDER | |
| -------------------------------------------------------- */ | |
| // Build stacked data manually with segments sorted by size per gender | |
| const stackedData = []; | |
| genders.forEach(gender => { | |
| const genderInfo = genderData.get(gender); | |
| const professions = genderInfo.professions; | |
| // Sort professions by value descending (largest at bottom) | |
| const sortedProfs = [...professions].sort((a, b) => b.value - a.value); | |
| let y0 = 0; | |
| sortedProfs.forEach(prof => { | |
| stackedData.push({ | |
| gender: gender, | |
| profession: prof.profession, | |
| value: prof.value, | |
| y0: y0, | |
| y1: y0 + prof.value | |
| }); | |
| y0 += prof.value; | |
| }); | |
| }); | |
| /* -------------------------------------------------------- | |
| 6. AXES | |
| -------------------------------------------------------- */ | |
| const x = d3.scaleBand() | |
| .domain(genders) | |
| .range([0, width]) | |
| .padding(0.3); | |
| const y = d3.scaleLinear() | |
| .domain([0, d3.max(Array.from(genderData.values()), d => d.total)]) | |
| .nice() | |
| .range([height, 0]); | |
| svg.append("g") | |
| .attr("transform", `translate(0,${height})`) | |
| .call(d3.axisBottom(x)) | |
| .selectAll("text") | |
| .style("text-anchor", "middle") | |
| .style("font-weight", "bold") | |
| .style("font-size", "14px"); | |
| svg.append("g") | |
| .call(d3.axisLeft(y)); | |
| svg.append("text") | |
| .attr("transform", "rotate(-90)") | |
| .attr("y", 0 - margin.left + 20) | |
| .attr("x", 0 - height / 2) | |
| .style("font-weight", "bold") | |
| .text("Count"); | |
| /* -------------------------------------------------------- | |
| 7. DRAW BARS (using manually stacked data) | |
| -------------------------------------------------------- */ | |
| svg.selectAll(".bar") | |
| .data(stackedData) | |
| .join("rect") | |
| .attr("class", "bar") | |
| .attr("x", d => x(d.gender)) | |
| .attr("y", d => y(d.y1)) | |
| .attr("height", d => y(d.y0) - y(d.y1)) | |
| .attr("width", x.bandwidth()) | |
| .attr("fill", d => colorMap[d.profession]) | |
| .append("title") | |
| .text(d => `${d.gender} – ${d.profession}: ${d.value}`); | |
| /* -------------------------------------------------------- | |
| 8. LEGEND (ordered by occurrence - unchanged) | |
| -------------------------------------------------------- */ | |
| const legend = svg.append("g") | |
| .attr("transform", `translate(${width + 20}, 0)`); | |
| professionOrder.forEach((prof, i) => { | |
| const row = legend.append("g").attr("transform", `translate(0,${i * 22})`); | |
| row.append("rect") | |
| .attr("width", 18) | |
| .attr("height", 18) | |
| .attr("fill", colorMap[prof]); | |
| row.append("text") | |
| .attr("x", 24) | |
| .attr("y", 9) | |
| .attr("dy", "0.35em") | |
| .text(`${prof} (${professionCounts[prof] || 0})`); | |
| }); | |
| /* -------------------------------------------------------- | |
| 9. DOWNLOAD SVG BUTTON | |
| -------------------------------------------------------- */ | |
| document.getElementById("downloadBtn").addEventListener("click", () => { | |
| const svgNode = document.querySelector("#chart"); | |
| const clone = svgNode.cloneNode(true); | |
| clone.setAttribute("xmlns", "http://www.w3.org/2000/svg"); | |
| const all = clone.querySelectorAll("*"); | |
| all.forEach(el => { | |
| const style = window.getComputedStyle(el); | |
| el.setAttribute("style", `font:${style.font}; fill:${style.fill}; stroke:${style.stroke};`); | |
| }); | |
| const svgData = new XMLSerializer().serializeToString(clone); | |
| const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = "gender_bar_chart.svg"; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |