Dragged more for a long time, the recent graduation procedures are almost done, should be able to come back…

Series of portals:

  1. D3 visualization :(1) at first sight, SVG and D3 charm
  2. D3 visualization :(2) Bar Chart with D3js
  3. Update in the…

Knowledge:

  • D3 Data Binding
  • Columnar drawing
  • coordinate
  • scale

Data is read in

The first step in data visualization is data reading. In D3, you can easily read data using D3.csv, which returns a Promise object.

CSV files are separated by commas. The local CSV data is as follows. The file name is data.csv:

country,population
China,1415046
India,1354052
United States,326767
Indonesia,266795
Brazil,210868
Pakistan,200814
Nigeria,195875
Bangladesh,166368
Russia,143965
Mexico,130759
Copy the code

First read the data in with the following code:

const data = d3.csv('data.csv').then(data= > {
    console.log(data))
})
Copy the code

As you can see, the console outputs an array where each piece of data is an object of type {country:xx, population:xx}

In order to convert the Number of people into units, expand all the data by a thousand times, i.e. :

const data = d3.csv('data.csv').then(data= > {
    data.forEach(element= > {
        element.population = +element.population * 1000
    });  // After processing the data, we can start drawing
    render(data)
})
Copy the code

Draw a histogram

The HTML file contains only one < SVG >
tag as in the previous section. Select SVG first:

const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');

const render = data= >{}// Draw a rendering function based on existing data
Copy the code

Data binding

Binding data to the DOM is the biggest feature of D3. D3.select and d3.selectAll return a selection set, but they have no data of their own and can be bound to by the data() function. There are two correlation functions:

  • selection.datum([value])Each element on the selection set is bound to the same element value
  • selection.data(values[,key])Select each element in the set to bind each item of the array values. Key is a key function that specifies the rules for binding the array.

Datum is less commonly used, and data() is used to bind the processed data to the DOM.

Update, Enter, and exit

When performing data binding, the number of data and elements may not be the same, so dynamic processing is required. This requires updata, Enter, and exit.

  • Update () does not actually exist when the corresponding element is exactly the same (binding data = the corresponding element), but it is imagined in conjunction with the subsequent Enter and exit instructions. But when the corresponding elements exactly meet, the direct operation can be followed directly by text, style and other operations can be.
  • Enter () When the corresponding element is insufficient (number of binding data > corresponding element) When the corresponding element is insufficient, the element is usually added to equal the number of binding data. Append is usually followed by append.
  • Exit () when there are too many corresponding elements (number of binding data < corresponding elements) When there are too many corresponding elements, the elements are usually removed so that they are equal to the number of binding data. It’s usually followed by remove.

This project mainly uses Enter, because the page only has SVG tags, what we need to do is draw

in SVG to represent the bar graph according to the data content. Understand Update, Enter, and Exit

Render function

With that in mind, you can start drawing

const render = data= > {
    svg.selectAll('rect').data(data)  // Select 'rect' and bind data, but there are no elements at this time, so use Enter
        .enter().append('rect')
        .attr('width',width)
        .attr('height'.'30px')}Copy the code

Update the view so that you can see that there are already images. But all I could see was a black rectangle. Because the graph does not currently reflect any data, it is simply a rectangle with a fixed ‘width’. To do this, you need to use data, but because the data can be large or small, you need to use scale in order for it to fit into the view.

scale

There are many scales in D3. In this example, linear scale (scaleLinear) and ordinal scale (scaleBand) are mainly used.

The linear scale maps the contents of the domain linearly to a range within the range, ensuring that no matter how large or small the initial value is, it fits the current view well. Mapping relationship:

The ordinal scale is not a continuous scale, domain() uses an array, and range() is a continuous field. Mapping relationship:

So, with the addition of scale in both directions, let the shape of the bar chart begin to appear:

const render = data= > {
    const xScale = d3.scaleLinear()
        .domain([0,d3.max(data, d => d.population)])
        .range([0,width]) // The maximum value fills up the view space
    const yScale = d3.scaleBand()
        .domain(data.map(d= > d.country))
        .range([0,height])

    svg.selectAll('rect').data(data)  // Select 'rect' and bind data, but there are no elements at this time, so use Enter
        .enter().append('rect')
        .attr('y',d => yScale(d.country))
        .attr('width',d => xScale(d.population)) // Width according to data
        .attr('height',yScale.bandwidth()) // The height is automatically generated by the scale
}
Copy the code

In this way, there is a prototype, the effect is as follows:

Code optimization

First of all, re-examine the code and find that the D => D. population and D => D.C. try appear several times in the setting and use of scale. If it needs to be modified, it will be repeated in many places in the code. To do this, give it a processing as follows:

const render = data= > {
    const xValue = d= > d.population; / / optimization
    const yValue = d= > d.country;  / / optimization

    const xScale = d3.scaleLinear()
        .domain([0,d3.max(data,xValue)]) / / optimization
        .range([0,width]) 
    const yScale = d3.scaleBand()
        .domain(data.map(yValue))  / / optimization
        .range([0,height])

    svg.selectAll('rect').data(data) 
        .enter().append('rect')
        .attr('y',d => yScale(yValue(d)))   / / optimization
        .attr('width',d => xScale(xValue(d))) / / optimization
        .attr('height',yScale.bandwidth()) 
Copy the code

coordinate

Use margins to optimize your layout

The following figure shows the layout diagram of margin. Since directly padding the canvas in accordance with the height and width of SVG will result in no spare space to place coordinate axes and so on, a margin is used here to replan the layout.

The code is as follows:

const margin = {left:50.top:10.right:20.bottom:30};
const innerHeight = height - margin.top - margin.bottom;
const innerWidth = width - margin.left - margin.right;
Copy the code

Where innerHeight and innerWidth are the actual occupied heights of the bar graph, so the code for the bar graph can be modified to read:

    const xScale = d3.scaleLinear()
        .domain([0, d3.max(data,xValue)])
        .range([0, innerWidth]) // Change width to innerWidth
    const yScale = d3.scaleBand()
        .domain(data.map(yValue))
        .range([0, innerHeight]) // Change height to innerHeight
    const g = svg.append('g')
        .attr('transform'.`translate(${margin.left}.${margin.top}) `) // Add a new element g to move maring.left and margine.top
    g.selectAll('rect').data(data)  
        .enter().append('rect')
        .attr('y',d => yScale(yValue(d)))
        .attr('width', d => xScale(xValue(d)))
        .attr('height',yScale.bandwidth())
Copy the code

Add coordinate axes

< SVG > , using functions such as axisLeft axisBottom, etc., drawing is generally divided into the following steps:

  • Create coordinate axesvar axisX = d3.axisLeft(xScale)Create axes based on scale
  • Create a new<g>groupvar gAxis = svg.append('g')
  • Insert coordinate axesaxisX(gAxis)Or directly in the previous stepsvg.append('g').call(axisX)

Therefore, in this example, the axes could be added like this:

    g.append('g').call(d3.axisLeft(yScale)); // The left side shows country
    g.append('g').call(d3.axisBottom(xScale))
        .attr('transform'.`translate(0,${innerHeight}) `) // It is bottom, but the default position is not below
Copy the code

Increase the clearance

Now that the bar chart is ugly, I should add a little space to make it look nice. It’s really easy. Use the padding property on yScale.

    const yScale = d3.scaleBand()
        .domain(data.map(yValue))
        .range([0, innerHeight])
        .padding(0.15)   // Add this attribute
Copy the code

Modify the style

To make the bar chart look nice, add some CSS styles like this:

body html{
            margin:0;
            overflow: hidden;
        }
        rect {
            fill:steelblue;
        }
        text {
            font-size: 1.1 em;
        }
Copy the code

The final result

Pay attention to

Because of chrome’s security limitations, reading local files using d3.csv is problematic. File //: reading content is not supported. Therefore, the code works on Github, but it may not work locally. You can open a local server and put the code on it.

The complete code

See d3 series tutorial source for the complete code