Public
Edited
Jan 13, 2024
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, 1200, 675])
// .style("font", "8px sans-serif")
.attr(
"style",
"max-width: 100%; height: 675; font: 10px 'Noto Sans JP', sans-serif; font-weight: bold;"
);

// SVGのサイズ設定
const width = 1200;
const height = 675;
const margin = { top: 10, right: 10, bottom: 60, left: 100 };

// day2から最新の日付を取得
const latestDate = day2;

// 日付を "m/d" 形式にフォーマット
const dateFormat = d3.timeFormat("%Y-%m-%d");
// 最新の日付を表示するテキストを追加
svg
.append("text")
.attr("text-anchor", "start")
.attr("x", margin.left + 10) // x座標を左マージンから10px右に設定
.attr("y", margin.top + 60) // y座標を上マージンから20px下に設定
.text(`${dateFormat(latestDate)}`) // 日付をフォーマットしてテキストに設定
.attr("font-size", "80px") // フォントサイズを設定
.attr("opacity", 0.2) // フォントサイズを設定
.attr("fill", "black"); // フォントの色を設定

svg
.append("text")
.attr("text-anchor", "start")
.attr("x", margin.left + 10) // x座標を左マージンから10px右に設定
.attr("y", margin.top + 100) // y座標を上マージンから20px下に設定
.text(`NHK紅白歌合戦 on 公式YouTUbe`) // 日付をフォーマットしてテキストに設定
.attr("font-size", "28px") // フォントサイズを設定
.attr("opacity", 0.2) // フォントサイズを設定
.attr("fill", "black"); // フォントの色を設定

// 軸のスケールを設定
const xScale = d3
.scaleLinear()
// .domain([0, 2900000])
.domain([0, 12000000])
.range([margin.left, width - margin.right]);

const yScale = d3
.scaleLinear()
// .domain([0, 50000])
.domain([0, 200000])
.range([height - margin.bottom, margin.top]);

// 折れ線グラフ用のラインジェネレータを作成
const lineGenerator = d3
.line()
.x((d) => xScale(d.平均ViewCount))
.y((d) => yScale(d.平均LikeCount));

// データをTitleShortごとにグループ化
const groupedData = d3.group(
data2.filter((d) => d.日付 <= day2),
(d) => d.TitleShort
);

// 各グループの最新のデータポイントを取得
const latestDataPoints = Array.from(groupedData, ([key, values]) => {
return values.sort((a, b) => new Date(b.日付) - new Date(a.日付))[0];
});

// 各グループごとに線セグメントを描画
groupedData.forEach((values, key) => {
values.forEach((d, i) => {
if (i < values.length - 1) {
const nextD = values[i + 1];
svg
.append("path")
.attr(
"d",
`M ${xScale(d.平均ViewCount)} ${yScale(d.平均LikeCount)} L ${xScale(
nextD.平均ViewCount
)} ${yScale(nextD.平均LikeCount)}`
)
.attr("stroke", color(key))
.attr("stroke-width", Math.sqrt(d.平均ViewCount) * 0.05) // 線の太さを動的に設定
.attr("stroke-opacity", 0.05)
.attr("fill", "none");
}
});
});

// データの描画
data2
.filter((d) => d.日付 <= day2)
.forEach((d) => {
const isLatestDate = latestDataPoints.some(
(latestD) => latestD.日付.getTime() === d.日付.getTime()
);
svg
.append("circle")
.attr("cx", xScale(d.平均ViewCount))
.attr("cy", yScale(d.平均LikeCount))
.attr("r", Math.sqrt(d.平均ViewCount) * 0.02) // 半径を適宜調整
.attr("fill", color(d.TitleShort)) // TitleShortに基づいて色を設定
.attr("fill-opacity", isLatestDate ? 0.6 : 0.2); // 透明度を設定
});

// 最新のデータポイントにラベルテキストを描画
latestDataPoints.forEach((d) => {
svg
.append("text")
.attr("text-anchor", "middle")
.attr("x", xScale(d.平均ViewCount))
.attr("y", yScale(d.平均LikeCount))
.text(d.TitleShort)
.attr("font-size", `${Math.max(0.000004 * d.平均ViewCount, 10)}px`)
.attr("fill", "black")
.attr("dy", "0.35em") // Y軸方向の調整を追加
.attr("stroke", "white")
.attr("stroke-width", `${Math.max(0.000005 * d.平均ViewCount, 10) * 0.1}`)
.attr("style", "font-weight: bold; fill-opacity: 1.0;")
.style("paint-order", "stroke fill");
});

// Y軸の目盛りラベルを設定
const yAxis = svg
.append("g")
.attr("class", "y-axis")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yScale).ticks(5)) // 目盛りの数を設定
.selectAll("text")
.style("font-size", "16px")
.attr("fill", "#666666"); // フォントの色を設定;;

// 0を非表示にするため、0の要素を選択して非表示に設定
yAxis.filter((d) => d === 0).remove();

// X軸の目盛りラベルを設定
const xAxis = svg
.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xScale).ticks(5)) // 目盛りの数を設定
.selectAll("text")
.style("font-size", "16px")
.attr("fill", "#666666"); // フォントの色を設定;

// 0を非表示にするため、0の要素を選択して非表示に設定
xAxis.filter((d) => d === 0).remove();

// x軸のラベルを追加
svg
.append("text")
.attr("text-anchor", "middle")
.attr("x", width / 2) // x座標をSVGの中央に設定
.attr("y", height - margin.bottom + 50) // y座標をx軸の下に設定
.text("視聴回数") // テキストを設定
.attr("font-size", "20px") // フォントサイズを設定
.attr("fill", "#666666"); // フォントの色を設定

// X軸の線を非表示にする
svg.select(".x-axis").selectAll("path").remove();

// X軸の線を非表示にする
svg.select(".y-axis").selectAll("path").remove();

// y軸のラベルを追加
svg
.append("text")
.attr("text-anchor", "middle")
.attr("x", -height / 2) // x座標をy軸の左に設定
.attr("y", margin.left - 80) // y座標をSVGの上に設定
.attr("transform", "rotate(-90)") // テキストを90度回転
.text("いいね数") // テキストを設定
.attr("font-size", "20px") // フォントサイズを設定
.attr("fill", "#666666"); // フォントの色を設定;

svg
.append("text")
.attr("x", 10) // 左から10pxの位置
.attr("y", height + 13) // 下から10pxの位置(キャプションのベースライン)
.text("YouTubeデータより徒然研究室(仮称)作成。") // キャプションのテキスト
.attr("fill", "#535353")
.attr(
"style",
"max-width: 100%; height: auto; font: 10px 'Noto Sans JP', sans-serif; font-weight: normal;"
)
.style("font-size", "12px"); // フォントサイズを12pxに設定

// SVG要素を返す
return svg.node();
}
Insert cell
230110_紅白チャンネルデータ変換231230_only.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
color = d3
.scaleOrdinal(
data2.map((d) => d.TitleShort),
d3.schemeTableau10
)
.unknown("black")
Insert cell
lastDefined = ({
reduceIndex(I, X) {
for (let i = I.length - 1; i >= 0; --i) {
const x = X[I[i]];
if (x != null) {
return x; // 最初に見つかった非null値を返す
}
}
}
})
Insert cell
csvData = FileAttachment(
"230110_紅白チャンネルデータ変換231230_only.csv"
).csv({ typed: true })
Insert cell
data2 = _230110.map((d) => {
const parts = d.日付.split("/");
const year = parts[0];
const month = parts[1].padStart(2, "0");
const day = parts[2].padStart(2, "0");

d.日付 = `${year}-${month}-${day}`;

// 日付形式に変換
d.日付 = new Date(`${year}-${month}-${day}`);
return d;
})
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell
// Noto Sans JP Blackフォントをロードする関数
async function loadFont() {
return new Promise((resolve) => {
const font = new FontFace(
"Noto Sans JP Black",
"url(https://cdn.plot.ly/plotly-texture/fonts/v1.3/noto-sans-jp-v13-latin-700.woff)"
);
font.load().then(() => {
document.fonts.add(font);
resolve();
});
});
}
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