12

Data Visualization for Dummies: 3 Simple Charts with <70 Lines of Code

 3 years ago
source link: https://hackernoon.com/data-visualization-for-dummies-3-simple-charts-with-less70-lines-of-code
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Data Visualization for Dummies: 3 Simple Charts with <70 Lines of Code

The most commonly used charts - line, bar, and pie charts - are used in data analysis and representation applications. I pick these charts for the most common use in most of the applications made for data analysis or representation. I did not use any framework, just HTML, CSS, and JavaScript. I recommend inspecting charts in the DOM to understand how D3 renders the elements. It takes around 70 lines of code to implement it.

Listen to this story

Speed:
Read by:
voice-avatar
Maksym Mostovyi

Software developer with expertise in JavaScript, Angular, AngularJs, D3

Here I would like to talk about converting large, complex data into an easy visual representation format. For that, I pick the most commonly used charts - line, bar, and pie.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Actually, there are plenty of charts, but you can find these three in most of the applications made for data analysis or representation. Next, I will explain how to build them by using the popular library D3.js.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

I’ve created a small demo application. It is a simple dashboard with those charts. I did not use any framework, just HTML, CSS, and JavaScript.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Also, I added 3 separate files with data set for each of the charts. I think it is more than enough to begin drawing the charts. But first, let’s take a look at the HTML structure:

0 reactions
heart.png
light.png
money.png
thumbs-down.png
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>D3 charts</title>
    <link rel="stylesheet" href="styles.css">
    <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script type="module" src="charts/bar-chart.js" defer></script>
    <script type="module" src="charts/line-chart.js" defer></script>
    <script type="module" src="charts/pie-chart.js" defer></script>
</head>
<body>
<header>
    <span>D3 Dashboard</span>
</header>
<section>
    <div class="bar-line-charts">
        <div class="chart-widget">
            <div class="widget-header">
                <span>Line chart</span>
            </div>
            <div id="line-chart" class="chart"></div>
        </div>
        <div class="chart-widget">
            <div class="widget-header">
                <span>Bar chart</span>
            </div>
            <div id="bar-chart" class="chart"></div>
        </div>
    </div>
    <div class="pie-chart">
        <div class="chart-widget">
            <div class="widget-header">
                <span>Pie chart</span>
            </div>
            <div id="d3-chart" class="chart" style="padding: 90px"></div>
        </div>
    </div>
</section>
</body>
</html>

Let’s draw the first chart, it is going to be a pie, then I will continue with the line and bar chart.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Pie Chart

import data from '../data/pie-data.js'

// Selecting the element
const element = document.getElementById('d3-chart');

// Setting dimensions
const margin = 10,
    width = 400,
    height = 400;

// Setting the radius of the pie
const radius = Math.min(width, height) / 2 - margin;

const svg = d3.select(element)
    .append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("style", "margin-top: -32px !important")
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// Setting the color scale
const color = d3.scaleOrdinal()
    .domain(data)
    .range(["#43ab92", "#f75f00", "#512c62"]);

// Setting the position of each group on the pie
const pie = d3.pie()
    .value(function (d) {
        return d[1].value;
    });

const data_ready = pie(Object.entries(data));

// Building arcs
const arcGenerator = d3.arc()
    .innerRadius(0)
    .outerRadius(radius);

// Building the pie chart
svg.selectAll('slices')
    .data(data_ready)
    .enter()
    .append('path')
    .attr('d', arcGenerator)
    .attr('fill', function (d) {
        return (color(d.data[1].name))
    });

// Adding titles to pie slices
svg.selectAll('slices')
    .data(data_ready)
    .enter()
    .append('text')
    .text(function (d) {
        return d.data[1].name
    })
    .attr("transform", function (d) {
        return "translate(" + arcGenerator.centroid(d) + ")";
    })
    .style("text-anchor", "middle")
    .style("font-size", 20);

Line Chart

import lineData from '../data/line-data.js'

// Selecting the element
const element = document.getElementById('line-chart');

// Setting dimensions
const margin = {top: 40, right: 30, bottom: 7, left: 50},
    width = 900 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

// Adding helper functions to make the line look nice
// The <defs> element is used to store graphical objects that will be used at a later time.
const createGradient = function(select) {
    const gradient = select
        .select('defs')
        .append('linearGradient')
        .attr('id', 'gradient')
        .attr('x1', '0%')
        .attr('y1', '100%')
        .attr('x2', '0%')
        .attr('y2', '0%');

    gradient.append('stop')
        .attr('offset', '0%')
        .attr('style', 'stop-color:#da6e2a;stop-opacity:0.05');

    gradient.append('stop')
        .attr('offset', '100%')
        .attr('style', 'stop-color:#da6e2a;stop-opacity:.5');
}

