Thursday, February 6, 2014

Compare Regression Results to a Specific Factor Level in R

Including a series of dummy variables in a regression in R is very simple. For example,

ols <- lm(weight ~ Time + Diet,
  data = ChickWeight)
summary(ols)

The above regression automatically includes a dummy variable for all but the first level of the factor of the Diet variable.

Call:
lm(formula = weight ~ Time + Diet, data = ChickWeight)

Residuals:
     Min       1Q   Median       3Q      Max 
-136.851  -17.151   -2.595   15.033  141.816 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  10.9244     3.3607   3.251  0.00122 ** 
Time          8.7505     0.2218  39.451  < 2e-16 ***
Diet2        16.1661     4.0858   3.957 8.56e-05 ***
Diet3        36.4994     4.0858   8.933  < 2e-16 ***
Diet4        30.2335     4.1075   7.361 6.39e-13 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 35.99 on 573 degrees of freedom
Multiple R-squared:  0.7453,  Adjusted R-squared:  0.7435 
F-statistic: 419.2 on 4 and 573 DF,  p-value: < 2.2e-16

This is great, and it's often what you want. But in this case, it's comparing each of the diets to Diet1. In some cases, you might want to compare to a specific diet that isn't the first in the factor list.

How can we choose which dummy to compare to? Fortunately, it's simple to compare to a specific dummy in R. We can just relevel the factor so the dummy we want to compare to is first.

ChickWeight$Diet <- relevel(ChickWeight$Diet,
  ref = 4)

olsRelevel <- lm(weight ~ Time + Diet,
  data = ChickWeight)
summary(olsRelevel)

The "ref" argument allows us to change the reference level of the factor variable. This means that when we perform regression analysis

You can use table or str to find the factor levels, if you don't already know them.

After releveling the factor variable, we can simply perform the same regression again, and this time it will compare the results to the new reference level:

olsRelevel <- lm(weight ~ Time + Diet,
  data = ChickWeight)
summary(olsRelevel)


Call:
lm(formula = weight ~ Time + Diet, data = ChickWeight)

Residuals:
     Min       1Q   Median       3Q      Max 
-136.851  -17.151   -2.595   15.033  141.816 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  41.1578     4.0828  10.081  < 2e-16 ***
Time          8.7505     0.2218  39.451  < 2e-16 ***
Diet1       -30.2335     4.1075  -7.361 6.39e-13 ***
Diet2       -14.0674     4.6665  -3.015  0.00269 ** 
Diet3         6.2660     4.6665   1.343  0.17989    
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 35.99 on 573 degrees of freedom
Multiple R-squared:  0.7453,  Adjusted R-squared:  0.7435 
F-statistic: 419.2 on 4 and 573 DF,  p-value: < 2.2e-16

Now we can choose any factor level as the reference for the series of dummies in the regression analysis. The code is available in a gist.

Reference

Sunday, February 2, 2014

LWIMW3: Trail Magic

I just finished my submission for Look What I Made Weekend 3. Look What I Made Weekend (LWIMW) is a chance for people to create something over the course of 48 hours. The concept is based on Ludum Dare and other game jams, but for LWIMW you don't have to make a game. Instead, you are free to pursue any creative endeavor and show off your results at the end.

NB: The content below is mostly a reprint of my submission at LWIMW.

My project is an interactive website that will be part of the companion site to a book my friend Scott Thigpen is writing.
I didn't quite start from scratch on this project. This image shows the progress I had made before the weekend. You can also view it on the web.

pre-LWIMW

The other images show the current status after the weekend.
I made a lot of progress this weekend. I added GPS routes to the map, added a table of contents, added marker clustering, improved the graphic design (CSS and basemap), and added about 25% of the final content.
post-LWIMW

post-LWIMW

The only essential things I have left to do are adding the rest of the content, working with my friend on palette and graphics, and tweaking some small things.

You can view the current state of the Trail Magic site here.

Thursday, January 30, 2014

Append Layer to overlayMaps in Leaflet

What if we want to create a layer based on geolocation, but have the layer only be added to the map once geolocation occurs? I didn't find this example in any Leaflet tutorials, but it's pretty simple with some basic JavaScript

I'll start with some code that should look familiar from the Leaflet Quick Start Guide, but with each item as its own layer.

  var marker = L.marker([51.5, -0.09]);

  var circle = L.circle([51.508, -0.11], 500, {
      color: 'red',
      fillColor: '#f03',
      fillOpacity: 0.5
  });

  var polygon = L.polygon([
      [51.509, -0.08],
      [51.503, -0.06],
      [51.51, -0.047]
  ]);

  var map = L.map('map', {
    center: [51.505, -0.09],
    zoom: 13,
    layers: [marker, circle, polygon]
  });

  var overlayMaps = {
    "Marker": marker,
    "Circle": circle,
    "Polygon": polygon
  };
  L.tileLayer('http://tile.cloudmade.com/[API-KEY]/29889/256/{z}/{x}/{y}.png', {
    attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
    maxZoom: 18
  }).addTo(map);

Next let's add the code that will display the three original layers if geolocation does not occur. In this case, we do not want the geolocated layer to show.


  function onLocationError(e) {
    alert(e.message);

    L.control.layers(null, overlayMaps).addTo(map);
  }

  map.on('locationerror', onLocationError);

You can see in the below screenshot that only the three layers are displayed.

With Geolocation Off
Finally, we need to add code that will display all the layers (including the geolocation layer) if geolocation does occur. We simply need to add the "yourLocation" layer to overlayMaps and then display the layers as normal.


  function onLocationFound(e) {
    yourLocation = L.marker(e.latlng);

    overlayMaps["You"] = yourLocation;

    L.control.layers(null, overlayMaps).addTo(map);
  }

  map.on('locationfound', onLocationFound);

