Rasmus Pank Roulund

The Dot-Spot task for experiment

This page briefly describes the Dot-Spot task used in Asset Price Dynamics and Endogenous Trader Overconfidence (2019) by Steffen Ahrens, Ciril Bosch-Rosa, and Rasmus Pank Roulund, and how to use the code. We also provide a simple otree example and the full otree source code to our experiment.

Dot-Spot example (see also demo)

In our experiment, we show participants Dot-Spot matrices, each for six seconds, and then ask them to answer the following questions,

  1. Please give us your best estimate for the number of red dots.
  2. How far away do you think your estimate is from the true answer?

Click here to reveal correct number dots

This example has red dots and blue dots.

Overview of code to generate Dot-Spots

We use something like the following d3.v3.js javascript function to display Dot-Spot tasks in the browser (e.g. for experiments using otree).

  var add_dotspot = function(_data, _anchor = "dotspot-div"){
      var radius = 3.5;      // dots size
      var stroke_width = .5; // stroke on dots
      var size = 500;        // canvas size
      var margin = 20;       // canvas margin
      var chart = d3.select('#' + _anchor)
                    .classed("svg-container", true)
                    .append('svg')
                    .attr("id", "dotspot")
                    .attr('width', size + margin)
                    .attr('height', size + margin)
                    .attr("preserveAspectRatio", "xMinYMin meet")
                    .attr("viewBox", [0, 0, size, size].join(" "))
                    .attr("style", ("border: 1px solid black;"
                                    + "background-color: white;"));
      var x = d3.scale.linear()
                .domain([d3.min(_data.map(d => d.x)),
                         d3.max(_data.map(d => d.x))])
                .range([ margin, size - margin]);
      var y = d3.scale.linear()
                .domain([d3.min(_data.map(d => d.y)),
                         d3.max(_data.map(d => d.y))])
                .range([ size - margin, margin]);
      var points_layer = chart.append("g");
      points_layer.selectAll(".dot")
                  .data(_data)
                  .enter().append("circle")
                  .attr("class", "dot")
                  .attr("r", radius)
                  .attr("cx", d => x(d.x))
                  .attr("cy", d => y(d.y))
                  .style("fill", d => d.color)
                  .style("stroke-width", String(stroke_width))
                  .style("stroke", d => d3.rgb(d.color ).darker(1.3))
}

It takes two arguments, _data and _anchor. _data is a list of objects with keys x, y, and color, e.g. something like this,

_data = [{"x": 0, "y": 0, "color": "blue"},
         ...,
         {"x": 20, "y": 20, "color": "red"}]

_anchor is a html id that the Dot-Spot canvas is written in.

Data can be generated in a number of ways.

Here is a the javascript code used on this page:

var generate_dotspot_data = function(dots=20,
                                     number_of_reds=200,
                                     epsilon=5)  {
    var eps = d3.shuffle(d3.range(-epsilon, epsilon)).pop();
    var r = d3.range(number_of_reds + eps).map(x => "red");
    var b = d3.range(dots*dots - r.length).map(x => "blue");
    var colors = d3.shuffle([].concat(r, b));
    return ([].concat(...d3.range(dots).map(
        x => d3.range(dots).map(
            y => ({"x": x, "y": y, "color": colors.pop()})))));
}

Here is a Python example similar to the one we used in our otree program:

def generate_dotspot_data(dots=20, number_of_reds=None, epsilon = None):
    """Generate dotspot dataset.

    Parameters
    ----------
    dots:
        number of dots per axis. Total number of dots is dots*dots
    number_of_reds:
        number of red dots in data
    epsilon:
        if not None, an integer between [-epsilon;epsilon] is
        added to number_of_reds

    Returns
    -------
    List of dictonaries with the keys x, y and color.
    """
    import random
    from itertools import product
    total = dots*dots
    reds = total // 2 if number_of_reds is None else number_of_reds
    eps = random.randint(-epsilon, epsilon) if epsilon else 0
    reds += eps
    colors = ["red"]*reds + ["blue"]*(total - reds)
    random.shuffle(colors)
    base = product(range(dots), range(dots))
    return [{"x": x, "y": y, "color": col}
            for (x, y), col in zip(base, colors)]

Simple otree Dot-Spot example

The source code to a simplified otree is provided here. You can also try the demo.

Full otree code for our experiment

I will provide the entire otree code used in our paper in due course (after a final review of code clarity, comments, etc.).