title | subtitle | week | type | reading | tasks | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Parallel Computing with R |
Write a parallel for loop |
11 |
Case Study |
|
|
- Parallel Computing with the R Language in a Supercomputing Environment
- CRAN Task View High Performance and Parallel Computing with R
- Download spatial data from the U.S. Census
- Write a parallel
foreach()
loop to generate a point representing each person in each census polygon (block/tract) - Set the output of the
foreach()
funtion to return a spatial (sf
) object - Make a 'dot map' of the racial distribution in Buffalo, NY.
The census data do not include specific addresses (the finest spatial information is the census block), so it's common to see chloropleths representing the aggregate statistics of the underlying polygon. This is accurate, but not so personal. Folks at the University of Virginia developed a simple yet effective visualization approach, called the 'Racial Dot Map' which conveys a simple idea - one dot equals one person. Here's how it looks for Buffalo, NY.
The idea is really simple. One just randomly generates a point for each person of each racial identity within each polygon.
Can you do it? Can you do it using multiple cores on your computer?
library(tidyverse)
library(spData)
library(sf)
## New Packages
library(mapview) # new package that makes easy leaflet maps
library(foreach)
library(doParallel)
registerDoParallel(4)
getDoParWorkers() # check registered cores
To use the tidycensus package, you will need to load the package and set your Census API key. A key can be obtained from http://api.census.gov/data/key_signup.html. You will only need to do that once (unless you delete your .Renviron file or move to a different computer).
# go to http://api.census.gov/data/key_signup.html and get a key, then run the line below with your key. Don't push your key to github!
library(tidycensus)
census_api_key("YOUR API KEY GOES HERE")
Write an Rmd script that:
- Downloads block-level data on population by race in each census block in Buffalo using
get_dicennial()
function of thetidycensus
package. You can use the following code:
library(tidycensus)
racevars <- c(White = "P005003",
Black = "P005004",
Asian = "P005006",
Hispanic = "P004003")
options(tigris_use_cache = TRUE)
erie <- get_decennial(geography = "block", variables = racevars,
state = "NY", county = "Erie County", geometry = TRUE,
summary_var = "P001001", cache_table=T)
- Crop the county-level data to
c(xmin=-78.9,xmax=-78.85,ymin=42.888,ymax=42.92)
to reduce the computational burdern. Feel free to enlarge this area if your computer is fast (or you are patient). - Write a foreach loop that does the following steps for each racial group in the
variable
column of theerie
dataset andrbind
s the results (e.g..combine=rbind
) into a singlesf
object. You may want to convert the variable column into a factor and uselevels()
or useunique()
.- filter the the data to include only one race at time
- use
st_sample()
to generate random points for each person that resided within each polygon. If you use a pipe (%>%
), you will have to setsize=.$value
. The.
indicates that the column comes from the dataset that was passed to the function. See here for details on how to use the.
in a pipe. - convert the points from
st_sample()
to spatial features withst_as_sf()
mutate
to add a column namedvariable
that is set to the current racial group (from the foreach loop)
- Use the
mapview()
function in themapview
package to make a leaflet map of the dataset and set thezcol
to the racial identity of each point. You can adjust any of the visualization parameters (such ascex
for size). Read more about mapview here. It's a new and really easy way to make leaflet maps from many types of spatial data.
Your final result should look something like this:
<script type="application/json" data-for="htmlwidget-fbb7869f353c03265e98">{"x":{"options":{"minZoom":1,"maxZoom":52,"crs":{"crsClass":"L.CRS.EPSG3857","code":null,"proj4def":null,"projectedBounds":null,"options":{}},"preferCanvas":true,"bounceAtZoomLimits":false,"maxBounds":[[[-90,-370]],[[90,370]]]},"calls":[{"method":"addProviderTiles","args":["CartoDB.Positron","CartoDB.Positron","CartoDB.Positron",{"errorTileUrl":"","noWrap":false,"detectRetina":false,"pane":"tilePane"}]},{"method":"addProviderTiles","args":["CartoDB.DarkMatter","CartoDB.DarkMatter","CartoDB.DarkMatter",{"errorTileUrl":"","noWrap":false,"detectRetina":false,"pane":"tilePane"}]},{"method":"addProviderTiles","args":["OpenStreetMap","OpenStreetMap","OpenStreetMap",{"errorTileUrl":"","noWrap":false,"detectRetina":false,"pane":"tilePane"}]},{"method":"addProviderTiles","args":["Esri.WorldImagery","Esri.WorldImagery","Esri.WorldImagery",{"errorTileUrl":"","noWrap":false,"detectRetina":false,"pane":"tilePane"}]},{"method":"addProviderTiles","args":["OpenTopoMap","OpenTopoMap","OpenTopoMap",{"errorTileUrl":"","noWrap":false,"detectRetina":false,"pane":"tilePane"}]},{"method":"addFlatGeoBuf","args":["buffalo_dots-variable","buffalo_dots - variable",null,true,"variable",{"radius":1,"stroke":true,"color":"#333333","weight":0,"opacity":0.9,"fill":true,"fillColor":null,"fillOpacity":0.6},{"className":""},"mapview-popup",{"radius":{"to":[3,15],"from":[3,15]},"weight":{"to":[1,10],"from":[1,10]},"opacity":{"to":[0,1],"from":[0,1]},"fillOpacity":{"to":[0,1],"from":[0,1]}}]},{"method":"addScaleBar","args":[{"maxWidth":100,"metric":true,"imperial":true,"updateWhenIdle":true,"position":"bottomleft"}]},{"method":"addHomeButton","args":[-78.8999994590973,42.8880000584939,-78.8500004756664,42.9199997832221,"buffalo_dots - variable","Zoom to buffalo_dots - variable"," buffalo_dots - variable <\/strong>","bottomright"]},{"method":"addLayersControl","args":[["CartoDB.Positron","CartoDB.DarkMatter","OpenStreetMap","Esri.WorldImagery","OpenTopoMap"],"buffalo_dots - variable",{"collapsed":true,"autoZIndex":true,"position":"topleft"}]},{"method":"addLegend","args":[{"colors":["#4B0055","#007094","#00BE7D","#FDE333"],"labels":["Asian","Black","Hispanic","White"],"na_color":null,"na_label":"NA","opacity":1,"position":"topright","type":"factor","title":"buffalo_dots - variable","extra":null,"layerId":null,"className":"info legend","group":"buffalo_dots - variable"}]}],"fitBounds":[42.8880000584939,-78.8999994590973,42.9199997832221,-78.8500004756664,[]]},"evals":[],"jsHooks":{"render":[{"code":"function(el, x, data) {\n return (\n function(el, x, data) {\n // get the leaflet map\n var map = this; //HTMLWidgets.find('#' + el.id);\n // we need a new div element because we have to handle\n // the mouseover output separately\n // debugger;\n function addElement () {\n // generate new div Element\n var newDiv = $(document.createElement('div'));\n // append at end of leaflet htmlwidget container\n $(el).append(newDiv);\n //provide ID and style\n newDiv.addClass('lnlt');\n newDiv.css({\n 'position': 'relative',\n 'bottomleft': '0px',\n 'background-color': 'rgba(255, 255, 255, 0.7)',\n 'box-shadow': '0 0 2px #bbb',\n 'background-clip': 'padding-box',\n 'margin': '0',\n 'padding-left': '5px',\n 'color': '#333',\n 'font': '9px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif',\n 'z-index': '700',\n });\n return newDiv;\n }\n\n\n // check for already existing lnlt class to not duplicate\n var lnlt = $(el).find('.lnlt');\n\n if(!lnlt.length) {\n lnlt = addElement();\n\n // grab the special div we generated in the beginning\n // and put the mousmove output there\n\n map.on('mousemove', function (e) {\n if (e.originalEvent.ctrlKey) {\n if (document.querySelector('.lnlt') === null) lnlt = addElement();\n lnlt.text(\n ' lon: ' + (e.latlng.lng).toFixed(5) +\n ' | lat: ' + (e.latlng.lat).toFixed(5) +\n ' | zoom: ' + map.getZoom() +\n ' | x: ' + L.CRS.EPSG3857.project(e.latlng).x.toFixed(0) +\n ' | y: ' + L.CRS.EPSG3857.project(e.latlng).y.toFixed(0) +\n ' | epsg: 3857 ' +\n ' | proj4: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs ');\n } else {\n if (document.querySelector('.lnlt') === null) lnlt = addElement();\n lnlt.text(\n ' lon: ' + (e.latlng.lng).toFixed(5) +\n ' | lat: ' + (e.latlng.lat).toFixed(5) +\n ' | zoom: ' + map.getZoom() + ' ');\n }\n });\n\n // remove the lnlt div when mouse leaves map\n map.on('mouseout', function (e) {\n var strip = document.querySelector('.lnlt');\n if( strip !==null) strip.remove();\n });\n\n };\n\n //$(el).keypress(67, function(e) {\n map.on('preclick', function(e) {\n if (e.originalEvent.ctrlKey) {\n if (document.querySelector('.lnlt') === null) lnlt = addElement();\n lnlt.text(\n ' lon: ' + (e.latlng.lng).toFixed(5) +\n ' | lat: ' + (e.latlng.lat).toFixed(5) +\n ' | zoom: ' + map.getZoom() + ' ');\n var txt = document.querySelector('.lnlt').textContent;\n console.log(txt);\n //txt.innerText.focus();\n //txt.select();\n setClipboardText('\"' + txt + '\"');\n }\n });\n\n }\n ).call(this.getMap(), el, x, data);\n}","data":null},{"code":"function(el, x, data) {\n return (function(el,x,data){\n var map = this;\n\n map.on('keypress', function(e) {\n console.log(e.originalEvent.code);\n var key = e.originalEvent.code;\n if (key === 'KeyE') {\n var bb = this.getBounds();\n var txt = JSON.stringify(bb);\n console.log(txt);\n\n setClipboardText('\\'' + txt + '\\'');\n }\n })\n }).call(this.getMap(), el, x, data);\n}","data":null}]}}</script>Update the map to include:
- Other racial groups
- Adjust colors to match the original
- Summarize the data in different ways (e.g. plot the polygon data, calculate indices, etc.)