Spaces:
Running
Running
File size: 9,498 Bytes
677155a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
// https://observablehq.com/@d3/treemap@480
import define1 from "./a33468b95d0b15b0@808.js";
import define2 from "./7a9e12f9fb3d8e06@459.js";
function _1(md){return(
md`# Treemap, CSV
Introduced by [Ben Shneiderman](http://www.cs.umd.edu/hcil/treemap-history/), treemaps recursively partition space into rectangles according to each node’s associated value. D3 supports several treemap tiling methods. See also [nested](/@d3/nested-treemap), [zoomable](/@d3/zoomable-treemap) and [animated](/@d3/animated-treemap) treemaps.`
)}
function _tile(Inputs,d3){return(
Inputs.select(
new Map([
["binary", d3.treemapBinary],
["squarify", d3.treemapSquarify],
["slice-dice", d3.treemapSliceDice],
["slice", d3.treemapSlice],
["dice", d3.treemapDice]
]),
{label: "Tiling method", value: d3.treemapBinary}
)
)}
function _key(Swatches,chart){return(
Swatches(chart.scales.color)
)}
function _chart(Treemap,flare,tile){return(
Treemap(flare, {
path: d => d.name.replace(/\./g, "/"), // e.g., "flare/animate/Easing"
value: d => d?.size, // size of each node (file); null for internal nodes (folders)
group: d => d.name.split(".")[1], // e.g., "animate" in "flare.animate.Easing"; for color
label: (d, n) => [...d.name.split(".").pop().split(/(?=[A-Z][a-z])/g), n.value.toLocaleString("en")].join("\n"),
title: (d, n) => `${d.name}\n${n.value.toLocaleString("en")}`, // text to show on hover
link: (d, n) => `https://github.com/prefuse/Flare/blob/master/flare/src${n.id}.as`,
tile, // e.g., d3.treemapBinary; set by input above
width: 1152,
height: 1152
})
)}
function _5(md){return(
md`This example uses a CSV file to represent the hierarchy as tabular data: each row in the file represents a node in the tree. If a *path* option is specified, the Treemap function can automatically impute the internal (parent) nodes and hence the CSV only needs to include leaves; however, if you use the *id* and *parentId* options instead, then the CSV file must include the internal nodes as well as the leaves. See the [JSON treemap](/@d3/json-treemap) example for using a JSON data source.`
)}
function _flare(FileAttachment){return(
FileAttachment("flare-2.csv").csv({typed: true})
)}
function _7(howto){return(
howto("Treemap")
)}
function _Treemap(d3,location){return(
function Treemap(data, { // data is either tabular (array of objects) or hierarchy (nested objects)
path, // as an alternative to id and parentId, returns an array identifier, imputing internal nodes
id = Array.isArray(data) ? d => d.id : null, // if tabular data, given a d in data, returns a unique identifier (string)
parentId = Array.isArray(data) ? d => d.parentId : null, // if tabular data, given a node d, returns its parent’s identifier
children, // if hierarchical data, given a d in data, returns its children
value, // given a node d, returns a quantitative value (for area encoding; null for count)
sort = (a, b) => d3.descending(a.value, b.value), // how to sort nodes prior to layout
label, // given a leaf node d, returns the name to display on the rectangle
group, // given a leaf node d, returns a categorical value (for color encoding)
title, // given a leaf node d, returns its hover text
link, // given a leaf node d, its link (if any)
linkTarget = "_blank", // the target attribute for links (if any)
tile = d3.treemapBinary, // treemap strategy
width = 640, // outer width, in pixels
height = 400, // outer height, in pixels
margin = 0, // shorthand for margins
marginTop = margin, // top margin, in pixels
marginRight = margin, // right margin, in pixels
marginBottom = margin, // bottom margin, in pixels
marginLeft = margin, // left margin, in pixels
padding = 1, // shorthand for inner and outer padding
paddingInner = padding, // to separate a node from its adjacent siblings
paddingOuter = padding, // shorthand for top, right, bottom, and left padding
paddingTop = paddingOuter, // to separate a node’s top edge from its children
paddingRight = paddingOuter, // to separate a node’s right edge from its children
paddingBottom = paddingOuter, // to separate a node’s bottom edge from its children
paddingLeft = paddingOuter, // to separate a node’s left edge from its children
round = true, // whether to round to exact pixels
colors = d3.schemeTableau10, // array of colors
zDomain, // array of values for the color scale
fill = "#ccc", // fill for node rects (if no group color encoding)
fillOpacity = group == null ? null : 0.6, // fill opacity for node rects
stroke, // stroke for node rects
strokeWidth, // stroke width for node rects
strokeOpacity, // stroke opacity for node rects
strokeLinejoin, // stroke line join for node rects
} = {}) {
// If id and parentId options are specified, or the path option, use d3.stratify
// to convert tabular data to a hierarchy; otherwise we assume that the data is
// specified as an object {children} with nested objects (a.k.a. the “flare.json”
// format), and use d3.hierarchy.
const root = path != null ? d3.stratify().path(path)(data)
: id != null || parentId != null ? d3.stratify().id(id).parentId(parentId)(data)
: d3.hierarchy(data, children);
// Compute the values of internal nodes by aggregating from the leaves.
value == null ? root.count() : root.sum(d => Math.max(0, value(d)));
// Prior to sorting, if a group channel is specified, construct an ordinal color scale.
const leaves = root.leaves();
const G = group == null ? null : leaves.map(d => group(d.data, d));
if (zDomain === undefined) zDomain = G;
zDomain = new d3.InternSet(zDomain);
const color = group == null ? null : d3.scaleOrdinal(zDomain, colors);
// Compute labels and titles.
const L = label == null ? null : leaves.map(d => label(d.data, d));
const T = title === undefined ? L : title == null ? null : leaves.map(d => title(d.data, d));
// Sort the leaves (typically by descending value for a pleasing layout).
if (sort != null) root.sort(sort);
// Compute the treemap layout.
d3.treemap()
.tile(tile)
.size([width - marginLeft - marginRight, height - marginTop - marginBottom])
.paddingInner(paddingInner)
.paddingTop(paddingTop)
.paddingRight(paddingRight)
.paddingBottom(paddingBottom)
.paddingLeft(paddingLeft)
.round(round)
(root);
const svg = d3.create("svg")
.attr("viewBox", [-marginLeft, -marginTop, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 10);
const node = svg.selectAll("a")
.data(leaves)
.join("a")
.attr("xlink:href", link == null ? null : (d, i) => link(d.data, d))
.attr("target", link == null ? null : linkTarget)
.attr("transform", d => `translate(${d.x0},${d.y0})`);
node.append("rect")
.attr("fill", color ? (d, i) => color(G[i]) : fill)
.attr("fill-opacity", fillOpacity)
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("stroke-opacity", strokeOpacity)
.attr("stroke-linejoin", strokeLinejoin)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);
if (T) {
node.append("title").text((d, i) => T[i]);
}
if (L) {
// A unique identifier for clip paths (to avoid conflicts).
const uid = `O-${Math.random().toString(16).slice(2)}`;
node.append("clipPath")
.attr("id", (d, i) => `${uid}-clip-${i}`)
.append("rect")
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);
node.append("text")
.attr("clip-path", (d, i) => `url(${new URL(`#${uid}-clip-${i}`, location)})`)
.selectAll("tspan")
.data((d, i) => `${L[i]}`.split(/\n/g))
.join("tspan")
.attr("x", 3)
.attr("y", (d, i, D) => `${(i === D.length - 1) * 0.3 + 1.1 + i * 0.9}em`)
.attr("fill-opacity", (d, i, D) => i === D.length - 1 ? 0.7 : null)
.text(d => d);
}
return Object.assign(svg.node(), {scales: {color}});
}
)}
export default function define(runtime, observer) {
const main = runtime.module();
function toString() { return this.url; }
const fileAttachments = new Map([
["flare-2.csv", {url: new URL("./files/a6b0d94a7f5828fd133765a934f4c9746d2010e2f342d335923991f31b14120de96b5cb4f160d509d8dc627f0107d7f5b5070d2516f01e4c862b5b4867533000", import.meta.url), mimeType: "text/csv", toString}]
]);
main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
main.variable(observer()).define(["md"], _1);
main.variable(observer("viewof tile")).define("viewof tile", ["Inputs","d3"], _tile);
main.variable(observer("tile")).define("tile", ["Generators", "viewof tile"], (G, _) => G.input(_));
main.variable(observer("key")).define("key", ["Swatches","chart"], _key);
main.variable(observer("chart")).define("chart", ["Treemap","flare","tile"], _chart);
main.variable(observer()).define(["md"], _5);
main.variable(observer("flare")).define("flare", ["FileAttachment"], _flare);
main.variable(observer()).define(["howto"], _7);
main.variable(observer("Treemap")).define("Treemap", ["d3","location"], _Treemap);
const child1 = runtime.module(define1);
main.import("Swatches", child1);
const child2 = runtime.module(define2);
main.import("howto", child2);
return main;
}
|