Choropleth Maps in R

How to make a choropleth map in R. A choropleth map shades geographic regions by value.


New to Plotly?

Plotly is a free and open-source graphing library for R. We recommend you read our Getting Started guide for the latest installation or upgrade instructions, then move on to our Plotly Fundamentals tutorials or dive straight in to some Basic Charts tutorials.

A Choropleth Map is a map composed of colored polygons. It is used to represent spatial variations of a quantity. This page documents how to build outline choropleth maps, but you can also build choropleth tile maps using our Mapbox trace types.

Base Map Configuration

Plotly figures made with plot_ly have a layout.geo object which can be used to control the appearance of the base map onto which data is plotted.

Introduction: main parameters for choropleth outline maps

Making choropleth maps requires two main types of input:

  1. Geometry information:
    1. This can either be a supplied GeoJSON file where each feature has either an id field or some identifying value in properties; or
    2. one of the built-in geometries within plot_ly: US states and world countries (see below)
  2. A list of values indexed by feature identifier.

The GeoJSON data is passed to the geojson argument, and the data is passed into the z argument of choropleth traces.

Note the geojson attribute can also be the URL to a GeoJSON file, which can speed up map rendering in certain cases.

GeoJSON with feature.id

Here we load a GeoJSON file containing the geometry information for US counties, where feature.id is a FIPS code.

library(plotly)
library(rjson)

data <- fromJSON(file="https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json")
data$features[[1]]
## $type
## [1] "Feature"
## 
## $properties
## $properties$GEO_ID
## [1] "0500000US01001"
## 
## $properties$STATE
## [1] "01"
## 
## $properties$COUNTY
## [1] "001"
## 
## $properties$NAME
## [1] "Autauga"
## 
## $properties$LSAD
## [1] "County"
## 
## $properties$CENSUSAREA
## [1] 594.436
## 
## 
## $geometry
## $geometry$type
## [1] "Polygon"
## 
## $geometry$coordinates
## $geometry$coordinates[[1]]
## $geometry$coordinates[[1]][[1]]
## [1] -86.49677  32.34444
## 
## $geometry$coordinates[[1]][[2]]
## [1] -86.71790  32.40281
## 
## $geometry$coordinates[[1]][[3]]
## [1] -86.81491  32.34080
## 
## $geometry$coordinates[[1]][[4]]
## [1] -86.89058  32.50297
## 
## $geometry$coordinates[[1]][[5]]
## [1] -86.91760  32.66417
## 
## $geometry$coordinates[[1]][[6]]
## [1] -86.71339  32.66173
## 
## $geometry$coordinates[[1]][[7]]
## [1] -86.71422  32.70569
## 
## $geometry$coordinates[[1]][[8]]
## [1] -86.41312  32.70739
## 
## $geometry$coordinates[[1]][[9]]
## [1] -86.41117  32.40994
## 
## $geometry$coordinates[[1]][[10]]
## [1] -86.49677  32.34444
## 
## 
## 
## 
## $id
## [1] "01001"

Data indexed by id

Here we load unemployment data by county, also indexed by FIPS code.

df = read.csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv", header = T, colClasses = c("fips"="character"))
head(df)
##    fips unemp
## 1 01001   5.3
## 2 01003   5.4
## 3 01005   8.6
## 4 01007   6.6
## 5 01009   5.5
## 6 01011   7.2

Choropleth Map Using GeoJSON

library(plotly)
library(rjson)

url <- 'https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json'
counties <- rjson::fromJSON(file=url)
url2<- "https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv"
df <- read.csv(url2, colClasses=c(fips="character"))
g <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  showlakes = TRUE,
  lakecolor = toRGB('white')
)
fig <- plot_ly()
fig <- fig %>% add_trace(
    type="choropleth",
    geojson=counties,
    locations=df$fips,
    z=df$unemp,
    colorscale="Viridis",
    zmin=0,
    zmax=12,
    marker=list(line=list(
      width=0)
    )
  )
fig <- fig %>% colorbar(title = "Unemployment Rate (%)")
fig <- fig %>% layout(
    title = "2016 US Unemployment by County"
)

fig <- fig %>% layout(
    geo = g
  )

fig

Note In this example we set layout.geo.scope to usa to automatically configure the map to display USA-centric data in an appropriate projection.

Indexing by GeoJSON Properties

If the GeoJSON you are using either does not have an id field or you wish you use one of the keys in the properties field, you may use the featureidkey parameter to specify where to match the values of locations.