const createGlowFilter = function(select) {
    const filter = select
        .select('defs')
        .append('filter')
        .attr('id', 'glow')

    filter.append('feGaussianBlur')
        .attr('stdDeviation', '4')
        .attr('result', 'coloredBlur');

    const femerge = filter
        .append('feMerge');

    femerge.append('feMergeNode')
        .attr('in', 'coloredBlur');

    femerge.append('feMergeNode')
        .attr('in', 'SourceGraphic');
}

// Parsing timestamps
const parseTime = d3.timeParse('%Y/%m/%d');

const parsedData = lineData.map(item => (
    {
        values: item.values.map((val) => ({
            total: val.total,
            date: parseTime(val.date)
        }))
    }));

// Appending svg to a selected element
const svg = d3.select(element)
    .append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', 300 + margin.top + margin.bottom)
    .attr("viewBox", `0 40 ${width + 80} ${height}`)
    .append('g')
    .attr('transform', `translate(${margin.left}, ${margin.top})`);

// Setting X,Y scale ranges
const xScale = d3.scaleTime()
    .domain([
        d3.min(parsedData, (d) => d3.min(d.values, (v) => v.date)),
        d3.max(parsedData, (d) => d3.max(d.values, (v) => v.date))
    ])
    .range([0, width]);

const yScale = d3.scaleLinear()
    .domain([
        d3.min(parsedData, (d) => d3.min(d.values, (v) => v.total)),
        d3.max(parsedData, (d) => d3.max(d.values, (v) => v.total))
    ])
    .range([height, 0]);

// Appending <defs>
svg.append('defs');
svg.call(createGradient);
svg.call(createGlowFilter);

// Drawing line with inner gradient
// Adding functionality to make line curved
const line = d3.line()
    .x(function(d) {
        return xScale(d.date);
    })
    .y(function(d) {
        return yScale(d.total);
    })
    .curve(d3.curveCatmullRom.alpha(0.5));

// Drawing inner part of a line
svg.selectAll('.line')
    .data(parsedData)
    .enter()
    .append('path')
    .attr('d', function(d) {
        const lineValues = line(d.values).slice(1);
        const splitedValues = lineValues.split(',');

        return `M0,${height},${lineValues},l0,${height - splitedValues[splitedValues.length - 1]}`
    })
    .style('fill', 'url(#gradient)');

// Drawing a line
svg.selectAll('.line')
    .data(parsedData)
    .enter()
    .append('path')
    .attr('d', function(d) {
        return line(d.values)
    })
    .attr('stroke-width', '2')
    .style('fill', 'none')
    .style('filter', 'url(#glow)')
    .attr('stroke', '#ff6f3c');

// Adding the x Axis
svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale));

// Adding the y Axis
svg.append("g")
    .call(d3.axisLeft(yScale));

Bar Chart

import barData from '../data/bar-data.js'

// Selecting the element
const element = document.getElementById('bar-chart');

// Setting dimensions
const margin = {top: 40, right: 20, bottom: 50, left: 50},
    width = 900 - margin.left - margin.right,
    height = 480 - margin.top - margin.bottom;

// Setting X,Y scale ranges
const xScale = d3.scaleBand()
    .range([0, width])
    .padding(0.1);

const yScale = d3.scaleLinear()
    .range([height, 0]);

// Appending svg to a selected element
const svg = d3.select(element).append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("viewBox", `0 40 ${width + 80} ${height}`)
    .append("g")
    .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

// Formatting the data
barData.forEach(function (d) {
    d.value = +d.value;
});

// Scaling the range of the data in the domains
xScale.domain(barData.map(function (d) {
    return d.name;
}));
yScale.domain([0, d3.max(barData, function (d) {
    return d.value;
})]);

// Appending the rectangles for the bar chart
svg.selectAll(".bar")
    .data(barData)
    .enter().append("rect")
    .attr("x", function (d) {
        return xScale(d.name);
    })
    .attr("width", xScale.bandwidth())
    .attr("y", function (d) {
        return yScale(d.value);
    })
    .attr("height", function (d) {
        return height - yScale(d.value);
    })
    .style("fill", "#ff6f3c");

// Adding the x Axis
svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale));

// Adding the y Axis
svg.append("g")
    .call(d3.axisLeft(yScale));

Wrapping up

As you can see, all these charts do not have difficult logic. It takes around 70 lines of code to implement it. That is how we can make complex data to be converted in easy visual representation format.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Try it yourself! The source code can be found on GitHub. I recommend inspecting charts in the DOM to understand how D3 renders the elements.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
4
heart.pngheart.pngheart.pngheart.png
light.pnglight.pnglight.pnglight.png
boat.pngboat.pngboat.pngboat.png
money.pngmoney.pngmoney.pngmoney.png
by Maksym Mostovyi @maksymmostovyi. Software developer with expertise in JavaScript, Angular, AngularJs, D3Read my stories
Join Hacker Noon

Create your free account to unlock your custom reading experience.


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK