Skip to content

Instantly share code, notes, and snippets.

@carlosfelipetorres
Last active September 9, 2016 22:18
Show Gist options
  • Save carlosfelipetorres/9eea8d99357e9f7e02208dd63a68358c to your computer and use it in GitHub Desktop.
Save carlosfelipetorres/9eea8d99357e9f7e02208dd63a68358c to your computer and use it in GitHub Desktop.
Tarea 4 Germany
license: mit

Built with blockbuilder.org

What, why, and how analysis

What?

  • Dataset Type: Table (A bar chart)
  • Data Types: Items & Attributes
    1. Measuring indexes (Categorical type)
    2. Values of index (Ordered, quantitative, sequential)

    Why?

  • Actions:
    1. Analyze
      1. Consume → Present
      Search
      1. Lookup → Target & Location known
      Query
      1. Identify
  • Targets:
    1. All data
      1. Outliers
      Attributes
      1. Distribution

    How?

  • Encode
    1. Arrange
      1. Express & Align
      Map
      1. Color →Saturation
      2. Size
  • Manipulate
    1. Select


    Tasks

    Present the measurement indexes from the dataset obtained from the EPI.

    Lookup for interesting data knowing the target and location of the information.

    Identify outliers in the data.

    Identify the distribution that forms the data.


    Decisions made:

    The main task of the proposed visualization was to compare the different indexes of the country, and view the difference between them. For that reason, we choose the bar chart, because it helps the user to understand and compare effectively the values of the indicators. We made an ordering functionality to show a more accurately comparison, but the user also has the option for viewing the data as it is. In the case when the data is not ordered, we decide to put a new channel that enforce the concept of comparison, that’s why we set the opacity of the bars according to the value of the data.

    The channels used in the visualization were the length of the bars and the color (saturation/opacity), and the bars were the marks. Talking about the chart characteristics, we put the value of the index in the Y axis, as is a continuous variable. And in the X axis it's set the measurement indexes of the country, a categorical, unordered variable.

    The diagram interaction is seen in the moment the user passes the mouse over the bars (hover). In this interaction the user can "ask" the visualization and the visualization will tell him the exact value of that index. In addition, the user can interact with the visualization making click over the checkbox to sort or not the visualization. For maintaining the best accuracy with the data, the tooltip was implemented as the names of the measurement indexes were kind of large and the user really can't tell what is the exact value of the bars just with a simple glance. Also the names of the indexes were rotated for better visualization.


    Effectiveness & Expressiveness

    Our visualization has a good expressiveness because we are the information of the dataset correctly and not showing another data that doesn’t correspond to the data we have. In addition, we have the ordered data in a way that represents correctly the dataset and matches the channels and the data characteristics.

    The effectiveness is achieved correctly because we are using the highest ranked channels, as the length and the saturation or opacity to encode the attributes of the dataset. We user the bars length as it is the best ranked channel, and after that we set the opacity of the bars because the saturation is one of the best ranked channels also.

    [
    {
    "index" : "EPI Score",
    "value" : 69.53
    }, {
    "index" : "10-Year Percent Change",
    "value" : 2.67
    }, {
    "index" : "Environmental Health",
    "value" : 76.25
    }, {
    "index" : "Ecosystem Vitality",
    "value" : 65.05
    }, {
    "index" : "EH - Health Impacts",
    "value" : 98.71
    }, {
    "index" : "EH - Air Quality",
    "value" : 70.74
    }, {
    "index" : "EH -Water and Sanitation",
    "value" : 59.31
    }, {
    "index" : "EV - Water Resources",
    "value" : 60.73
    }, {
    "index" : "EV - Agriculture",
    "value" : 64.98
    }, {
    "index" : "EV - Forests",
    "value" : 58.94
    }, {
    "index" : "EV - Fisheries",
    "value" : 26.52
    }, {
    "index" : "EV- Biodiversity and Habitat",
    "value" : 91.76
    }, {
    "index" : "EV - Climate and Energy",
    "value" : 60.53
    }, {
    "index" : "Child Mortality",
    "value" : 98.71
    }, {
    "index" : "Household Air Quality",
    "value" : 95
    }, {
    "index" : "Air Pollution - Average Exposure to PM2.5",
    "value" : 71.89
    }, {
    "index" : "Air Pollution - Average PM2.5 Exceedance",
    "value" : 45.32
    }, {
    "index" : "Access to Sanitation",
    "value" : 44.95
    }, {
    "index" : "Access to Drinking Water",
    "value" : 73.66
    }, {
    "index" : "Wastewater Treatment",
    "value" : 60.73
    }, {
    "index" : "Agricultural Subsidies",
    "value" : 33.95
    }, {
    "index" : "Pesticide Regulation",
    "value" : 96
    }, {
    "index" : "Change in Forest Cover ",
    "value" : 58.94
    }, {
    "index" : "Fish Stocks",
    "value" : 16.67
    }, {
    "index" : "Coastal Shelf Fishing Pressure",
    "value" : 36.38
    }, {
    "index" : "Terrestrial Protected Areas (National Biome Weights)",
    "value" : 100
    }, {
    "index" : "Terrestrial Protected Areas (Global Biome Weights)",
    "value" : 100
    }, {
    "index" : "Marine Protected Areas",
    "value" : 75.28
    }, {
    "index" : "Critical Habitat Protection",
    "value" : 0
    }, {
    "index" : "Trend in Carbon Intensity",
    "value" : 77.14
    }, {
    "index" : "Change of Trend in Carbon Intensity",
    "value" : 46.79
    }, {
    "index" : "Trend in CO2 Emissions per KwH",
    "value" : 48.99
    }, {
    "index" : "Access to Electricity",
    "value" : 100
    }
    ];
    index value
    Environmental Health 92.83
    Ecosystem Vitality 72.23
    EH - Health Impacts 100
    EH - Air Quality 78.5
    EH -Water and Sanitation 100
    EV - Water Resources 95.18
    EV - Agriculture 65.31
    EV - Forests 31.35
    EV - Fisheries 13.4
    EV- Biodiversity and Habitat 100
    EV - Climate and Energy 62.77
    Child Mortality 100
    Household Air Quality 95
    Air Pollution - Average Exposure to PM2.5 82.24
    Air Pollution - Average PM2.5 Exceedance 58.27
    Access to Sanitation 100
    Access to Drinking Water 100
    Wastewater Treatment 95.18
    Agricultural Subsidies 38.62
    Pesticide Regulation 92
    Change in Forest Cover 31.35
    Fish Stocks 8.33
    Coastal Shelf Fishing Pressure 18.46
    Terrestrial Protected Areas (National Biome Weights) 100
    Terrestrial Protected Areas (Global Biome Weights) 100
    Marine Protected Areas 100
    Critical Habitat Protection -1
    Trend in Carbon Intensity 65.24
    Change of Trend in Carbon Intensity 27.82
    Trend in CO2 Emissions per KwH 58.26
    Access to Electricity 100
    <!DOCTYPE html>
    <head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
    <script type="text/javascript" src="jquery.tipsy.js"></script>
    <link href="tipsy.css" rel="stylesheet" type="text/css" />
    <style>
    .bar:hover {
    fill: brown;
    }
    body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    position: relative;
    width: 960px;
    }
    .axis text {
    font: 10px sans-serif;
    }
    .axis path,
    .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
    }
    .bar {
    fill: steelblue;
    fill-opacity: .5;
    }
    .x.axis path {
    display: none;
    }
    label {
    position: absolute;
    top: 10px;
    right: 10px;
    }
    </style>
    <h1>Visualization 4</h1>
    <h2>Nychol Bazurto G&oacute;mez and Carlos Torres</h2>
    <h4>Country: Germany</h4>
    <h4>EPI Score: 80.47</h4>
    <h4>10-Year Percent Change: 1.89%</h4>
    <label><input type="checkbox"> Sort values</label>
    </head>
    <body>
    <script>
    // Feel free to change or delete any of the code you see in this editor!
    var margin = {margin:60, top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    var EPI_Score = 80.47;
    var Ten_Year_Percent_Change = 1.89;
    var x = d3.scaleBand()
    .range([0, width]);
    var y = d3.scaleLinear()
    .range([height, 0]);
    var xAxis = d3.axisBottom(x)
    .scale(x);
    var yAxis = d3.axisLeft(y)
    .scale(y);
    var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var tooltip = d3.select("body")
    .append("label")
    .style("position", "absolute")
    .style("z-index", "10")
    .style("visibility", "hidden");
    d3.tsv("data.tsv", function(error, data) {
    data.forEach(function(d) {
    d.value = +d.value;
    });
    x.domain(data.map(function(d) { return d.index; }));
    y.domain([0, d3.max(data, function(d) { return d.value; })]);
    svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .selectAll("text")
    .attr("y", 0)
    .attr("x", 70)
    .attr("dy", "-0.2296em")
    .attr("transform", "rotate(-90)")
    .style("font-weight","bold");
    svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Value");
    svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("class", "bar")
    .attr("x", function(d) { return x(d.index); })
    .attr('width', x.bandwidth())
    .attr("y", function(d) { return y(d.value); })
    .attr("height", function(d) { return height - y(d.value); })
    .style("opacity", function(d){
    if(d.value > 0 && d.value <= 40){
    return 0.3;
    }else if(d.value > 40 && d.value <= 80){
    return 0.5;
    }else if(d.value > 80 && d.value <= 90){
    return 0.7;
    }else if(d.value > 90 && d.value <= 100){
    return 0.9;
    }
    });
    d3.select("input").on("change", change);
    var sortTimeout = setTimeout(function() {
    d3.select("input").property("checked", true).each(change);
    }, 2000);
    function change() {
    clearTimeout(sortTimeout);
    // Copy-on-write since tweens are evaluated after a delay.
    var x0 = x.domain(data.sort(this.checked
    ? function(a, b) { return b.value - a.value; }
    : function(a, b) { return d3.ascending(a.index, b.index); })
    .map(function(d) { return d.index; }))
    .copy();
    svg.selectAll(".bar")
    .sort(function(a, b) { return x0(a.index) - x0(b.index); });
    var transition = svg.transition().duration(750),
    delay = function(d, i) { return i * 50; };
    transition.selectAll(".bar")
    .delay(delay)
    .attr("x", function(d) { return x0(d.index); });
    transition.select(".x.axis")
    .call(xAxis)
    .selectAll("g")
    .delay(delay);
    }
    $('rect').tipsy({
    gravity: 'w',
    html: true,
    title: function() {
    var d = this.__data__;
    return +d.value +" "+d.index;
    }
    });
    });
    </script>
    // tipsy, facebook style tooltips for jquery
    // version 1.0.0a
    // (c) 2008-2010 jason frame [jason@onehackoranother.com]
    // released under the MIT license
    (function($) {
    function maybeCall(thing, ctx) {
    return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
    }
    function Tipsy(element, options) {
    this.$element = $(element);
    this.options = options;
    this.enabled = true;
    this.fixTitle();
    }
    Tipsy.prototype = {
    show: function() {
    var title = this.getTitle();
    if (title && this.enabled) {
    var $tip = this.tip();
    $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
    $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
    $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
    var pos = $.extend({}, this.$element.offset(), {
    width: this.$element[0].offsetWidth || 0,
    height: this.$element[0].offsetHeight || 0
    });
    if (typeof this.$element[0].nearestViewportElement == 'object') {
    // SVG
    var el = this.$element[0];
    var rect = el.getBoundingClientRect();
    pos.width = rect.width;
    pos.height = rect.height;
    }
    var actualWidth = $tip[0].offsetWidth,
    actualHeight = $tip[0].offsetHeight,
    gravity = maybeCall(this.options.gravity, this.$element[0]);
    var tp;
    switch (gravity.charAt(0)) {
    case 'n':
    tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
    break;
    case 's':
    tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
    break;
    case 'e':
    tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
    break;
    case 'w':
    tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
    break;
    }
    if (gravity.length == 2) {
    if (gravity.charAt(1) == 'w') {
    tp.left = pos.left + pos.width / 2 - 15;
    } else {
    tp.left = pos.left + pos.width / 2 - actualWidth + 15;
    }
    }
    $tip.css(tp).addClass('tipsy-' + gravity);
    $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
    if (this.options.className) {
    $tip.addClass(maybeCall(this.options.className, this.$element[0]));
    }
    if (this.options.fade) {
    $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
    } else {
    $tip.css({visibility: 'visible', opacity: this.options.opacity});
    }
    var t = this;
    var set_hovered = function(set_hover){
    return function(){
    t.$tip.stop();
    t.tipHovered = set_hover;
    if (!set_hover){
    if (t.options.delayOut === 0) {
    t.hide();
    } else {
    setTimeout(function() {
    if (t.hoverState == 'out') t.hide(); }, t.options.delayOut);
    }
    }
    };
    };
    $tip.hover(set_hovered(true), set_hovered(false));
    }
    },
    hide: function() {
    if (this.options.fade) {
    this.tip().stop().fadeOut(function() { $(this).remove(); });
    } else {
    this.tip().remove();
    }
    },
    fixTitle: function() {
    var $e = this.$element;
    if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
    $e.attr('original-title', $e.attr('title') || '').removeAttr('title');
    }
    if (typeof $e.context.nearestViewportElement == 'object'){
    if ($e.children('title').length){
    $e.append('<original-title>' + ($e.children('title').text() || '') + '</original-title>')
    .children('title').remove();
    }
    }
    },
    getTitle: function() {
    var title, $e = this.$element, o = this.options;
    this.fixTitle();
    if (typeof o.title == 'string') {
    var title_name = o.title == 'title' ? 'original-title' : o.title;
    if ($e.children(title_name).length){
    title = $e.children(title_name).html();
    } else{
    title = $e.attr(title_name);
    }
    } else if (typeof o.title == 'function') {
    title = o.title.call($e[0]);
    }
    title = ('' + title).replace(/(^\s*|\s*$)/, "");
    return title || o.fallback;
    },
    tip: function() {
    if (!this.$tip) {
    this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
    }
    return this.$tip;
    },
    validate: function() {
    if (!this.$element[0].parentNode) {
    this.hide();
    this.$element = null;
    this.options = null;
    }
    },
    enable: function() { this.enabled = true; },
    disable: function() { this.enabled = false; },
    toggleEnabled: function() { this.enabled = !this.enabled; }
    };
    $.fn.tipsy = function(options) {
    if (options === true) {
    return this.data('tipsy');
    } else if (typeof options == 'string') {
    var tipsy = this.data('tipsy');
    if (tipsy) tipsy[options]();
    return this;
    }
    options = $.extend({}, $.fn.tipsy.defaults, options);
    if (options.hoverlock && options.delayOut === 0) {
    options.delayOut = 100;
    }
    function get(ele) {
    var tipsy = $.data(ele, 'tipsy');
    if (!tipsy) {
    tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
    $.data(ele, 'tipsy', tipsy);
    }
    return tipsy;
    }
    function enter() {
    var tipsy = get(this);
    tipsy.hoverState = 'in';
    if (options.delayIn === 0) {
    tipsy.show();
    } else {
    tipsy.fixTitle();
    setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
    }
    }
    function leave() {
    var tipsy = get(this);
    tipsy.hoverState = 'out';
    if (options.delayOut === 0) {
    tipsy.hide();
    } else {
    var to = function() {
    if (!tipsy.tipHovered || !options.hoverlock){
    if (tipsy.hoverState == 'out') tipsy.hide();
    }
    };
    setTimeout(to, options.delayOut);
    }
    }
    if (options.trigger != 'manual') {
    var binder = options.live ? 'live' : 'bind',
    eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
    eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
    this[binder](eventIn, enter)[binder](eventOut, leave);
    }
    return this;
    };
    $.fn.tipsy.defaults = {
    className: null,
    delayIn: 0,
    delayOut: 0,
    fade: false,
    fallback: '',
    gravity: 'n',
    html: false,
    live: false,
    offset: 0,
    opacity: 0.8,
    title: 'title',
    trigger: 'hover',
    hoverlock: false
    };
    // Overwrite this method to provide options on a per-element basis.
    // For example, you could store the gravity in a 'tipsy-gravity' attribute:
    // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
    // (remember - do not modify 'options' in place!)
    $.fn.tipsy.elementOptions = function(ele, options) {
    return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
    };
    $.fn.tipsy.autoNS = function() {
    return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
    };
    $.fn.tipsy.autoWE = function() {
    return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
    };
    /**
    * yields a closure of the supplied parameters, producing a function that takes
    * no arguments and is suitable for use as an autogravity function like so:
    *
    * @param margin (int) - distance from the viewable region edge that an
    * element should be before setting its tooltip's gravity to be away
    * from that edge.
    * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
    * if there are no viewable region edges effecting the tooltip's
    * gravity. It will try to vary from this minimally, for example,
    * if 'sw' is preferred and an element is near the right viewable
    * region edge, but not the top edge, it will set the gravity for
    * that element's tooltip to be 'se', preserving the southern
    * component.
    */
    $.fn.tipsy.autoBounds = function(margin, prefer) {
    return function() {
    var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
    boundTop = $(document).scrollTop() + margin,
    boundLeft = $(document).scrollLeft() + margin,
    $this = $(this);
    if ($this.offset().top < boundTop) dir.ns = 'n';
    if ($this.offset().left < boundLeft) dir.ew = 'w';
    if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
    if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
    return dir.ns + (dir.ew ? dir.ew : '');
    };
    };
    })(jQuery);
    .tipsy { font-weight: bold; font-family: Helvetica, Arial, Sans-Serif; font-size: 16px; position: absolute; padding: 5px; z-index: 100000; }
    .tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
    /* Rounded corners */
    .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
    /* Uncomment for shadow */
    .tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }
    .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
    /* Rules to colour arrows */
    .tipsy-arrow-n { border-bottom-color: #000; }
    .tipsy-arrow-s { border-top-color: #000; }
    .tipsy-arrow-e { border-left-color: #000; }
    .tipsy-arrow-w { border-right-color: #000; }
    .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
    .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
    .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
    .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
    .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
    .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
    .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
    .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment