1735632180
2024-12-31 07:56:00
データ サイエンスと Web 開発の進化し続ける状況において、データの視覚化を習得することは単なる資産ではありません。それは必要不可欠です。過去数週間にわたり、私は 5 つの異なる JavaScript データ視覚化プロジェクトを巡る旅に乗り出し、それぞれのプロジェクトでデータの処理、視覚化、対話性のユニークな側面を学びました。私が学んだことの概要は次のとおりです。
プロジェクト 1: GDP 棒グラフ
私がやったこと:
// URL to fetch the data from
const dataUrl = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json";// Fetch the data
fetch(dataUrl)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // Parse the JSON response
})
.then((data) => {
createChart(data.data); // Pass the dataset to createChart
})
.catch((error) => {
console.error("Error fetching the data:", error);
});
// Function to create the bar chart
function createChart(dataset) {
const w = 800; // Width of the SVG
const h = 400; // Height of the SVG
const padding = 40; // Padding around the chart
// Append a title to the chart (#1)
d3.select("body")
.append("h1")
.attr("id", "title") // Title id
.text("United States GDP Bar Chart");
// Create an SVG container
const svg = d3.select("body").append("svg").attr("width", w).attr("height", h);
// Create scales for the chart
const xScale = d3
.scaleTime()
.domain([new Date(d3.min(dataset, (d) => d[0])), new Date(d3.max(dataset, (d) => d[0]))]) // Dates from the dataset
.range([padding, w - padding]);
const yScale = d3
.scaleLinear()
.domain([0, d3.max(dataset, (d) => d[1])]) // GDP values
.range([h - padding, padding]);
// Create axes
const xAxis = d3.axisBottom(xScale); // x-axis generator
const yAxis = d3.axisLeft(yScale); // y-axis generator
// Append x-axis (#2)
svg.append("g")
.attr("id", "x-axis") // x-axis id
.attr("transform", `translate(0, ${h - padding})`)
.call(xAxis);
// Append y-axis (#3)
svg.append("g")
.attr("id", "y-axis") // y-axis id
.attr("transform", `translate(${padding}, 0)`)
.call(yAxis);
// Ensure both axes have multiple ticks with class="tick" (#4)
d3.selectAll(".tick").classed("tick", true);
// Add rectangles for the bars (#5 - #11)
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("class", "bar") // Bar class
.attr("x", (d) => xScale(new Date(d[0]))) // Map date to x-axis (#10)
.attr("y", (d) => yScale(d[1])) // Map GDP to y-axis (#11)
.attr("width", (w - 2 * padding) / dataset.length) // Dynamically calculate bar width
.attr("height", (d) => h - padding - yScale(d[1])) // Height based on GDP value (#9)
.attr("fill", "navy") // Bar color
.attr("data-date", (d) => d[0]) // Data-date attribute (#6, #7)
.attr("data-gdp", (d) => d[1]) // Data-gdp attribute (#6, #8)
.on("mouseover", function (event, d) {
// Tooltip on mouseover (#12, #13)
tooltip
.style("opacity", 1)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 20}px`)
.attr("data-date", d[0]) // Set data-date attribute (#13)
.html(`Date: ${d[0]}
GDP: $${d[1]} Billion`);
})
.on("mouseout", function () {
tooltip.style("opacity", 0); // Hide tooltip
});
// Append a tooltip to the body (#12, #13)
const tooltip = d3
.select("body")
.append("div")
.attr("id", "tooltip") // Tooltip id
.style("position", "absolute")
.style("background-color", "lightgray")
.style("padding", "5px")
.style("border-radius", "5px")
.style("opacity", 0); // Initially hidden
}
- 学んだスキル: 私は、フェッチによる非同期データの取得と JSON の解析のスキルを磨きました。 D3.js を使用して、スケーラブルな軸の作成、データ バインディングの管理、ツールチップなどの対話型機能の実装を学びました。
- なぜ重要なのか: GDP などの経済データを視覚化すると、長期にわたる経済パターンを理解するのに役立ちます。これは政策立案者、投資家、教育者にとって非常に重要です。棒グラフを使用すると、即座に視覚的に比較し、傾向や異常を強調表示できます。
プロジェクト 2: プロ自転車レースにおけるドーピング
私がやったこと:
// Fetch the data
const dataUrl = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json";fetch(dataUrl)
.then((response) => response.json())
.then((data) => {
createChart(data);
})
.catch((error) => {
console.error("Error fetching the data:", error);
});
function createChart(dataset) {
const w = 800; // Width of the SVG
const h = 500; // Height of the SVG
const padding = 60; // Padding around the chart
// Add a title to the chart (#1)
d3.select("body").append("h1").attr("id", "title").text("Doping in Professional Bicycle Racing");
// Create the SVG container
const svg = d3.select("body").append("svg").attr("width", w).attr("height", h);
// Define scales for x and y axes
const xScale = d3
.scaleLinear()
.domain([d3.min(dataset, (d) => d.Year) - 1, d3.max(dataset, (d) => d.Year) + 1])
.range([padding, w - padding]);
const yScale = d3
.scaleTime()
.domain([
d3.min(dataset, (d) => new Date(1970, 0, 1, 0, d.Seconds / 60, d.Seconds % 60)),
d3.max(dataset, (d) => new Date(1970, 0, 1, 0, d.Seconds / 60, d.Seconds % 60)),
])
.range([h - padding, padding]);
// Create x-axis and append to the chart (#2)
const xAxis = d3.axisBottom(xScale).tickFormat(d3.format("d"));
svg.append("g")
.attr("id", "x-axis")
.attr("transform", `translate(0, ${h - padding})`)
.call(xAxis);
// Add x-axis label
svg.append("text")
.attr("x", w / 2)
.attr("y", h - 20)
.attr("text-anchor", "middle")
.text("Year");
// Create y-axis and append to the chart (#3)
const yAxis = d3.axisLeft(yScale).tickFormat(d3.timeFormat("%M:%S"));
svg.append("g").attr("id", "y-axis").attr("transform", `translate(${padding}, 0)`).call(yAxis);
// Add y-axis label
svg.append("text")
.attr("x", -h / 2)
.attr("y", 20)
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.text("Time of Rider");
// Append dots for data points (#4, #5, #6, #7, #8)
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d.Year)) // User Story #7
.attr("cy", (d) => yScale(new Date(1970, 0, 1, 0, d.Seconds / 60, d.Seconds % 60))) // User Story #8
.attr("r", 5)
.attr("class", (d) => (d["Doping"] === "" ? "dot nodoping" : "dot doping")) // Added nodoping/doping class
.attr("data-xvalue", (d) => d.Year) // User Story #5
.attr("data-yvalue", (d) => new Date(1970, 0, 1, 0, d.Seconds / 60, d.Seconds % 60)) // User Story #5
.on("mouseover", (event, d) => {
tooltip
.style("visibility", "visible")
.html(`${d.Name}, ${d.Nationality}
Year: ${d.Year}, Time: ${d.Time}`)
.attr("data-year", d.Year); // User Story #15
})
.on("mousemove", (event) => {
tooltip.style("top", `${event.pageY + 10}px`).style("left", `${event.pageX + 10}px`);
})
.on("mouseout", () => {
tooltip.style("visibility", "hidden");
});
// Add a legend (#13)
const legend = svg.append("g").attr("id", "legend");
legend
.append("rect")
.attr("x", w - 250)
.attr("y", padding)
.attr("width", 15)
.attr("height", 15)
.attr("fill", "blue");
legend
.append("text")
.attr("x", w - 230)
.attr("y", padding + 12)
.text("No doping allegations");
legend
.append("rect")
.attr("x", w - 250)
.attr("y", padding + 20)
.attr("width", 15)
.attr("height", 15)
.attr("fill", "orange");
legend
.append("text")
.attr("x", w - 230)
.attr("y", padding + 32)
.text("Riders with doping allegations");
// Add a tooltip (#14, #15)
const tooltip = d3
.select("body")
.append("div")
.attr("id", "tooltip")
.style("visibility", "hidden")
.style("position", "absolute")
.style("background-color", "lightgray")
.style("padding", "5px")
.style("border-radius", "5px");
}
- 学んだスキル: D3 を使用して時間スケール、特に散布図形式で時間を表す方法についての知識を深めました。ツールチップなどのインタラクティブな要素のイベント処理も重要なポイントでした。
- なぜ重要なのか: このプロジェクトは単なるデータの視覚化以上のものでした。それはデータを通じてストーリーを伝えることについてでした。時間を年に対してプロットすることで、ドーピング疑惑がパフォーマンスに及ぼす影響を視覚的に評価でき、スポーツ倫理とパフォーマンス指標についての洞察が得られます。
プロジェクト 3: 地球気温ヒートマップ
私がやったこと:
// URL to fetch the data from
const dataUrl = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json";// Fetch the data
fetch(dataUrl)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // Parse the JSON response
})
.then((data) => {
createChart(data.baseTemperature, data.monthlyVariance); // Pass base temp and dataset to createChart
})
.catch((error) => {
console.error("Error fetching the data:", error);
});
// Function to create the heat map
function createChart(baseTemperature, dataset) {
const width = 1200; // Width of the SVG
const height = 500; // Height of the SVG
const padding = 100; // Padding around the chart
// Create and append the tooltip dynamically
const tooltip = d3.select("body").append("div").attr("id", "tooltip");
// Add Title (#1)
d3.select("body").append("h1").attr("id", "title").text("Monthly Global Land-Surface Temperature");
// Add Description (#2)
d3.select("body").append("p").attr("id", "description").text("1753 - 2015: Base temperature 8.66°C");
// X and Y scales
const years = [...new Set(dataset.map((d) => d.year))];
const xScale = d3
.scaleBand()
.domain(years)
.range([padding, width - padding]);
const yScale = d3
.scaleBand()
.domain(d3.range(1, 13)) // Months from 1 to 12
.range([padding, height - padding]);
const colorScale = d3
.scaleSequential(d3.interpolateRdBu)
.domain([
baseTemperature + d3.max(dataset, (d) => d.variance),
baseTemperature + d3.min(dataset, (d) => d.variance),
]);
// SVG container
const svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
// X-axis (#3)
svg.append("g")
.attr("id", "x-axis")
.attr("transform", `translate(0, ${height - padding})`)
.call(d3.axisBottom(xScale).tickValues(years.filter((year, index) => index % 10 === 0)));
// Y-axis (#4)
svg.append("g")
.attr("id", "y-axis")
.attr("transform", `translate(${padding}, 0)`)
.call(d3.axisLeft(yScale).tickFormat((month) => d3.timeFormat("%B")(new Date(0, month - 1))));
// Heatmap Cells (#5, #6, #7, #8, #9, #10)
svg.selectAll(".cell")
.data(dataset)
.enter()
.append("rect")
.attr("class", "cell")
.attr("x", (d) => xScale(d.year))
.attr("y", (d) => yScale(d.month))
.attr("width", xScale.bandwidth())
.attr("height", yScale.bandwidth())
.attr("fill", (d) => colorScale(baseTemperature + d.variance))
.attr("data-month", (d) => d.month - 1)
.attr("data-year", (d) => d.year)
.attr("data-temp", (d) => baseTemperature + d.variance)
.on("mouseover", function (event, d) {
const tooltip = d3.select("#tooltip");
tooltip
.style("opacity", 1)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 10}px`)
.html(
`Year: ${d.year}
Month: ${d3.timeFormat("%B")(new Date(0, d.month - 1))}
Temperature: ${(
baseTemperature + d.variance
).toFixed(2)}°C`
)
.attr("data-year", d.year);
})
.on("mouseout", function () {
d3.select("#tooltip").style("opacity", 0);
});
// Legend (#13, #14, #15)
const legendWidth = 300;
const legendColors = 10;
const legendScale = d3.scaleLinear().domain(colorScale.domain()).range([0, legendWidth]);
const legendAxis = d3.axisBottom(legendScale).ticks(legendColors).tickFormat(d3.format(".1f"));
const legend = svg
.append("g")
.attr("id", "legend")
.attr("transform", `translate(${padding}, ${height - 40})`);
legend
.selectAll("rect")
.data(d3.range(legendColors))
.enter()
.append("rect")
.attr("x", (d) => (legendWidth / legendColors) * d)
.attr("y", -10)
.attr("width", legendWidth / legendColors)
.attr("height", 10)
.attr("fill", (d) =>
colorScale(colorScale.domain()[0] + (d / legendColors) * (colorScale.domain()[1] - colorScale.domain()[0]))
);
legend.append("g").call(legendAxis);
}
- 学んだスキル: D3.js のカラー スケールを使用して色の理論を深く掘り下げ、分散データを効果的に視覚的に表現する方法を理解しました。大規模なデータセットの処理と応答性の高いヒート マップの作成も重要な学習点でした。
- なぜ重要なのか: 気候変動は私たちの時代を決定づける問題の 1 つです。何世紀にもわたる気温データを視覚化することは、気候変動対策の緊急性を伝えるのに役立ちます。ヒートマップは、さまざまな地理的および時間的次元にわたるパターンと変化を確認する直感的な方法を提供します。
プロジェクト 4: 米国教育コロプレス マップ
私がやったこと:
// Define async function to load data
async function loadData() {
// Load data using Promise.all and await
const data = await Promise.all([
d3.json("https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json"),
d3.json("https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json"),
]);createChoroplethChart(data);
}
function createChoroplethChart(data) {
const [counties, education] = data;
// SVG and dimensions
const width = 960;
const height = 600;
const svg = d3.select("#choropleth").attr("width", width).attr("height", height);
// Color scale for education data
const colorScale = d3.scaleThreshold().domain([15, 30, 45, 60]).range(d3.schemeBlues[5]);
// Path generator
const path = d3.geoPath();
// Draw counties
svg.append("g")
.selectAll("path")
.data(topojson.feature(counties, counties.objects.counties).features)
.enter()
.append("path")
.attr("class", "county") // User Story #3: Each county has a class of "county".
.attr("d", path)
.attr("data-fips", (d) => d.id) // User Story #5: Assigning data-fips property.
.attr("data-education", (d) => {
const match = education.find((e) => e.fips === d.id);
return match ? match.bachelorsOrHigher : 0; // User Story #5: Assigning data-education property.
})
.attr("fill", (d) => {
const match = education.find((e) => e.fips === d.id);
return colorScale(match ? match.bachelorsOrHigher : 0); // User Story #4: At least 4 fill colors for counties.
});
// Tooltip
const tooltip = d3.select("#tooltip").style("opacity", 0).attr("data-education", 0); // User Story #10: Tooltip with id="tooltip".
svg.selectAll(".county")
.on("mouseover", (event, d) => {
const match = education.find((e) => e.fips === d.id);
tooltip.transition().duration(200).style("opacity", 0.9);
tooltip
.html(`Area: ${match.area_name}
Education: ${match.bachelorsOrHigher}%`)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 28}px`)
.attr("data-education", match.bachelorsOrHigher); // User Story #11: Tooltip with data-education property.
})
.on("mouseout", () => {
tooltip.transition().duration(500).style("opacity", 0);
});
// Legend for color scale
const legendWidth = 200;
const legendHeight = 20;
// Legend group
const legend = svg.append("g").attr("id", "legend").attr("transform", "translate(50, 550)");
legend
.selectAll("rect")
.data(colorScale.range())
.enter()
.append("rect")
.attr("x", (d, i) => i * (legendWidth / colorScale.range().length))
.attr("width", legendWidth / colorScale.range().length)
.attr("height", legendHeight)
.attr("fill", (d) => d);
// Adding labels under each color rectangle
legend
.selectAll("text")
.data(colorScale.domain())
.enter()
.append("text")
.text((d) => d)
.attr("x", (d, i) => (i + 0.5) * (legendWidth / colorScale.range().length))
.attr("y", legendHeight + 15)
.attr("text-anchor", "middle")
.attr("font-size", "10px");
// Logging to check if legend elements are created
console.log(d3.select("#legend").node());
}
// Call the async function to load data and continue
loadData();
- 学んだスキル: TopoJSON を使用した地理データの操作、コロプレス マップの作成、複雑なデータ構造の処理について学びました。マッピングに D3 を使用することは、空間データの視覚化について私に教えてくれた新しい挑戦でした。
- なぜ重要なのか: 地域全体の教育レベルは、政策決定、資金配分、教育戦略に影響を与える可能性があります。コロプレス マップは、学歴の格差や集中を明確に視覚的に表示するため、教育者や政策立案者にとって強力なツールとなります。
プロジェクト 5: ビデオ ゲームの販売ツリーマップ
私がやったこと:
// The dataset
const DATASET_URL = "https://cdn.freecodecamp.org/testable-projects-fcc/data/tree_map/video-game-sales-data.json";// Set up dimensions
const WIDTH = 960;
const HEIGHT = 570;
const LEGEND_HEIGHT = 100;
// Create SVG container
const svg = d3
.select("#treemap-container")
.append("svg")
.attr("width", WIDTH)
.attr("height", HEIGHT + LEGEND_HEIGHT);
// Create tooltip
const tooltip = d3.select("#tooltip");
// Fetch and process data
d3.json(DATASET_URL)
.then((data) => {
// Create treemap layout
const treemap = d3.treemap().size([WIDTH, HEIGHT]).padding(1);
// Create root hierarchy
const root = d3
.hierarchy(data)
.sum((d) => d.value)
.sort((a, b) => b.value - a.value);
// Generate treemap data
treemap(root);
// Color scale for different categories
const colorScale = d3
.scaleOrdinal()
.domain(root.children.map((d) => d.data.name))
.range(d3.schemeSet3);
// Create tiles
const cell = svg
.selectAll("g")
.data(root.leaves())
.enter()
.append("g")
.attr("transform", (d) => `translate(${d.x0},${d.y0})`);
// Add rectangles
cell.append("rect")
.attr("class", "tile")
.attr("data-name", (d) => d.data.name)
.attr("data-category", (d) => d.data.category)
.attr("data-value", (d) => d.data.value)
.attr("width", (d) => d.x1 - d.x0)
.attr("height", (d) => d.y1 - d.y0)
.attr("fill", (d) => colorScale(d.parent.data.name))
.on("mousemove", (event, d) => {
const [x, y] = d3.pointer(event, document.body);
tooltip
.style("display", "block")
.style("left", x + 10 + "px")
.style("top", y - 60 + "px")
.attr("data-value", d.data.value);
tooltip.select(".game-name").text(d.data.name);
tooltip.select(".category").text(`Category: ${d.parent.data.name}`);
tooltip.select(".value").text(`Value: ${d.data.value}M`);
})
.on("mouseout", () => {
tooltip.style("display", "none");
});
// Add text labels
cell.append("text")
.attr("class", "tile-text")
.selectAll("tspan")
.data((d) => d.data.name.split(/(?=[A-Z][^A-Z])/g))
.enter()
.append("tspan")
.attr("x", 4)
.attr("y", (d, i) => 13 + i * 10)
.text((d) => d);
// Create legend
const legend = svg
.append("g")
.attr("id", "legend")
.attr("transform", `translate(0, ${HEIGHT + 10})`);
const categories = root.children.map((d) => d.data.name);
const legendItems = legend
.selectAll("g")
.data(categories)
.enter()
.append("g")
.attr("transform", (d, i) => `translate(${Math.floor(i / 3) * 250}, ${(i % 3) * 25})`);
// Add legend rectangles
legendItems
.append("rect")
.attr("class", "legend-item")
.attr("width", 15)
.attr("height", 15)
.attr("fill", (d) => colorScale(d));
// Add legend text
legendItems
.append("text")
.attr("class", "legend-label")
.attr("x", 20)
.attr("y", 12)
.text((d) => d);
})
.catch((error) => {
console.error("Error loading the dataset:", error);
});
- 学んだスキル: ツリーマップは、サイズと色がさまざまなデータの次元を表す階層型データの視覚化を私に教えてくれました。また、SVG の操作とデータ駆動型のドキュメント構造の理解のスキルも向上しました。
- なぜ重要なのか: エンターテインメント業界、特にビデオゲームにはデータが豊富にあります。ツリーマップを通じてカテゴリ別およびタイトル別に売上データを視覚化することは、ゲーム開発者やマーケティング担当者の市場分析、トレンド発見、戦略計画に役立ちます。
学習データの可視化についての考察
- インタラクティブ性により理解が促進されます。 各プロジェクトでは、データをよりアクセスしやすく魅力的にするために、ツールチップやホバー効果などのインタラクティブな要素の重要性を強調しました。
- 適切なグラフの種類を選択することが重要です。 データのストーリーを正しい視覚化タイプ (比較用の棒グラフでも、分布用のヒートマップでも) と一致させることは、効果的なコミュニケーションにとって重要です。
- データ処理の精度: フェッチから解析まで、データ処理のあらゆるステップが最終的なビジュアライゼーションの精度と実用性に影響を与える可能性があります。
- 色を通したコミュニケーション: データ視覚化における色の理論を理解して適用すると、データのメッセージや認識が大きく変わる可能性があります。
データの視覚化は、単にデータを美しく見せることだけではありません。それはそれを話すようにすることです。これらのプロジェクトにより、説得力のあるデータ ストーリーを伝えるためのツールが私に備わったので、テクノロジーまたはデータ駆動型の分野に携わるすべての人には、このスキルに取り組むことをお勧めします。コードだけの問題ではありません。それは、私たちの周囲の世界の見方と理解の仕方を変えることです。
#私は #つの単純な #JavaScript #データ視覚化プロジェクトを実行しましたこれが私が学んだことです #エボジャクソンほか #年 #月