The screenshots below show what happens once geolocation has occurred.

With Geolocation On

Showing Geolocation Layer
 The full code is available in a gist.

References

Thursday, January 23, 2014

Add and Remove Leaflet Circle on Click

Similar to the popup example in the Leaflet tutorial, you might want to allow a user to add a circle centered on the point they click.

After defining your map, you first need to declare the variable you'll be using:

  var clickCircle;

While popups automatically disappear on the next click, circles do not. The following code removes the former circle from the leaflet map before drawing the new circle:

  function onMapClick(e) {
    if (clickCircle != undefined) {
      map.removeLayer(clickCircle);
    };

Next, we want our function to draw the new circle (NB: "1609 * 3" is the radius of the circle. This makes the radius equal 3 miles):

    clickCircle = L.circle(e.latlng, 1609 * 3, {
      color: '#f07300',
      fillOpacity: 0,
      opacity: 0.5
    }).addTo(map);
  }


And finally, we need to add an event listener so that the function will be run when a user clicks on the map:


  map.on('click', onMapClick);


The full code is available in a gist.

References



Thursday, January 16, 2014

Embed Audio in Leaflet Pop-up

Here's how to embed audio in a leaflet map. This example will also show you how to embed most other HTML in a leaflet map, or how to embed audio in an HTML file.

In the html file:

  function onEachAudio(feature, layer) {
    layer.bindPopup(feature.properties.name + "<br>" + feature.properties.html);
  };

  new L.GeoJSON.AJAX("audio.geojson", {
    onEachFeature: onEachAudio,
    pointToLayer: function(feature, latlng) {
      return L.marker(latlng, {icon: audioIcon});
    }
  }).addTo(map);

And here's the geojson format where you'll include your audio HTML and the coordinates where you want to view each file:


{
  "type": "FeatureCollection",
  "features": [
  {
    "type": "Feature",
    "properties": {
      "name": "<a href='http://www.freesound.org/people/genghis%20attenborough/sounds/212798/'>Deep basement</a>",
      "html": "<p><audio width='300' height='32' src='http://www.freesound.org/data/previews/212/212798_205108-lq.mp3' controls='controls'><br />Your browser does not support the audio element.<br /></audio></p>"
    },
    "geometry": {
      "type": "Point",
      "coordinates": [-100,34]
    }
  },{
    "type": "Feature",
    "properties": {
      "name": "<a href='http://www.freesound.org/people/John%20Sipos/sounds/125696/'>Atlantis docks then lands.</a>",
      "html": "<p><audio width='300' height='32' src='http://www.freesound.org/data/previews/125/125696_593024-lq.mp3' controls='controls'><br />Your browser does not support the audio element.<br /></audio></p>"
    },
    "geometry": {
      "type": "Point",
      "coordinates": [-84,40]
    }
  }
  ]
}

Just substitute in the address of your audio files for the sample files above in the "html" property of the geojson features.
Example Leaflet Map with Audio


The full code is available in a gist.

Thursday, January 9, 2014

Import JSON Data from an External File in Leaflet

If your JSON or GeoJSON data is long, you might want to store it in a separate file to make your code more readable or to reduce repetition and allow you to access the same data file from multiple pages. You can do that with jQuery.

First, load jQuery 1.10.2 or the newest version of jQuery (2.0 onward does not support Internet Explorer 6, 7, or 8) in the header:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

Next, in your leaflet script at the bottom of the body, you'll need to add all your other code and your basemap, like normal, for example:

  var map = L.map('map').setView([38.0740, -55], 3);

  function onEachFeature(feature, layer) {
    layer.bindPopup(feature.properties.City + ", " + feature.properties.State + ", " + feature.properties.Country);
  }

Then, you can just use jQuery to get the JSON or GeoJSON file. A simple version of this would be:

  $.getJSON("cities.geoJSON", function (cities) {
    L.geoJson(cities).addTo(map);
  })

But you can also add more options to your L.geoJson code, just like you would if using data stored in the HTML file. For example:


  $.getJSON("cities.geoJSON", function (cities) {
    L.geoJson(cities, {
      onEachFeature: onEachFeature,
      pointToLayer: function (feature, latlng) {
        switch (feature.properties.Remember) {
          case '1': return L.marker(latlng, {icon: visitedIcon});
          case '?': return L.marker(latlng, {icon: uncertainIcon});
          case '0': return L.marker(latlng, {icon: uncertainIcon});
        }
      }
    }).addTo(map);
  })

So just a couple lines of jQuery allow you to store your JSON or GeoJSON data in a separate file.

The example code is available in a gist and the example is viewable here.

Further Reading

Thursday, December 19, 2013

Make All Polygons the Same Shade in Leaflet

The Quick Start tutorial shows us how to change the color of a polygon:

var circle = L.circle([51.508, -0.11], 500, {
    color: 'red',
    fillColor: '#f03',
    fillOpacity: 0.5
}).addTo(map);

But what if we want to change the color and style of all (or a set of the) polygons?

First we can define the style:

var defaultStyle = {
  color: 'green',
  fillOpacity: 0.2
};

And then we can just add that style to our polygons:

var polygon = L.polygon([
    [51.509, -0.08],
    [51.503, -0.06],
    [51.51, -0.047]
]).setStyle(defaultStyle).addTo(map);

var circle = L.circle([51.508, -0.11], 500).setStyle(defaultStyle).addTo(map);


The full code (based on the Quick Start tutorial) is available in a gist.