In the following GeoJSON object/data-file pairing, the values of properties.district match the values of the district column:

library(plotly)
library(rjson)

url <- 'https://raw.githubusercontent.com/plotly/datasets/master/election.geojson'
geojson <- rjson::fromJSON(file=url)
url2<- "https://raw.githubusercontent.com/plotly/datasets/master/election.csv"
df <- read.csv(url2)
g <- list(
  fitbounds = "locations",
  visible = FALSE
)
fig <- plot_ly() 
fig <- fig %>% add_trace(
    type="choropleth",
    geojson=geojson,
    locations=df$district,
    z=df$Bergeron,
    colorscale="Viridis",
    featureidkey="properties.district"
  )
fig <- fig %>% layout(
    geo = g
  )
fig <- fig %>% colorbar(title = "Bergeron Votes")
fig <- fig %>% layout(
    title = "2013 Montreal Election"
)
fig

Using Built-in Country and State Geometries

Plotly comes with two built-in geometries which do not require an external GeoJSON file:

  1. USA States
  2. Countries as defined in the Natural Earth dataset.

Note and disclaimer: cultural (as opposed to physical) features are by definition subject to change, debate and dispute. Plotly includes data from Natural Earth "as-is" and defers to the Natural Earth policy regarding disputed borders which read:

Natural Earth Vector draws boundaries of countries according to defacto status. We show who actually controls the situation on the ground.

To use the built-in countries geometry, provide locations as three-letter ISO country codes.

