diff --git a/.Rbuildignore b/.Rbuildignore index 0f03e30..f36161f 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,4 +7,5 @@ ^.*README_files/.*$ ^tmp$ ^tmp/$ -^tmp/.*$ \ No newline at end of file +^tmp/.*$ +^cran-comments\.md$ diff --git a/DESCRIPTION b/DESCRIPTION index 2cf3a1d..8ba34e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,9 +1,9 @@ Package: statebins Type: Package -Title: statebins - U.S. State Cartogram Heatmaps in R; an alternative to - choropleth maps for USA States -Version: 1.2.1 -Date: 2015-01-30 +Title: U.S. State Cartogram Heatmaps in R; an Alternative to + Choropleth Maps for USA States +Version: 1.2.2 +Date: 2015-12-21 Author: Bob Rudis (@hrbrmstr) Maintainer: Bob Rudis Description: statebins is an alternative to choropleth maps for USA States and @@ -25,3 +25,4 @@ Depends: gridExtra, scales, RColorBrewer +RoxygenNote: 5.0.1 diff --git a/LICENSE b/LICENSE index f802fca..78f441a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,2 @@ -YEAR: 2014 +YEAR: 2015 COPYRIGHT HOLDER: Bob Rudis \ No newline at end of file diff --git a/NAMESPACE b/NAMESPACE index 90f59a8..a7801e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,41 @@ -# Generated by roxygen2 (4.1.0): do not edit by hand +# Generated by roxygen2: do not edit by hand export(statebins) export(statebins_continuous) export(statebins_manual) import(RColorBrewer) -import(ggplot2) -import(grid) import(gridExtra) -import(scales) +importFrom(ggplot2,aes) +importFrom(ggplot2,aes_string) +importFrom(ggplot2,coord_equal) +importFrom(ggplot2,element_blank) +importFrom(ggplot2,element_rect) +importFrom(ggplot2,element_text) +importFrom(ggplot2,geom_point) +importFrom(ggplot2,geom_text) +importFrom(ggplot2,geom_tile) +importFrom(ggplot2,ggplot) +importFrom(ggplot2,ggplotGrob) +importFrom(ggplot2,ggtitle) +importFrom(ggplot2,guide_legend) +importFrom(ggplot2,guides) +importFrom(ggplot2,labs) +importFrom(ggplot2,scale_color_manual) +importFrom(ggplot2,scale_fill_brewer) +importFrom(ggplot2,scale_fill_gradientn) +importFrom(ggplot2,scale_fill_manual) +importFrom(ggplot2,scale_x_continuous) +importFrom(ggplot2,scale_y_continuous) +importFrom(ggplot2,scale_y_reverse) +importFrom(ggplot2,theme) +importFrom(ggplot2,theme_bw) +importFrom(grid,arrow) +importFrom(grid,gpar) +importFrom(grid,grid.draw) +importFrom(grid,grid.newpage) +importFrom(grid,textGrob) +importFrom(grid,unit) +importFrom(grid,unit.c) +importFrom(grid,unit.pmax) +importFrom(grid,unit.pmin) +importFrom(scales,alpha) diff --git a/R/statebins-package.R b/R/statebins-package.R index 44e028d..97236a9 100644 --- a/R/statebins-package.R +++ b/R/statebins-package.R @@ -1,6 +1,16 @@ #' statebins is an alternative to choropleth maps for US States +#' #' @name statebins-package #' @docType package #' @author Bob Rudis (@@hrbrmstr) -#' @import ggplot2 scales grid gridExtra RColorBrewer +#' @import gridExtra RColorBrewer +#' @importFrom scales alpha +#' @importFrom ggplot2 ggplot geom_tile scale_fill_manual guides geom_tile ggplotGrob +#' @importFrom ggplot2 geom_point geom_text scale_color_manual guides theme labs +#' @importFrom ggplot2 scale_x_continuous scale_y_continuous coord_equal theme_bw +#' @importFrom ggplot2 aes guide_legend element_rect element_blank element_text +#' @importFrom ggplot2 aes_string scale_y_reverse scale_fill_gradientn +#' @importFrom ggplot2 scale_fill_brewer ggtitle +#' @importFrom grid arrow unit grid.newpage grid.draw unit.c unit.pmax unit.pmin +#' @importFrom grid textGrob gpar NULL diff --git a/R/statebins.R b/R/statebins.R index b7d3bee..401718b 100644 --- a/R/statebins.R +++ b/R/statebins.R @@ -44,38 +44,43 @@ invert <- function(hexColor, darkColor="black", lightColor="white") { #' #' \code{statebins()} creates "statebin" charts in the style of \url{http://bit.ly/statebins} #' -#' This version uses discrete \link{RColorBrewer} scales, binned by the "breaks" parameter. +#' This version uses discrete \code{RColorBrewer} scales, binned by the "breaks" parameter. #' #' The function minimally expects the caller to pass in a data frame that: #' #' \itemize{ -#' \item has one column of all state abbreviationis (all caps, including \code{DC} & \code{PR} or a column of state names (standard capitalization) named \code{state} +#' \item has one column of all state abbreviationis (all caps, including \code{DC} & +#' \code{PR} or a column of state names (standard capitalization) named \code{state} #' \item has another column of values named \code{value} #' } #' -#' Doing so will create a "statebin" chart with 5 breaks and return a \link{ggplot2} object. +#' Doing so will create a "statebin" chart with 5 breaks and return a ggplot2 object. #' #' You can use a different column for the state names and values by changing \code{state_col} #' and \code{value_col} accordingly. #' #' To add a title, change \code{plot_title} to anything but an empty atomic string vector (i.e. \code{""}) #' and set \code{title_position} to "\code{top}" or "\code{bottom}". Choosing "\code{bottom}" -#' will cause \code{statebins} to use \link{arrangeGrob} to position the title via \code{sub} and +#' will cause \code{statebins} to use \code{arrangeGrob} to position the title via \code{sub} and #' return a frame grob instead of a ggplot2 object. #' #' @param state_data data frame of states and values to plot -#' @param state_col column name in \code{state_data} that has the states. no duplicates and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") +#' @param state_col column name in \code{state_data} that has the states. no duplicates +#' and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") #' @param value_col column name in \code{state_data} that holds the values to be plotted #' @param text_color default "\code{black}" #' @param font_size font size (default = \code{3}) #' @param state_border_col default "\code{white}" - this creates the "spaces" between boxes -#' @param breaks a single number (greater than or equal to 2) giving the number of intervals into which data values are to be cut. +#' @param breaks a single number (greater than or equal to 2) giving the number of intervals +#' into which data values are to be cut. #' @param labels labels for the levels \code{breaks} #' @param legend_title title for the legend -#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or "\code{bottom}" (defaults to "\code{top}") -#' @param brewer_pal which named \link{RColorBrewer} palette to use (defaults to "PuBu") +#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or +#' "\code{bottom}" (defaults to "\code{top}") +#' @param brewer_pal which named \code{RColorBrewer} palette to use (defaults to "PuBu") #' @param plot_title title for the plot -#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" for none); if "\code{bottom}", you get back a grob vs a ggplot object +#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" +#' for none); if "\code{bottom}", you get back a grob vs a ggplot object #' @return ggplot2 object or grob #' @export #' @examples @@ -148,37 +153,41 @@ statebins <- function(state_data, state_col="state", value_col="value", #' #' \code{statebins()} creates "statebin" charts in the style of \url{http://bit.ly/statebins} #' -#' This version uses a continuous scale based on \link{RColorBrewer} scales -#' (passing in a 6 element \code{RColorBrewer} palette to \link{scale_fill_gradientn}). +#' This version uses a continuous scale based on \code{RColorBrewer} scales +#' (passing in a 6 element \code{RColorBrewer} palette to \code{scale_fill_gradientn}). #' #' The function minimally expects the caller to pass in a data frame that: #' #' \itemize{ -#' \item has one column of all state abbreviationis (all caps, including \code{DC} & \code{PR} ) or a column of state names (standard capitalization) named \code{state} +#' \item has one column of all state abbreviationis (all caps, including \code{DC} & +#' \code{PR} ) or a column of state names (standard capitalization) named \code{state} #' \item has another column of values named \code{value} #' } #' -#' Doing so will create a "statebin" chart with 5 breaks and return a \link{ggplot2} object. +#' Doing so will create a "statebin" chart with 5 breaks and return a \code{ggplot2} object. #' #' You can use a different column for the state names and values by changing \code{state_col} #' and \code{value_col} accordingly. #' #' To add a title, change \code{plot_title} to anything but an empty atomic string vector (i.e. \code{""}) #' and set \code{title_position} to "\code{top}" or "\code{bottom}". Choosing "\code{bottom}" -#' will cause \code{statebins} to use \link{arrangeGrob} to position the title via \code{sub} and +#' will cause \code{statebins} to use \code{arrangeGrob} to position the title via \code{sub} and #' return a frame grob instead of a ggplot2 object. #' #' @param state_data data frame of states and values to plot -#' @param state_col column name in \code{state_data} that has the states. no duplicates and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") +#' @param state_col column name in \code{state_data} that has the states. no duplicates +#' and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") #' @param value_col column name in \code{state_data} that holds the values to be plotted #' @param text_color default "\code{black}" #' @param font_size font size (default = \code{3}) #' @param state_border_col default "\code{white}" - this creates the "spaces" between boxes #' @param legend_title title for the legend -#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or "\code{bottom}" (defaults to "\code{top}") -#' @param brewer_pal which named \link{RColorBrewer} palette to use (defaults to "PuBu") +#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or +#' "\code{bottom}" (defaults to "\code{top}") +#' @param brewer_pal which named \code{RColorBrewer} palette to use (defaults to "PuBu") #' @param plot_title title for the plot -#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" for none); if "\code{bottom}", you get back a grob vs a ggplot object +#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" +#' for none); if "\code{bottom}", you get back a grob vs a ggplot object #' @return ggplot2 object or grob #' @export #' @examples @@ -255,41 +264,53 @@ statebins_continuous <- function(state_data, state_col="state", value_col="value #' The function minimally expects the caller to pass in a data frame that: #' #' \itemize{ -#' \item has one column of all state abbreviationis (all caps, including \code{DC} & \code{PR} or a column of state names (standard capitalization) named \code{state} +#' \item has one column of all state abbreviationis (all caps, including \code{DC} & +#' \code{PR} or a column of state names (standard capitalization) named \code{state} #' \item has another column of colors named \code{color} #' } #' -#' Doing so will create a "statebin" chart with the colors specified as a \link{ggplot2} object. +#' Doing so will create a "statebin" chart with the colors specified as a ggplot2 object. #' #' You can use a different column for the state names and colors by changing \code{state_col} #' and \code{color_col} accordingly. #' #' To add a title, change \code{plot_title} to anything but an empty atomic string vector (i.e. \code{""}) #' and set \code{title_position} to "\code{top}" or "\code{bottom}". Choosing "\code{bottom}" -#' will cause \code{statebins} to use \link{arrangeGrob} to position the title via \code{sub} and +#' will cause \code{statebins} to use \code{arrangeGrob} to position the title via \code{sub} and #' return a frame grob instead of a ggplot2 object. #' #' @param state_data data frame of states and values to plot -#' @param state_col column name in \code{state_data} that has the states. no duplicates and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") +#' @param state_col column name in \code{state_data} that has the states. no duplicates +#' and can be names (e.g. "\code{Maine}") or abbreviatons (e.g. "\code{ME}") #' @param color_col column name in \code{state_data} that holds the colors to be used #' @param text_color default "\code{black}" #' @param font_size font size (default = \code{3}) #' @param state_border_col default "\code{white}" - this creates the "spaces" between boxes -#' @param labels labels for the legend (should be the same number as distinct colors in \code{color_col}); \code{NULL} == no labels/legend +#' @param labels labels for the legend (should be the same number as distinct colors in +#' \code{color_col}); \code{NULL} == no labels/legend #' @param legend_title title for the legend -#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or "\code{bottom}" (defaults to "\code{top}") +#' @param legend_position "\code{none}", "\code{top}", "\code{left}", "\code{right}" or +#' "\code{bottom}" (defaults to "\code{top}") #' @param plot_title title for the plot -#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" for none); if "\code{bottom}", you get back a grob vs a ggplot object +#' @param title_position where to put the title ("\code{bottom}" or "\code{top}" or "" +#' for none); if "\code{bottom}", you get back a grob vs a ggplot object #' @return ggplot2 object or grob #' @export #' @examples #' \dontrun{ #' library(httr) #' library(dplyr) -#' election_2012 <- GET("https://raw.githubusercontent.com/hrbrmstr/statebins/master/tmp/election2012.csv") -#' results <- read.csv(textConnection(content(election_2012, as="text")), header=TRUE, stringsAsFactors=FALSE) -#' results <- results %>% mutate(color=ifelse(is.na(Obama), "#2166ac", "#b2182b")) %>% select(state, color) -#' results %>% statebins_manual(font_size=4, text_color = "white", labels=c("Romney", "Obama"), legend_position="right", legend_title="Winner") +#' election_2012 <- +#' GET("https://raw.githubusercontent.com/hrbrmstr/statebins/master/tmp/election2012.csv") +#' results <- read.csv(textConnection(content(election_2012, as="text")), +#' header=TRUE, stringsAsFactors=FALSE) +#' results <- results %>% +#' mutate(color=ifelse(is.na(Obama), "#2166ac", "#b2182b")) %>% +#' select(state, color) +#' results %>% +#' statebins_manual(font_size=4, +#' text_color = "white", labels=c("Romney", "Obama"), +#' legend_position="right", legend_title="Winner") #' } statebins_manual <- function(state_data, state_col="state", color_col="color", text_color="black", font_size=3, diff --git a/README.Rmd b/README.Rmd index 493a9d1..4467c2f 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,8 +1,8 @@ --- -title: "README" -author: "Bob Rudis" -date: January 30, 2015 +title: "statebins" output: + html_document: + keep_md: true md_document: variant: markdown_github --- @@ -23,6 +23,7 @@ The following functions are implemented: ### News +- Version `1.2.2` released (CRAN update) - Version `1.2.1` released - Added support for `PR`/`Puerto Rico`[[1](https://github.com/hrbrmstr/statebins/issues/2)] and fixed a bug[[2](https://github.com/hrbrmstr/statebins/issues/3)] when using anything but a `data.frame` as input. Also no longer fails (deals with the following but with a warning) when duplicate states are in the input data or invalid states are in the input data. - Version `1.1.0` released - `statebins_manual()` for manual placement of colors and moving of AK in support of a [pull request](https://github.com/hrbrmstr/statebins/pull/1) by [hansthompson](https://github.com/hansthompson) - Version `1.0.0` released diff --git a/README.html b/README.html new file mode 100644 index 0000000..d327097 --- /dev/null +++ b/README.html @@ -0,0 +1,284 @@ + + + + + + + + + + + + +statebins + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +

statebins - U.S. State Cartogram Heatmaps in R; an alternative to choropleth maps for USA States

+

The following functions are implemented:

+ +
+

TODO

+
    +
  • The current version is usable, but I think the plot margins and the legends need work
  • +
  • Apply algorithm to switch to light-on-dark depending on the background tile color
  • +
+
+
+

News

+
    +
  • Version 1.2.1 released - Added support for PR/Puerto Rico[1] and fixed a bug[2] when using anything but a data.frame as input. Also no longer fails (deals with the following but with a warning) when duplicate states are in the input data or invalid states are in the input data.
  • +
  • Version 1.1.0 released - statebins_manual() for manual placement of colors and moving of AK in support of a pull request by hansthompson
  • +
  • Version 1.0.0 released
  • +
+
+
+

Installation

+
devtools::install_github("hrbrmstr/statebins")
+
+
+

Usage

+

All of the following examples use the WaPo data. It looks like the columns they use are scaled data and I didn’t take the time to figure out what they did, so the final figure just mimics their output (including the non-annotated legend).

+
library(statebins)
+
+# current verison
+packageVersion("statebins")
+
## [1] '1.2.2'
+
# the original wapo data
+
+dat <- read.csv("http://www.washingtonpost.com/wp-srv/special/business/states-most-threatened-by-trade/states.csv?cache=1", stringsAsFactors=FALSE)
+
+gg <- statebins(dat, "state", "avgshare94_00", breaks=4, 
+                labels=c("0-1", "1-2", "2-3", "3-4"),
+                legend_title="Share of workforce with jobs lost or threatened by trade", font_size=3, 
+                brewer_pal="Blues", text_color="black", 
+                plot_title="1994-2000", title_position="bottom")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+
gg
+
## TableGrob (2 x 1) "arrange": 2 grobs
+##     z     cells    name              grob
+##     1 (1-1,1-1) arrange    gtable[layout]
+## sub 2 (2-2,1-1) arrange text[GRID.text.1]
+
# continuous scale, legend on top
+
+gg2 <- statebins_continuous(dat, "state", "avgshare01_07",
+                            legend_title="Share of workforce with jobs lost or threatened by trade", legend_position="top",
+                            brewer_pal="OrRd", text_color="black", font_size=3, 
+                            plot_title="2001-2007", title_position="bottom")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+
gg2
+
## TableGrob (2 x 1) "arrange": 2 grobs
+##     z     cells    name               grob
+##     1 (1-1,1-1) arrange     gtable[layout]
+## sub 2 (2-2,1-1) arrange text[GRID.text.53]
+
# continuous scale, no legend
+
+gg3 <- statebins_continuous(dat, "state", "avgshare08_12",
+                            legend_title="States", legend_position="none",
+                            brewer_pal="Purples", text_color="black", font_size=3, 
+                            plot_title="2008-2012", title_position="bottom")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+
gg3
+
## TableGrob (2 x 1) "arrange": 2 grobs
+##     z     cells    name               grob
+##     1 (1-1,1-1) arrange     gtable[layout]
+## sub 2 (2-2,1-1) arrange text[GRID.text.89]
+
# mortality (only to show PR and using a data.table)
+# from: http://www.cdc.gov/nchs/fastats/state-and-territorial-data.htm
+
+dat <- data.table::fread("http://dds.ec/data/deaths.csv")
+statebins_continuous(dat, "state", "death_rate", legend_title="Per 100K pop",
+                    plot_title="Mortality Rate (2010)")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+
## TableGrob (2 x 1) "arrange": 2 grobs
+##     z     cells    name                grob
+##     1 (1-1,1-1) arrange      gtable[layout]
+## sub 2 (2-2,1-1) arrange text[GRID.text.117]
+
# fertility (only to show tbl_dt)
+
+dat <- dplyr::tbl_dt(dat)
+statebins_continuous(dat, "state", "fertility_rate", legend_title="Per 100K pop", 
+                     plot_title="Fertility Rate (2010)", brewer_pal="PuBuGn")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+
## TableGrob (2 x 1) "arrange": 2 grobs
+##     z     cells    name                grob
+##     1 (1-1,1-1) arrange      gtable[layout]
+## sub 2 (2-2,1-1) arrange text[GRID.text.153]
+
# manual - perhaps good for elections?
+
+library(httr)
+library(dplyr)
+election_2012 <- GET("https://raw.githubusercontent.com/hrbrmstr/statebins/master/tmp/election2012.csv")
+results <- read.csv(textConnection(content(election_2012, as="text")), header=TRUE, stringsAsFactors=FALSE)
+results <- results %>% mutate(color=ifelse(is.na(Obama), "#2166ac", "#b2182b")) %>% select(state, color)
+results %>% statebins_manual(font_size=4, text_color = "white", labels=c("Romney", "Obama"), legend_position="right", legend_title="Winner")
+
## Warning: `show_guide` has been deprecated. Please use `show.legend` instead.
+

+
# or, more like the one in the WaPo article; i might be picking the wrong columns here. it's just for an example
+
+sb <- function(col, title) {
+  statebins(dat, "state",col, brewer_pal="Blues", text_color="black", legend_position="none", font_size=3, plot_title=title, breaks=4, labels=1:4)
+}
+
# cheating and using <table> to arrange them below and also making a WaPo-like legend, 
+# since mucking with grid graphics margins/padding was not an option time-wise at the moment
+
+sb("avgshare94_00", "1994-2000")
+sb("avgshare01_07", "2001-2007")
+sb("avgshare08_12", "2008-2012")
+ + + +
+img +
+

And, we’ll throw in a gratuitous animation for good measure:

+
# data set from StatsAmerica - http://www.statsamerica.org/profiles/sip_index.html
+
+# median household income from the ACS survey
+miacs <- read.csv("http://dds.ec/data/median-income-acs.csv", header=TRUE, stringsAsFactors=FALSE)
+
+# generate frames based on year
+sapply(unique(miacs$year), function(year) {
+  
+  png(file=sprintf("tmp/household%d.png", year),
+      type="quartz", antialias="subpixel", width=800, height=600)
+  
+  rng <- floor(range(miacs[miacs$year==year,]$mh_inc))
+  
+  ggtmp <- statebins(miacs[miacs$year==year,], "state", "mh_inc",
+                   legend_title="States", legend_position="none",
+                   brewer_pal="Greens", text_color="black", font_size=3,
+                   plot_title=sprintf("Median Household Income (ACS) %d\n$%s - $%s", year, comma(rng[1]), comma(rng[2])), title_position="top")
+  
+  print(ggtmp)
+  
+  dev.off()
+  
+})
+
+# animate them with ImageMagick
+system("convert -background white -alpha remove -layers OptimizePlus -delay 150 tmp/*.png -loop 1 tmp/household.gif")
+
+