Public
Edited
Dec 22, 2022
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
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const width = 1000;
const height = 200;
const margin = { top: 10, right: 10, bottom: 20, left: 30 };

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

const xScale = d3
.scaleUtc()
.domain(d3.extent(dataOfName, (d) => d.date)) //(d) => d.date アクセサ関数
.range([margin.left, width - margin.right]);

const xAxis = d3
.axisBottom()
.scale(xScale)
.tickValues(xScale.ticks(d3.utcHour.every(12)))
.tickFormat(d3.utcFormat("%d日 %H時"));

svg
.append("g")
.selectAll("circle")
.data(dataOfName)
.join("circle")
.attr("cx", (d) => xScale(d.date))
.attr("cy", height / 2)
.attr("r", 3)
.attr("fill", "orange")
.attr("opacity", 0.5);
svg
.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(xAxis);

return svg.node();
}
Insert cell
Insert cell
dataOfDates = {
// データに day と msec を追加
// msecは、00:00からのミリ秒
const angle = d3
.scaleLinear()
.domain([0, 24 * 60 * 60 * 1000])
.range([0, Math.PI * 2]);

const dataWithDayMsec = dataOfName.map((d) => ({
day: new Date(
Date.UTC(
d.date.getUTCFullYear(),
d.date.getUTCMonth(),
d.date.getUTCDate()
)
),
msec: (d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000,
minutes: d.date.getUTCMinutes(),
startAngle: angle(
(d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000
),
endAngle:
angle((d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000) +
0.1,
...d //他のは全部残してね
}));

return d3.group(dataWithDayMsec, (d) => d.day);
}
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([...dataOfDates.keys()])
.range([margin.top, height - margin.bottom]);

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

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((d) => d3.utcFormat(`%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
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) => `translate(0, ${yScale(d[0]) + yScale.bandwidth() / 2})`
); // 垂直軸の原点を移動しておく

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

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
{
const width = 300;
const height = 300;
const margin = { top: 80, right: 80, bottom: 80, left: 80 };
const svg = d3
.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("width", width);

const angle = d3
.scaleUtc()
.domain(d3.extent(dataOfName, (d) => d.date))
.range([-90, 270]);

const xScale = d3
.scaleUtc()
.domain(d3.extent(dataOfName, (d) => d.date)) //(d) => d.date アクセサ関数
.range([0, height / 2]);

svg
.append("g")
.selectAll("rect")
.data(dataOfName)
.join("rect")
.attr("transform", (d) => `rotate(${angle(d.date)})`)
.attr("y", 20)
.attr("width", 10)
.attr("height", (d) => xScale(d.date))
.attr("fill", "none")
.attr("stroke", "black")
.attr("opacity", 0.5);

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

const angle = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000])
.range([-90, 270]);

const xScale = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([10, 100]);

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

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

// 日付グループ
const dayG = svg
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) => `translate(0, ${yScale(d[0]) + yScale.bandwidth() / 2})`
);

dayG
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("transform", (d) => `rotate(${angle(d.msec)})`)
.attr("transform-origin", width / 2)
.attr("x", width / 2)
.attr("height", (d) => xScale(d.msec))
.attr("width", 10)
.attr("fill", "none")
.attr("stroke", "black")
.attr("opacity", 0.5);

return svg.node();
}
Insert cell
{
const width = 1000;
const height = 600;
const margin = { top: 10, right: 10, bottom: 20, left: 30 };

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

const angle = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([-90, 270]);

const xScale = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([margin.left, width - margin.right]);

const yScale = d3.scaleUtc().domain([0, 60]).range([10, 100]); // 60 minutes

const dayScale = d3
.scaleLinear()
.domain([0, 30])
.range([0, height - margin.top]);

const dayG = svg
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) =>
`translate(${xScale(d[0]) + xScale.bandwidth() / 2}, ${dayScale(
d[1].length
)})`
);

dayG
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("transform", (d) => `rotate(${angle(d.msec)})`)
.attr("height", (d) => yScale(d.minutes))
.attr("width", 10)
.attr("fill", "none")
.attr("stroke", "black")
.attr("opacity", 0.5);

return svg.node();
}
Insert cell
{
const width = 1000;
const height = 600;
const margin = { top: 10, right: 10, bottom: 20, left: 30 };

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

const angle = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([-90, 270]);

const dayAngle = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([200, 360]);

const xScale = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([margin.left, width - margin.right]);

const yScale = d3.scaleUtc().domain([0, 60]).range([10, 100]); // 60 minutes

const dayScale = d3
.scaleLinear()
.domain([0, d3.max(dataOfDates, (d) => d[1].length)])
.range([0, height - margin.top - 100]);

// test line
svg
.append("g")
.selectAll("line")
.data(dataOfDates)
.join("line")
.attr("x1", width / 2)
.attr("y1", height)
.attr(
"x2",
(d) =>
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr(
"y2",
(d) =>
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("stroke", "#fffffe");

const dayG = svg
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) =>
`translate(${
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
}, ${
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
})`
);

dayG
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("transform", (d) => `rotate(${angle(d.msec)})`)
.attr("height", (d) => yScale(d.minutes))
.attr("width", 10)
.attr("fill", "none")
.attr("stroke", "black")
.attr("opacity", 0.5);

return svg.node();
}
Insert cell
{
const width = 1200;
const height = 700;
const margin = { top: 20, right: 20, bottom: 20, left: 20 };

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

const angle = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([-90, 270]);

const dayAngle = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([200, 360]);

const xScale = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([margin.left, width - margin.right]);

const yScale = d3.scaleUtc().domain([0, 59]).range([30, 130]); // 60 minutes

const dayScale = d3
.scaleLinear()
.domain([0, d3.max(dataOfDates, (d) => d[1].length)])
.range([0, width / 2 - margin.left]);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "gray")
.attr("opacity", 1);

svg
.append("g")
.selectAll("line")
.data(dataOfDates)
.join("line")
.attr(
"x1",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("y1", 0)
.attr(
"x2",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr(
"y2",
(d) =>
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("stroke", "#000");

const dayG = svg
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) =>
`translate(${
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
}, ${
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
})`
);

dayG
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("transform", (d) => `rotate(${angle(d.msec)})`)
.attr("height", (d) => yScale(d.minutes))
.attr("width", 10)
.attr("rx", 4)
.attr("ry", 4)
.attr("fill", (d, i) => {
if (d.turning === "ON") {
return "white";
} else if (d.turning === "OFF") {
return "black";
} else if (d.turning === "OTHER") {
return "orange";
}
})
.attr("stroke", "black")
.attr("opacity", 1);

svg
.append("g")
.selectAll("circle")
.data(dataOfDates)
.join("circle")
.attr(
"cx",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr(
"cy",
(d) =>
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("r", 13)
.attr("stroke", "black")
.attr("fill", "white");

return svg.node();
}
Insert cell
data = d3.range(24).map((d) => ({
msec: d * 60 * 60 * 1000
}))
Insert cell
{
const svg = d3
.create("svg")
.attr("viewBox", [-50, -50, 100, 100])
.attr("width", 400);

// 円グラフにするには、d3.pie()でデータを作る
// const pie = d3
// .pie()
// .value((d) => d.msec)(data)
const angle = d3
.scaleLinear()
.domain([0, 24 * 60 * 60 * 1000])
.range([0, Math.PI * 2]);
const pie = dataOfName.map((d) => ({
startAngle: angle(
(d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000
),
endAngle:
angle((d.date.getUTCHours() * 60 + d.date.getUTCMinutes()) * 60 * 1000) +
0.26
}));
const arc = d3.arc().innerRadius(0).outerRadius(40);

const g = svg.append("g").selectAll("g").data(pie).join("g");

g.append("path").attr("d", arc).attr("fill", "white").attr("stroke", "black");

return svg.node();
}
Insert cell
{
const width = 1200;
const height = 700;
const margin = { top: 20, right: 20, bottom: 20, left: 20 };

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

const angle = d3
.scaleUtc()
.domain([0, 24 * 60 * 60 * 1000]) // 24 hours
.range([-90, 270]);

const dayAngle = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([200, 360]);

const xScale = d3
.scaleBand()
.domain([...dataOfDates.keys()])
.range([margin.left, width - margin.right]);

const yScale = d3.scaleUtc().domain([0, 59]).range([30, 130]); // 60 minutes

const dayScale = d3
.scaleLinear()
.domain([0, 30])
.range([0, height - margin.top]);

svg
.append("rect")
.attr("width", width)
.attr("height", height)
.attr("fill", "#ddddff")
.attr("opacity", 0.2);

svg
.append("g")
.selectAll("line")
.data(dataOfDates)
.join("line")
.attr(
"x1",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("y1", 0)
.attr(
"x2",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr(
"y2",
(d) =>
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("stroke", "#000");

const dayG = svg
.append("g")
.selectAll("g")
.data(dataOfDates)
.join("g")
.attr("class", "day")
.attr(
"transform",
(d) =>
`translate(${
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
}, ${
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
})`
);

dayG
.selectAll("rect")
.data((d) => d[1])
.join("rect")
.attr("transform", "rotate(180)")
.attr("height", (d) => yScale(d.minutes))
.attr("width", 0)
.transition()
.duration(2000)
.delay((d, i) => i * 80)
.attr("transform", (d) => `rotate(${angle(d.msec)})`)
.attr("height", (d) => yScale(d.minutes))
.attr("width", 10)
.attr("rx", 4)
.attr("ry", 4)
.attr("fill", (d, i) => {
if (d.turning === "ON") {
return "white";
} else if (d.turning === "OFF") {
return "black";
} else if (d.turning === "OTHER") {
return "orange";
}
})
.attr("stroke", "black")
.attr("opacity", 1);

svg
.append("g")
.selectAll("circle")
.data(dataOfDates)
.join("circle")
.attr(
"cx",
(d) =>
margin.left +
width / 2 +
Math.cos(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr(
"cy",
(d) =>
height +
Math.sin(dayAngle(d[0]) * (Math.PI / 180)) * dayScale(d[1].length)
)
.attr("r", 13)
.attr("stroke", "black")
.attr("fill", "white");

return svg.node();
}
Insert cell
d3.extent(dataOfDates, (d) => d[1].length)
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