library(plotly)
df <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv")
df
##                               COUNTRY GDP..BILLIONS. CODE
## 1                         Afghanistan          21.71  AFG
## 2                             Albania          13.40  ALB
## 3                             Algeria         227.80  DZA
## 4                      American Samoa           0.75  ASM
## 5                             Andorra           4.80  AND
## 6                              Angola         131.40  AGO
## 7                            Anguilla           0.18  AIA
## 8                 Antigua and Barbuda           1.24  ATG
## 9                           Argentina         536.20  ARG
## 10                            Armenia          10.88  ARM
## 11                              Aruba           2.52  ABW
## 12                          Australia        1483.00  AUS
## 13                            Austria         436.10  AUT
## 14                         Azerbaijan          77.91  AZE
## 15                       Bahamas, The           8.65  BHM
## 16                            Bahrain          34.05  BHR
## 17                         Bangladesh         186.60  BGD
## 18                           Barbados           4.28  BRB
## 19                            Belarus          75.25  BLR
## 20                            Belgium         527.80  BEL
## 21                             Belize           1.67  BLZ
## 22                              Benin           9.24  BEN
## 23                            Bermuda           5.20  BMU
## 24                             Bhutan           2.09  BTN
## 25                            Bolivia          34.08  BOL
## 26             Bosnia and Herzegovina          19.55  BIH
## 27                           Botswana          16.30  BWA
## 28                             Brazil        2244.00  BRA
## 29             British Virgin Islands           1.10  VGB
## 30                             Brunei          17.43  BRN
## 31                           Bulgaria          55.08  BGR
## 32                       Burkina Faso          13.38  BFA
## 33                              Burma          65.29  MMR
## 34                            Burundi           3.04  BDI
## 35                         Cabo Verde           1.98  CPV
## 36                           Cambodia          16.90  KHM
## 37                           Cameroon          32.16  CMR
## 38                             Canada        1794.00  CAN
## 39                     Cayman Islands           2.25  CYM
## 40           Central African Republic           1.73  CAF
## 41                               Chad          15.84  TCD
## 42                              Chile         264.10  CHL
## 43                              China       10360.00  CHN
## 44                           Colombia         400.10  COL
## 45                            Comoros           0.72  COM
## 46  Congo, Democratic Republic of the          32.67  COD
## 47             Congo, Republic of the          14.11  COG
## 48                       Cook Islands           0.18  COK
## 49                         Costa Rica          50.46  CRI
## 50                      Cote d'Ivoire          33.96  CIV
## 51                            Croatia          57.18  HRV
## 52                               Cuba          77.15  CUB
## 53                            Curacao           5.60  CUW
## 54                             Cyprus          21.34  CYP
## 55                     Czech Republic         205.60  CZE
## 56                            Denmark         347.20  DNK
## 57                           Djibouti           1.58  DJI
## 58                           Dominica           0.51  DMA
## 59                 Dominican Republic          64.05  DOM
## 60                            Ecuador         100.50  ECU
## 61                              Egypt         284.90  EGY
## 62                        El Salvador          25.14  SLV
## 63                  Equatorial Guinea          15.40  GNQ
## 64                            Eritrea           3.87  ERI
## 65                            Estonia          26.36  EST
## 66                           Ethiopia          49.86  ETH
## 67  Falkland Islands (Islas Malvinas)           0.16  FLK
## 68                      Faroe Islands           2.32  FRO
## 69                               Fiji           4.17  FJI
## 70                            Finland         276.30  FIN
## 71                             France        2902.00  FRA
## 72                   French Polynesia           7.15  PYF
## 73                              Gabon          20.68  GAB
## 74                        Gambia, The           0.92  GMB
## 75                            Georgia          16.13  GEO
## 76                            Germany        3820.00  DEU
## 77                              Ghana          35.48  GHA
## 78                          Gibraltar           1.85  GIB
## 79                             Greece         246.40  GRC
## 80                          Greenland           2.16  GRL
## 81                            Grenada           0.84  GRD
## 82                               Guam           4.60  GUM
## 83                          Guatemala          58.30  GTM
## 84                           Guernsey           2.74  GGY
## 85                      Guinea-Bissau           1.04  GNB
## 86                             Guinea           6.77  GIN
## 87                             Guyana           3.14  GUY
## 88                              Haiti           8.92  HTI
## 89                           Honduras          19.37  HND
## 90                          Hong Kong         292.70  HKG
## 91                            Hungary         129.70  HUN
## 92                            Iceland          16.20  ISL
## 93                              India        2048.00  IND
## 94                          Indonesia         856.10  IDN
## 95                               Iran         402.70  IRN
## 96                               Iraq         232.20  IRQ
## 97                            Ireland         245.80  IRL
## 98                        Isle of Man           4.08  IMN
## 99                             Israel         305.00  ISR
## 100                             Italy        2129.00  ITA
## 101                           Jamaica          13.92  JAM
## 102                             Japan        4770.00  JPN
## 103                            Jersey           5.77  JEY
## 104                            Jordan          36.55  JOR
## 105                        Kazakhstan         225.60  KAZ
## 106                             Kenya          62.72  KEN
## 107                          Kiribati           0.16  KIR
## 108                      Korea, North          28.00  PRK
## 109                      Korea, South        1410.00  KOR
## 110                            Kosovo           5.99  KSV
## 111                            Kuwait         179.30  KWT
## 112                        Kyrgyzstan           7.65  KGZ
## 113                              Laos          11.71  LAO
## 114                            Latvia          32.82  LVA
## 115                           Lebanon          47.50  LBN
## 116                           Lesotho           2.46  LSO
## 117                           Liberia           2.07  LBR
## 118                             Libya          49.34  LBY
## 119                     Liechtenstein           5.11  LIE
## 120                         Lithuania          48.72  LTU
## 121                        Luxembourg          63.93  LUX
## 122                             Macau          51.68  MAC
## 123                         Macedonia          10.92  MKD
## 124                        Madagascar          11.19  MDG
## 125                            Malawi           4.41  MWI
## 126                          Malaysia         336.90  MYS
## 127                          Maldives           2.41  MDV
## 128                              Mali          12.04  MLI
## 129                             Malta          10.57  MLT
## 130                  Marshall Islands           0.18  MHL
## 131                        Mauritania           4.29  MRT
## 132                         Mauritius          12.72  MUS
## 133                            Mexico        1296.00  MEX
## 134   Micronesia, Federated States of           0.34  FSM
## 135                           Moldova           7.74  MDA
## 136                            Monaco           6.06  MCO
## 137                          Mongolia          11.73  MNG
## 138                        Montenegro           4.66  MNE
## 139                           Morocco         112.60  MAR
## 140                        Mozambique          16.59  MOZ
## 141                           Namibia          13.11  NAM
## 142                             Nepal          19.64  NPL
## 143                       Netherlands         880.40  NLD
## 144                     New Caledonia          11.10  NCL
## 145                       New Zealand         201.00  NZL
## 146                         Nicaragua          11.85  NIC
## 147                           Nigeria         594.30  NGA
## 148                             Niger           8.29  NER
## 149                              Niue           0.01  NIU
## 150          Northern Mariana Islands           1.23  MNP
## 151                            Norway         511.60  NOR
## 152                              Oman          80.54  OMN
## 153                          Pakistan         237.50  PAK
## 154                             Palau           0.65  PLW
## 155                            Panama          44.69  PAN
## 156                  Papua New Guinea          16.10  PNG
## 157                          Paraguay          31.30  PRY
## 158                              Peru         208.20  PER
## 159                       Philippines         284.60  PHL
## 160                            Poland         552.20  POL
## 161                          Portugal         228.20  PRT
## 162                       Puerto Rico          93.52  PRI
## 163                             Qatar         212.00  QAT
## 164                           Romania         199.00  ROU
## 165                            Russia        2057.00  RUS
## 166                            Rwanda           8.00  RWA
## 167             Saint Kitts and Nevis           0.81  KNA
## 168                       Saint Lucia           1.35  LCA
## 169                      Saint Martin           0.56  MAF
## 170         Saint Pierre and Miquelon           0.22  SPM
## 171  Saint Vincent and the Grenadines           0.75  VCT
## 172                             Samoa           0.83  WSM
## 173                        San Marino           1.86  SMR
## 174             Sao Tome and Principe           0.36  STP
## 175                      Saudi Arabia         777.90  SAU
## 176                           Senegal          15.88  SEN
## 177                            Serbia          42.65  SRB
## 178                        Seychelles           1.47  SYC
## 179                      Sierra Leone           5.41  SLE
## 180                         Singapore         307.90  SGP
## 181                      Sint Maarten         304.10  SXM
## 182                          Slovakia          99.75  SVK
## 183                          Slovenia          49.93  SVN
## 184                   Solomon Islands           1.16  SLB
## 185                           Somalia           2.37  SOM
## 186                      South Africa         341.20  ZAF
## 187                       South Sudan          11.89  SSD
## 188                             Spain        1400.00  ESP
## 189                         Sri Lanka          71.57  LKA
## 190                             Sudan          70.03  SDN
## 191                          Suriname           5.27  SUR
## 192                         Swaziland           3.84  SWZ
## 193                            Sweden         559.10  SWE
## 194                       Switzerland         679.00  CHE
## 195                             Syria          64.70  SYR
## 196                            Taiwan         529.50  TWN
## 197                        Tajikistan           9.16  TJK
## 198                          Tanzania          36.62  TZA
## 199                          Thailand         373.80  THA
## 200                       Timor-Leste           4.51  TLS
## 201                              Togo           4.84  TGO
## 202                             Tonga           0.49  TON
## 203               Trinidad and Tobago          29.63  TTO
## 204                           Tunisia          49.12  TUN
## 205                            Turkey         813.30  TUR
## 206                      Turkmenistan          43.50  TKM
## 207                            Tuvalu           0.04  TUV
## 208                            Uganda          26.09  UGA
## 209                           Ukraine         134.90  UKR
## 210              United Arab Emirates         416.40  ARE
## 211                    United Kingdom        2848.00  GBR
## 212                     United States       17420.00  USA
## 213                           Uruguay          55.60  URY
## 214                        Uzbekistan          63.08  UZB
## 215                           Vanuatu           0.82  VUT
## 216                         Venezuela         209.20  VEN
## 217                           Vietnam         187.80  VNM
## 218                    Virgin Islands           5.08  VGB
## 219                         West Bank           6.64  WBG
## 220                             Yemen          45.45  YEM
## 221                            Zambia          25.61  ZMB
## 222                          Zimbabwe          13.74  ZWE
fig <- plot_ly(df, type='choropleth', locations=df$CODE, z=df$GDP..BILLIONS., text=df$COUNTRY, colorscale="Blues")

