UNHCR Logo
  • Guidance
  • Chart types
  • Resources
  • Tutorials
  • Product gallery
Tutorials
  • R
    • Change over time
    • Comparison
    • Correlation
    • Distribution
    • Geospatial
    • Part-to-a-whole
    • Ranking
  • Matplotlib
    • Change over time
    • Comparison
    • Correlation
    • Distribution
    • Part-to-a-whole
    • Ranking
  • Plotly Python
    • Comparison
  • D3
    • Change over time
    • Comparison
    • Correlation
    • Distribution
    • Geospatial
    • Part-to-a-whole
On this page
  • Bubble chart
  • Connected scatterplot
  • Scatterplot
  1. Home
  2. Tutorials
  3. D3
  4. Correlation

Bubble chart

A bubble chart displays multi-dimensional data in a two-dimensional plot. It can be considered as a variation of the scatterplot, in which the dots are replaced with bubbles. However, unlike a scatterplot which has only two variables defined by the X and Y axis, on a bubble chart each data point (bubble) can be assigned with a third variable (by size of bubble) and a fourth variable (by colour of bubble).

More about: Bubble chart - Other tutorials: R Matplotlib

            
              <!DOCTYPE html>
              <meta charset="utf-8">
              <!-- Include d3 library -->
              <script src="https://d3js.org/d3.v7.min.js"></script>
              <!-- Create a container to host the chart -->
              <div id="bubble_container"></div>
            
          
            
                             
            
          
            

              // set the dimensions and margins of the graph
              const margin = {top: 60, right: 60, bottom: 50, left: 50};
              const width = 450 - margin.left - margin.right;
              const height = 350 - margin.top - margin.bottom;

              // append the svg object to the body of the page
              const svg = d3.select("#bubble_container")
                .append("svg")
                  .attr("width", "100%")
                  .attr("height", "100%")
                  .attr("viewBox", "0 0 450 350")
                  .attr("preserveAspectRatio", "xMinYMin")
                .append("g")
                  .attr("transform", `translate(${margin.left}, ${margin.top})`);

              // parse the Data
              d3.csv("https://raw.githubusercontent.com/GDS-ODSSS/unhcr-dataviz-platform/master/data/correlation/bubble.csv")
              .then(function(data){

              // list of value keys
              const typeKeys = data.columns.slice(1);

              // X scale and Axis
              const f =  d3.format("~s")
              const xScale = d3.scaleLinear()
                .domain(d3.extent(data, d => +d.refugee_number)).nice()
                .range([0, width])
              svg
                .append('g')
                .attr("transform", `translate(0, ${height})`)
                .call(d3.axisBottom(xScale).tickSize(0).ticks(5).tickPadding(8).tickFormat(f))
                .call(d => d.select(".domain").remove());
              
              // Y scale and Axis
              const yScale = d3.scaleLinear()
                  .domain(d3.extent(data, d => +d.idp_number)).nice()
                  .range([height, 0]);
              svg
                .append('g')
                .call(d3.axisLeft(yScale).tickSize(0).ticks(6).tickPadding(4).tickFormat(f))
                .call(d => d.select(".domain").remove());
              
              // bubble size scale
              const zScale = d3.scaleSqrt()
                .domain([600000, 160000000])
                .range([2, 50])

              // color palette
              const color = d3.scaleOrdinal()
                .domain(typeKeys)
                .range(["#EF4A60", "#8EBEFF", "#00B398", "#E1CC0D", "#589BE5", "#18375F", "#0072BC"])
              
              // set horizontal grid line
              const GridLineH = () => d3.axisLeft().scale(yScale);
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineH()
                  .tickSize(-width,0,0)
                  .tickFormat("")
                  .ticks(6)
              ); 

              // set vertical grid line
              const GridLineV = () => d3.axisBottom().scale(xScale);
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineV()
                  .tickSize(height,0,0)
                  .tickFormat("")
                  .ticks(5)); 

              // create a tooltip
              const tooltip = d3.select("body")
                .append("div")
                  .attr("class", "tooltip");

              // tooltip events
              const mouseover = function(d) {
                  tooltip
                      .style("opacity", 1)
                  d3.select(this)
                      .style("stroke", "#EF4A60")
                      .style("opacity", 1)
              };
              const mousemove = function(event,d) {
                  const f = d3.format(",");
                  tooltip
                  .html("<div><b>Total displaced:</b> " +f(d.total_number)+ "</div><div><b>Refugees:</b> " +f(d.refugee_number)+ "</div><div><b>IDPs:</b> "+ f(d.idp_number) +"</div>")
                      .style("top", event.pageY - 10 + "px")
                      .style("left", event.pageX + 10 + "px")
              };
              const mouseleave = function(d) {
                  tooltip
                      .style("opacity", 0)
                  d3.select(this)
                      .style("stroke", "none")
                      .style("opacity", 0.5)
              };
              
              // create graph
              svg.append("g")
                .selectAll("g")
                .data(data)
                .join("circle")
                    .attr("cx", d => xScale(+d.refugee_number))
                    .attr("cy", d => yScale(+d.idp_number))
                    .attr("r", d =>zScale(+d.total_number))
                    .style("fill", d => color(d.region))
                    .style("opacity", 0.6)
                .on("mouseover", mouseover)
                .on("mousemove", mousemove)
                .on("mouseleave", mouseleave)

              // set data labels  
              svg
                .append("g")
                .attr("font-size", 10)
                .selectAll("text")
                .data(data)
                .join("text")
                  .attr("dy", "0.35em")
                  .attr("x", d => xScale(+d.refugee_number) + 15)
                  .attr("y", d => yScale(+d.idp_number))
                  .text(d => d.region);
                                
              // set title
              svg
                .append("text")
                  .attr("class", "chart-title")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", -(margin.top)/1.5)
                  .attr("text-anchor", "start")
                .text("Comparison of refugee and IDP population by region | 2021")

              // set X axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", width/1.7)
                  .attr("y", height+margin.bottom*0.6)
                  .attr("text-anchor", "middle")
                .text("Number of refugees (millions)");
          
              // set Y axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", -(margin.top/8))
                  .attr("text-anchor", "start")
                .text("Number of IDPs (millions)")
              
              // set source
              svg
                .append("text")
                  .attr("class", "chart-source")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", height + margin.bottom*0.8)
                  .attr("text-anchor", "start")
                .text("Source: UNHCR")

              // set copyright
              svg
                .append("text")
                  .attr("class", "copyright")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", height + margin.bottom*0.95)
                  .attr("text-anchor", "start")
                .text("©UNHCR, The UN Refugee Agency")
              })

            
          

Connected scatterplot

A connected scatterplot is a type of visualization that displays the evolution of a series of data points that are connected by straight line segments. In some cases, it is not the most intuitive to read; but it is impressive for storytelling.

More about: Connected scatterplot - Other tutorials: R Matplotlib

            
              <!DOCTYPE html>
              <meta charset="utf-8">
              <!-- Include d3 library -->
              <script src="https://d3js.org/d3.v7.min.js"></script>
              <!-- Create a container to host the chart -->
              <div id="connected_scatterplot_container"></div>
            
          
            
                             
            
          
            
              
              // set the dimensions and margins of the graph
              const margin = {top: 60, right: 60, bottom: 50, left: 50};
              const width = 450 - margin.left - margin.right;
              const height = 350 - margin.top - margin.bottom;

              // append the svg object to the body of the page
              const svg = d3.select("#connected_scatterplot_container")
                .append("svg")
                  .attr("width", "100%")
                  .attr("height", "100%")
                  .attr("viewBox", "0 0 450 350")
                  .attr("preserveAspectRatio", "xMinYMin")
                .append("g")
                  .attr("transform", `translate(${margin.left}, ${margin.top})`);

              // parse the Data
              d3.csv("https://raw.githubusercontent.com/GDS-ODSSS/unhcr-dataviz-platform/master/data/correlation/scatterplot_connected.csv")
              .then(function(data){

              // list of value keys
              const typeKeys = data.columns.slice(1);

              // X scale and Axis
              const f =  d3.format("~s")
              const xScale = d3.scaleLinear()
                .domain(d3.extent(data, d => +d.refugee_number)).nice()
                .range([0, width])
              svg
                .append('g')
                .attr("transform", `translate(0, ${height})`)
                .call(d3.axisBottom(xScale).tickSize(0).ticks(5).tickPadding(8).tickFormat(f))
                .call(d => d.select(".domain").remove());
              
              // Y scale and Axis
              const yScale = d3.scaleLinear()
                  .domain(d3.extent(data, d => +d.idp_number)).nice()
                  .range([height, 0]);
              svg
                .append('g')
                .call(d3.axisLeft(yScale).tickSize(0).ticks(3).tickPadding(4).tickFormat(f))
                .call(d => d.select(".domain").remove());
              
              // bubble size scale
              const zScale = d3.scaleSqrt()
                .domain([600000, 160000000])
                .range([2, 50])

              // color palette
              const color = d3.scaleOrdinal()
                .domain(typeKeys)
                .range(["#EF4A60", "#8EBEFF", "#00B398", "#E1CC0D", "#589BE5", "#18375F", "#0072BC"])
              
              // set horizontal grid line
              const GridLineH = () => d3.axisLeft().scale(yScale);
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineH()
                  .tickSize(-width,0,0)
                  .tickFormat("")
                  .ticks(3)
              ); 

              // set vertical grid line
              const GridLineV = () => d3.axisBottom().scale(xScale);
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineV()
                  .tickSize(height,0,0)
                  .tickFormat("")
                  .ticks(5)
              ); 

              // create a tooltip
              const tooltip = d3.select("body")
                .append("div")
                  .attr("class", "tooltip");

              // tooltip events
              const mouseover = function(d) {
                  tooltip
                      .style("opacity", 1)
                  d3.select(this)
                      .style("stroke", "#EF4A60")
                      .style("opacity", 0.5)
              };
              const mousemove = function(event,d) {
                  const f = d3.format(",");
                  const t = d3.format("Y");

                  tooltip
                  .html("<div style='color: #0072BC'<b>" +t(d.year)+ "</b></div><div><b>Refugees:</b> " +f(d.refugee_number)+ "</div><div><b>IDPs:</b> "+ f(d.idp_number) +"</div>")
                      .style("top", event.pageY - 10 + "px")
                      .style("left", event.pageX + 10 + "px")
              };
              const mouseleave = function(d) {
                  tooltip
                      .style("opacity", 0)
                  d3.select(this)
                      .style("stroke", "none")
                      .style("opacity", 1)
              };
              
              // create graph
              svg.append("g")
                .selectAll("g")
                .data(data)
                .join("circle")
                    .attr("cx", d => xScale(+d.refugee_number))
                    .attr("cy", d => yScale(+d.idp_number))
                    .attr("r", 3)
                    .style("fill", "#0072BC")
                .on("mouseover", mouseover)
                .on("mousemove", mousemove)
                .on("mouseleave", mouseleave)

              const arrowPoints = [[0, 0], [0, 20], [20, 10]];

              svg
              .append('defs')
                .append('marker')
                   .attr('id', 'arrow')
                   .attr('viewBox', [0, 0, 20, 20])
                   .attr('refX', 10)
                   .attr('refY', 10)
                   .attr('markerWidth', 8)
                   .attr('markerHeight', 8)
                   .attr('orient', 'auto-start-reverse')
                .append('path')
                   .attr('d', d3.line()(arrowPoints))
                   .attr('fill', '#0072BC');
              svg
               .append("path")
                .datum(data)
                .attr("fill", "none")
                .attr("stroke", "#0072BC")
                .attr("stroke-width", 1.5)
                .attr("d", d3.line()
                  .x(d => xScale(+d.refugee_number))
                  .y(d => yScale(+d.idp_number))
                )
                .attr('marker-end', 'url(#arrow)')
                .attr('fill', 'none')
                                
              // set title
              svg
                .append("text")
                  .attr("class", "chart-title")
                  .attr("x", -(margin.left)*0.4)
                  .attr("y", -(margin.top)/1.5)
                  .attr("text-anchor", "start")
                .text("Evolution of refugee vs IDP population in Afghanistan | 2001-2021")

              // set X axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", width/1.7)
                  .attr("y", height+margin.bottom*0.6)
                  .attr("text-anchor", "middle")
                .text("Number of refugees (millions)");
          
              // set Y axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", -(margin.left)*0.4)
                  .attr("y", -(margin.top/8))
                  .attr("text-anchor", "start")
                .text("Number of IDPs (millions)")
              
              // set source
              svg
                .append("text")
                  .attr("class", "chart-source")
                  .attr("x", -(margin.left)*0.4)
                  .attr("y", height + margin.bottom*0.8)
                  .attr("text-anchor", "start")
                .text("Source: UNHCR")

              // set copyright
              svg
                .append("text")
                  .attr("class", "copyright")
                  .attr("x", -(margin.left)*0.4)
                  .attr("y", height + margin.bottom*0.95)
                  .attr("text-anchor", "start")
                .text("©UNHCR, The UN Refugee Agency")
              })
            
          

Scatterplot

A scatterplot is a type of visualization using Cartesian Coordinates to display two variables for a set of data. The data are displayed as a collection of dots. The position of each dot on the horizontal and vertical axis indicates the values for an individual data point.

More about: Scatterplot - Other tutorials: R Matplotlib

            
              <!DOCTYPE html>
              <meta charset="utf-8">
              <!-- Include d3 library -->
              <script src="https://d3js.org/d3.v7.min.js"></script>
              <!-- Create a container to host the chart -->
              <div id="scatterplot_container"></div>
            
          
            
                             
            
          
            

              // set the dimensions and margins of the graph
              const margin = {top: 60, right: 50, bottom: 50, left: 50};
              const width = 450 - margin.left - margin.right;
              const height = 350 - margin.top - margin.bottom;

              // append the svg object to the body of the page
              const svg = d3.select("#scatterplot_container")
                .append("svg")
                  .attr("width", "100%")
                  .attr("height", "100%")
                  .attr("viewBox", "0 0 450 350")
                  .attr("preserveAspectRatio", "xMinYMin")
                .append("g")
                  .attr("transform", `translate(${margin.left}, ${margin.top})`);

              // parse the Data
              d3.csv("https://raw.githubusercontent.com/GDS-ODSSS/unhcr-dataviz-platform/master/data/correlation/scatterplot.csv")
              .then(function(data){

              // list of value keys
              const typeKeys = data.columns.slice(1);

              // X scale and Axis
              const f =  d3.format("~s")
              const xScale = d3.scaleLinear()
                .domain(d3.extent(data, d => +d.refugee_number)).nice()
                .range([0, width])
              svg
                .append('g')
                .attr("transform", `translate(0, ${height})`)
                .call(d3.axisBottom(xScale).tickSize(0).ticks(5).tickPadding(8).tickFormat(f))
                .call(d => d.select(".domain").remove());
              
              // Y scale and Axis
              const yScale = d3.scaleLinear()
                  .domain(d3.extent(data, d => +d.idp_number)).nice()
                  .range([height, 0]);
              svg
                .append('g')
                .call(d3.axisLeft(yScale).tickSize(0).ticks(6).tickPadding(4).tickFormat(f))
                .call(d => d.select(".domain").remove());

              // color palette
              const color = d3.scaleOrdinal()
                .domain(typeKeys)
                .range(["#EF4A60", "#8EBEFF", "#00B398", "#E1CC0D", "#589BE5", "#18375F", "#0072BC"])
              
              // set horizontal grid line
              const GridLineH = function() {return d3.axisLeft().scale(yScale)};
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineH()
                  .tickSize(-width,0,0)
                  .tickFormat("")
                  .ticks(6)
              ); 

              // set vertical grid line
              const GridLineV = function() {return d3.axisBottom().scale(xScale)};
              svg
                .append("g")
                  .attr("class", "grid")
                .call(GridLineV()
                  .tickSize(height,0,0)
                  .tickFormat("")
                  .ticks(5)
              ); 

              // create a tooltip
              const tooltip = d3.select("body")
                .append("div")
                  .attr("class", "tooltip");

              // tooltip events
              const mouseover = function(d) {
                  tooltip
                      .style("opacity", 1)
                  d3.select(this)
                      .style("stroke", "#EF4A60")
                      .style("opacity", .5)
              };
              const mousemove = function(event,d) {
                  const f = d3.format(",");
                  tooltip
                  .html("<div><b>Refugees:</b> " +f(d.refugee_number)+ "</div><div><b>IDPs:</b> "+ f(d.idp_number) +"</div>")
                      .style("top", event.pageY - 10 + "px")
                      .style("left", event.pageX + 10 + "px")
              };
              const mouseleave = function(d) {
                  tooltip
                      .style("opacity", 0)
                  d3.select(this)
                      .style("stroke", "none")
                      .style("opacity", 1)
              };
              
              // create graph
              svg.append("g")
                .selectAll("g")
                .data(data)
                .join("circle")
                    .attr("cx", d => xScale(+d.refugee_number))
                    .attr("cy", d => yScale(+d.idp_number))
                    .attr("r", 4)
                    .style("fill", d => color(d.region))
                .on("mouseover", mouseover)
                .on("mousemove", mousemove)
                .on("mouseleave", mouseleave)

              // set data labels  
              svg
                .append("g")
                .attr("font-size", 10)
                .selectAll("text")
                .data(data)
                .join("text")
                  .attr("dy", "0.35em")
                  .attr("x", d => xScale(+d.refugee_number) + 7)
                  .attr("y", d => yScale(+d.idp_number))
                  .text(d => d.region);
                                
              // set title
              svg
                .append("text")
                  .attr("class", "chart-title")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", -(margin.top)/1.5)
                  .attr("text-anchor", "start")
                .text("Comparison of refugee and IDP population by region | 2021")

              // set X axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", width/1.7)
                  .attr("y", height+margin.bottom*0.6)
                  .attr("text-anchor", "middle")
                .text("Number of refugees (millions)");
          
              // set Y axis label
              svg
                .append("text")
                  .attr("class", "chart-label")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", -(margin.top/8))
                  .attr("text-anchor", "start")
                .text("Number of IDPs (millions)")
              
              // set source
              svg
                .append("text")
                  .attr("class", "chart-source")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", height + margin.bottom*0.8)
                  .attr("text-anchor", "start")
                .text("Source: UNHCR")

              // set copyright
              svg
                .append("text")
                  .attr("class", "copyright")
                  .attr("x", -(margin.left)*0.5)
                  .attr("y", height + margin.bottom*0.95)
                  .attr("text-anchor", "start")
                .text("©UNHCR, The UN Refugee Agency")
              })

            
          
Contact us
  • Guidance
  • Chart types
  • Resources
  • Tutorials
  • Product gallery

© UNHCR