Public
Edited
Nov 18, 2024
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rawdata = d3.tsv(uri, d3.autoType)
Insert cell
Insert cell
rawdata
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
rawdata
X
hour
Y
day
Color
material
Size
Facet X
Facet Y
name
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
new Date(
Date.UTC(
rawdata.at(0).year,
rawdata.at(0).month - 1,
rawdata.at(0).day,
rawdata.at(0).hour,
rawdata.at(0).minute
)
)
Insert cell
Insert cell
demo_dataset = rawdata.map((d) => ({
date: new Date(Date.UTC(d.year, d.month - 1, d.day, d.hour, d.minute)),
...d
}))
Insert cell
Insert cell
import { dataset, names } from "0891ddb9b5334273"
Insert cell
dataset
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
names
Insert cell
Insert cell
[...dataset].sort((a, b) => a.hour - b.hour)
Insert cell
Insert cell
dataset.filter((d) => d.name === "kai_shiraishi")
Insert cell
Insert cell
dataset.find((d) => d.name === "kai_shiraishi")
Insert cell
Insert cell
d3.group(dataset, (d) => d.name)
Insert cell
Insert cell
[...d3.group(dataset, (d) => d.material).keys()]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dataGroupedByDates = d3.group(dataOfDates, (d) => d.dateOnly)
Insert cell
Insert cell
{
const width = 800;
const height = 300;
const margin = { top: 30, right: 30, bottom: 30, left: 120 };

const xScale = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([margin.left, width - margin.right]);

const yScale = d3
.scaleBand()
.domain([...dataGroupedByDates.keys()])
.range([margin.top, height - margin.bottom]);

const xAxis = d3
.axisBottom()
.scale(xScale)
.ticks(5)
.tickFormat(d3.utcFormat("%H:%M")) // 目盛りのフォーマット(書式)を指定
.tickSize(-(height - margin.top - margin.bottom));
const yAxis = d3
.axisLeft()
.scale(yScale)
.ticks(7)
.tickFormat(dateFormat) // 目盛りのフォーマット(書式)を指定
.tickSize(-(width - margin.left - margin.right));

const svg = d3.create("svg").attr("width", width).attr("height", height);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0.1);

svg
.append("g")
.attr("class", "xAxis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "yAxis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);

// 日付グループ
const dayG = svg
.append("g")
.selectAll("g")
.data(dataGroupedByDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) => `translate(0, ${yScale(d[0]) + yScale.bandwidth() / 2})`
); // 垂直軸の原点を移動しておく

// ドット
dayG
.selectAll("circle")
.data((d) => d[1]) // データの値(配列)をバインドする
.join("circle")
.attr("cx", (d) => xScale(d.msec))
.attr("r", 10)
.attr("fill", "steelblue")
.attr("opacity", 0.25);

svg
.selectAll("g.xAxis, g.yAxis")
.selectAll("line, path")
.attr("stroke", "white")
.attr("opacity", 0.5);

svg.selectAll("g.xAxis, g.yAxis").selectAll("text").attr("font-size", 16);

return svg.node();
}
Insert cell
Insert cell
dataOfDatesTimes = {
// データに day と time を追加
// timeは6時間ごとの時間帯で0,1,2,3の値をとる
const dataWithDayTime = dataOfName.map((d) => ({
...d,
dateOnly: new Date(
Date.UTC(
d.date.getUTCFullYear(),
d.date.getUTCMonth(),
d.date.getUTCDate()
)
),
time: Math.floor(
((d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000) /
(6 * 60 * 60 * 1000)
)
}));
return d3.group(
dataWithDayTime,
(d) => d.dateOnly,
(d) => d.time
);
}
Insert cell
{
const width = 800;
const height = 300;
const margin = { top: 30, right: 30, bottom: 30, left: 120 };

const xScale = d3
.scaleLinear()
.domain([0, 4])
.range([margin.left, width - margin.right]);

const yScale = d3
.scaleBand()
.domain([...dataOfDatesTimes.keys()])
.range([margin.top, height - margin.bottom]);

const xAxis = d3
.axisBottom()
.scale(xScale)
.ticks(4)
.tickFormat((d) => [0, 6, 12, 18, 24][d])
.tickSize(-(height - margin.top - margin.bottom));
const yAxis = d3
.axisLeft()
.scale(yScale)
.ticks(7)
.tickFormat(dateFormat)
.tickSize(-(width - margin.left - margin.right));

const svg = d3.create("svg").attr("width", width).attr("height", height);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0.1);

svg
.append("g")
.attr("class", "xAxis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "yAxis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);

const dayG = svg
.selectAll("g.day")
.data(dataOfDatesTimes)
.join("g")
.attr("class", "day")
.attr("transform", (d) => `translate(0, ${yScale(d[0])})`);

const timeG = dayG
.selectAll("g.time")
.data((d) => d[1])
.join("g")
.attr("class", "time")
.attr("transform", (d) => `translate(${xScale(d[0])}, 0)`);

// circle の cx を求めるスケール関数
const circleX = d3
.scaleBand()
// .domain([]) // domainは後で決める
.range([10, xScale(1) - xScale(0) - 10]); // xScale(1) - xScale(0)は各時間帯の幅

timeG
.append("rect")
.attr("x", 5)
.attr("y", 5)
.attr("width", xScale(1) - xScale(0) - 10)
.attr("height", yScale.bandwidth() - 10)
.attr("rx", 10)
.attr("fill", "#fff")
.attr("fill-opacity", 0.5)
.attr("stroke", "steelblue")
.attr("stroke-width", 1);

timeG
.selectAll("circle")
.data((d) => d[1])
.join("circle")
.attr("cx", (d, i, nodes) => {
// circleノードの数(nodes.length)がわかるので、circleXのdomainをここで指定する
// d3.range(5) は、 [0, 1, 2, 3, 4] になる
circleX.domain(d3.range(nodes.length));
return circleX(i) + circleX.bandwidth() / 2;
})
.attr("cy", yScale.bandwidth() / 2)
.attr("r", 10)
.attr("fill", "steelblue")
.attr("opacity", 0.5);

svg
.selectAll("g.xAxis, g.yAxis")
.selectAll("line, path")
.attr("stroke", "white")
.attr("opacity", 0.5);
svg.selectAll("g.xAxis, g.yAxis").selectAll("text").attr("font-size", 16);

return svg.node();
}
Insert cell
Insert cell
{
const width = 800;
const height = 300;
const margin = { top: 30, right: 30, bottom: 30, left: 120 };

const xScale = d3
.scaleLinear()
.domain([0, 4])
.range([margin.left, width - margin.right]);

const yScale = d3
.scaleBand()
.domain([...dataOfDatesTimes.keys()])
.range([margin.top, height - margin.bottom]);

const materials = d3.group(dataOfName, (d) => d.material);
const color = d3
.scaleOrdinal()
.domain([...materials.keys()])
.range(d3.schemeCategory10);

const xAxis = d3
.axisBottom()
.scale(xScale)
.ticks(4)
.tickFormat((d) => [0, 6, 12, 18, 24][d])
.tickSize(-(height - margin.top - margin.bottom));
const yAxis = d3
.axisLeft()
.scale(yScale)
.ticks(7)
.tickFormat(dateFormat)
.tickSize(-(width - margin.left - margin.right));

const svg = d3.create("svg").attr("width", width).attr("height", height);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0.1);

svg
.append("g")
.attr("class", "xAxis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "yAxis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);

const dayG = svg
.selectAll("g.day")
.data(dataOfDatesTimes)
.join("g")
.attr("class", "day")
.attr("transform", (d) => `translate(0, ${yScale(d[0])})`);

const timeG = dayG
.selectAll("g.time")
.data((d) => d[1])
.join("g")
.attr("class", "time")
.attr("transform", (d) => `translate(${xScale(d[0])}, 0)`);

const circleX = d3.scaleBand().range([10, xScale(1) - xScale(0) - 10]);

timeG
.selectAll("circle")
.data((d) => d[1])
.join("circle")
.attr("cx", (d, i, nodes) => {
circleX.domain(d3.range(nodes.length));
return circleX(i) + circleX.bandwidth() / 2;
})
.attr("cy", yScale.bandwidth() / 2)
.attr("r", 10)
.attr("fill", (d) => color(d.material))
.attr("opacity", 0.5);

svg
.selectAll("g.xAxis, g.yAxis")
.selectAll("line, path")
.attr("stroke", "white")
.attr("opacity", 0.5);
svg.selectAll("g.xAxis, g.yAxis").selectAll("text").attr("font-size", 16);

return svg.node();
}
Insert cell
materials = d3.group(dataset, (d) => d.material)
Insert cell
colorScale = d3
.scaleOrdinal()
.domain([...materials.keys()])
.range([
"white",
"orange",
"silver",
"black",
"paleblue",
"gray",
"green",
"yellow",
"none",
"silver",
"red",
"blue",
"gold",
"green",
"gray",
"white",
"purple"
])
Insert cell
colorScale("布")
Insert cell
Insert cell
sugimotoChart = (data) => {
// function myChart(data) { // 1行目と同じ意味
// const width = 800;
const height = (width * 150) / 215; // ポストカードと同じ縦横比にする
const margin = { top: height * 0.05, right: 30, bottom: 30, left: 120 };

const dataOfDatesTimes = getDataOfDatesTimes(data);

const xScale = d3
.scaleLinear()
.domain([0, 4])
.range([margin.left, width - margin.right]);

const yScale = d3
.scaleBand()
.domain([...dataOfDatesTimes.keys()])
.range([margin.top, height - margin.bottom]);

const dow = (d) => "日月火水木金土".at(new Date(d).getDay());

const xAxis = d3
.axisBottom()
.scale(xScale)
.ticks(4)
.tickFormat((d) => [0, 6, 12, 18, 24][d])
.tickSize(-(height - margin.top - margin.bottom));
const yAxis = d3
.axisLeft()
.scale(yScale)
.ticks(7)
.tickFormat((d) => d3.timeFormat(`%m月%d日`)(d) + ` (${dow(d)})`)
.tickSize(-(width - margin.left - margin.right));

const svg = d3.create("svg").attr("width", width).attr("height", height);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0.1);

svg
.append("g")
.attr("class", "xAxis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);
svg
.append("g")
.attr("class", "yAxis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);

const dayG = svg
.selectAll("g.day")
.data(dataOfDatesTimes)
.join("g")
.attr("class", "day")
.attr("transform", (d) => `translate(0, ${yScale(d[0])})`);

const timeG = dayG
.selectAll("g.time")
.data((d) => d[1])
.join("g")
.attr("class", "time")
.attr("transform", (d) => `translate(${xScale(d[0])}, 0)`);

const circleX = d3.scaleBand().range([10, xScale(1) - xScale(0) - 10]);

timeG
.selectAll("circle")
.data((d) => d[1])
.join("circle")
.attr("cx", (d, i, nodes) => {
circleX.domain(d3.range(nodes.length));
return circleX(i) + circleX.bandwidth() / 2;
})
.attr("cy", yScale.bandwidth() / 2)
.attr("r", 10)
.attr("fill", (d) => colorScale(d.material))
.attr("opacity", 0.5);

svg
.selectAll("g.xAxis, g.yAxis")
.selectAll("line, path")
.attr("stroke", "white")
.attr("opacity", 0.5);
svg.selectAll("g.xAxis, g.yAxis").selectAll("text").attr("font-size", 16);

return svg.node();
}
Insert cell
Insert cell
Insert cell
sugimotoLegend = (data) => {
const height = (width * 150) / 215;

const materials = [...d3.group(data, (d) => d.material).keys()];

const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width);

const g = svg
.append("g")
.selectAll("g")
.data(materials)
.join("g")
.attr("transform", (d, i) => `translate(10, ${20 + i * 20})`);

g.append("circle").attr("r", 10).attr("fill", colorScale);

g.append("text")
.attr("x", 20)
.attr("dy", 8)
.text((d) => d);

return svg.node();
}
Insert cell
Insert cell
Insert cell
sugimotoChart(dataOfName)
Insert cell
Insert cell
sugimotoChart(dataset.filter((d) => d.name === "fujitamai"))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
_heart = svg`<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 100 100" width="100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<g>
<path d="M94.5,29.4c0,12.4-7.8,25-23.3,37.9c-10.8,8.9-24.2,17.2-40.1,24.8c0.6-3.1,0.9-6,0.9-8.8c0-7.2-2.3-13.7-6.8-19.6
c-4.3-4.7-8.5-9.4-12.8-14.1c-4.6-5.7-6.8-12-6.8-18.8c0-6.7,2.3-12.2,6.8-16.5s10.1-6.5,16.9-6.5c4.6,0,8.9,1.2,12.8,3.6
c4.3,2.6,7,6.1,8.2,10.3C54.8,12.5,61.8,7.9,71,7.9c6.2,0,11.6,2,16.2,6C92.1,18.1,94.5,23.3,94.5,29.4z"/>
</g>
</svg>`
Insert cell
Insert cell
heart = svg`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100"><path d="M94.5 29.4c0 12.4-7.8 25-23.3 37.9C60.4 76.2 47 84.5 31.1 92.1c.6-3.1.9-6 .9-8.8 0-7.2-2.3-13.7-6.8-19.6-4.3-4.7-8.5-9.4-12.8-14.1-4.6-5.7-6.8-12-6.8-18.8 0-6.7 2.3-12.2 6.8-16.5s10.1-6.5 16.9-6.5c4.6 0 8.9 1.2 12.8 3.6 4.3 2.6 7 6.1 8.2 10.3C54.8 12.5 61.8 7.9 71 7.9c6.2 0 11.6 2 16.2 6 4.9 4.2 7.3 9.4 7.3 15.5z"/></svg>`
Insert cell
Insert cell
heartPath = d3.select(heart).select("path")
Insert cell
heartD = heartPath.attr("d")
Insert cell
d3Heart = d3.select(heart)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more