fig

To use the USA States geometry, set locationmode='USA-states' and provide locations as two-letter state abbreviations:

library(plotly)
df <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv")
df$hover <- with(df, paste(state, '<br>', "Beef", beef, "Dairy", dairy, "<br>",
                           "Fruits", total.fruits, "Veggies", total.veggies,
                           "<br>", "Wheat", wheat, "Corn", corn))

fig <- plot_geo(df, locationmode = 'USA-states')
fig <- fig %>% add_trace(
    z = ~total.exports, text = ~hover, locations = ~code,
    color = ~total.exports, colors = 'Purples'
  )
fig <- fig %>% colorbar(title = "Millions USD")
fig <- fig %>% layout(
    title = '2011 US Agriculture Exports by State<br>(Hover for breakdown)'
  )

fig

Customize choropleth chart

library(plotly)
df <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv")
df$hover <- with(df, paste(state, '<br>', "Beef", beef, "Dairy", dairy, "<br>",
                           "Fruits", total.fruits, "Veggies", total.veggies,
                           "<br>", "Wheat", wheat, "Corn", corn))
# give state boundaries a white border
l <- list(color = toRGB("white"), width = 2)
# specify some map projection/options
g <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  showlakes = TRUE,
  lakecolor = toRGB('white')
)

fig <- plot_geo(df, locationmode = 'USA-states')
fig <- fig %>% add_trace(
    z = ~total.exports, text = ~hover, locations = ~code,
    color = ~total.exports, colors = 'Purples'
  )
fig <- fig %>% colorbar(title = "Millions USD")
fig <- fig %>% layout(
    title = '2011 US Agriculture Exports by State<br>(Hover for breakdown)',
    geo = g
  )

fig

Note In this example we set layout.geo.scope to usa to automatically configure the map to display USA-centric data in an appropriate projection.

World Choropleth Map

df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')

# light grey boundaries
l <- list(color = toRGB("grey"), width = 0.5)

# specify map projection/options
g <- list(
  showframe = FALSE,
  showcoastlines = FALSE,
  projection = list(type = 'Mercator')
)

fig <- plot_geo(df)
fig <- fig %>% add_trace(
    z = ~GDP..BILLIONS., color = ~GDP..BILLIONS., colors = 'Blues',
    text = ~COUNTRY, locations = ~CODE, marker = list(line = l)
  )
fig <- fig %>% colorbar(title = 'GDP Billions US$', tickprefix = '$')
fig <- fig %>% layout(
    title = '2014 Global GDP<br>Source:<a href="https://www.cia.gov/library/publications/the-world-factbook/fields/2195.html">CIA World Factbook</a>',
    geo = g
  )

fig

Choropleth Inset Map

df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_ebola.csv')
# restrict from June to September
df <- subset(df, Month %in% 6:9)
# ordered factor variable with month abbreviations
df$abbrev <- ordered(month.abb[df$Month], levels = month.abb[6:9])
# September totals
df9 <- subset(df, Month == 9)

# common plot options
g <- list(
  scope = 'africa',
  showframe = F,
  showland = T,
  landcolor = toRGB("grey90")
)

g1 <- c(
  g,
  resolution = 50,
  showcoastlines = T,
  countrycolor = toRGB("white"),
  coastlinecolor = toRGB("white"),
  projection = list(type = 'Mercator'),
  list(lonaxis = list(range = c(-15, -5))),
  list(lataxis = list(range = c(0, 12))),
  list(domain = list(x = c(0, 1), y = c(0, 1)))
)

g2 <- c(
  g,
  showcountries = F,
  bgcolor = toRGB("white", alpha = 0),
  list(domain = list(x = c(0, .6), y = c(0, .6)))
)

fig <- df %>% plot_geo(
    locationmode = 'country names', sizes = c(1, 600), color = I("black")
  )
fig <- fig %>% add_markers(
    y = ~Lat, x = ~Lon, locations = ~Country,
    size = ~Value, color = ~abbrev, text = ~paste(Value, "cases")
  )
fig <- fig %>% add_text(
    x = 21.0936, y = 7.1881, text = 'Africa', showlegend = F, geo = "geo2"
  )
fig <- fig %>% add_trace(
    data = df9, z = ~Month, locations = ~Country,
    showscale = F, geo = "geo2"
  )
fig <- fig %>% layout(
    title = 'Ebola cases reported by month in West Africa 2014<br> Source: <a href="https://data.hdx.rwlabs.org/dataset/rowca-ebola-cases">HDX</a>',
    geo = g1, geo2 = g2
  )

fig

What About Dash?

Dash for R is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.

Learn about how to install Dash for R at https://dashr.plot.ly/installation.

Everywhere in this page that you see fig, you can display the same figure in a Dash for R application by passing it to the figure argument of the Graph component from the built-in dashCoreComponents package like this:

library(plotly)

fig <- plot_ly() 
# fig <- fig %>% add_trace( ... )
# fig <- fig %>% layout( ... ) 

library(dash)
library(dashCoreComponents)
library(dashHtmlComponents)

app <- Dash$new()
app$layout(
    htmlDiv(
        list(
            dccGraph(figure=fig) 
        )
     )
)

app$run_server(debug=TRUE, dev_tools_hot_reload=FALSE)