Plot map with values for countries as color in R?

自古美人都是妖i 提交于 2019-11-29 02:54:22

问题


I have following simple example data which I want to plot on a map with gradient color corresponding to value of the given country.

ddf = read.table(text="
country value
USA 10
UK 30
Sweden 50
Japan 70
China 90
Germany 100
France 80
Italy 60
Nepal 40
Nigeria 20
", header=T)

On google search, I found several sites. However, I am looking for code which is small and clear, and should preferably be fast (I found ggplot methods to be relativley slow). The resolution of world map need not be high.

I tried following code:

library(maptools)
data(wrld_simpl)
plot(wrld_simpl)

Specific nations can be colored as given on : Using [R] maps package - colouring in specific nations on a world map Using the command:

plot(wrld_simpl, col = c(gray(.80), "red")[grepl("^U", wrld_simpl@data$NAME) + 1])

But how can I get map with above data in a gradient of colors. Thanks for your help.


回答1:


You could use rworldmap if you wanted less code and a coarser resolution map.

library(rworldmap)

#create a map-shaped window
mapDevice('x11')
#join to a coarse resolution map
spdf <- joinCountryData2Map(ddf, joinCode="NAME", nameJoinColumn="country")

mapCountryData(spdf, nameColumnToPlot="value", catMethod="fixedWidth")

Default categorisation, colours and legends can be altered, see this RJournal paper.

It would be faster with country codes rather than names.




回答2:


Define "slow". ggplot provides one of the most flexible ways to present data on maps at the the cost of a few extra seconds.

library(RColorBrewer)
library(maptools)
library(ggplot2)

data(wrld_simpl)

ddf = read.table(text="
                 country value
                 'United States' 10
                 'United Kingdom' 30
                 'Sweden' 50
                 'Japan' 70
                 'China' 90
                 'Germany' 100
                 'France' 80
                 'Italy' 60
                 'Nepal' 40
                 'Nigeria' 20", header=TRUE)

# Pascal had a #spiffy solution that is generally faster

plotPascal <- function() {

  pal <- colorRampPalette(brewer.pal(9, 'Reds'))(length(ddf$value))
  pal <- pal[with(ddf, findInterval(value, sort(unique(value))))]

  col <- rep(grey(0.8), length(wrld_simpl@data$NAME))
  col[match(ddf$country, wrld_simpl@data$NAME)] <- pal

  plot(wrld_simpl, col = col)

}

plotme <- function() {

  # align colors to countries

  ddf$brk <- cut(ddf$value, 
                 breaks=c(0, sort(ddf$value)), 
                 labels=as.character(ddf[order(ddf$value),]$country),
                 include.lowest=TRUE)

  # this lets us use the contry name vs 3-letter ISO
  wrld_simpl@data$id <- wrld_simpl@data$NAME

  wrld <- fortify(wrld_simpl, region="id")
  wrld <- subset(wrld, id != "Antarctica") # we don't rly need Antarctica

  gg <- ggplot()

  # setup base map
  gg <- gg + geom_map(data=wrld, map=wrld, aes(map_id=id, x=long, y=lat), fill="white", color="#7f7f7f", size=0.25)

  # add our colored regions
  gg <- gg + geom_map(data=ddf, map=wrld, aes(map_id=country, fill=brk),  color="white", size=0.25)

  # this sets the scale and, hence, the legend
  gg <- gg + scale_fill_manual(values=colorRampPalette(brewer.pal(9, 'Reds'))(length(ddf$value)), 
                               name="Country")

  # this gives us proper coords. mercator proj is default
  gg <- gg + coord_map()
  gg <- gg + labs(x="", y="")
  gg <- gg + theme(plot.background = element_rect(fill = "transparent", colour = NA),
                   panel.border = element_blank(),
                   panel.background = element_rect(fill = "transparent", colour = NA),
                   panel.grid = element_blank(),
                   axis.text = element_blank(),
                   axis.ticks = element_blank(),
                   legend.position = "right")
  gg

}

system.time(plotme())
##  user  system elapsed 
## 1.911   0.005   1.915 

system.time(plotthem())
##  user  system elapsed 
## 1.125   0.014   1.138 

The ggplot code produces the following map:

The timings vary per-run, but I've not seen them go more than a full minute apart (it appeard to average 0.6m on my system, but I wasn't about to do extensive benchmarking).

UPDATE

As your requirements continue to be teased out, you can replace the discrete scale with a continuous one rather easily.

pal <- colorRampPalette(brewer.pal(9, 'Reds'))(length(ddf$value))
palSz <- 10 # not sure what you really want/need for this range

gg <- gg + scale_fill_gradient2(low = pal[1],
                                mid = pal[palSz/2],
                                high = pal[palSz],
                                midpoint = (max(ddf$value) + min(ddf$value)) / 2,
                                name="value")

But, it sounds like you should probably stick with @Andy's rworldmap since it abstracts the complexity.




回答3:


Probably not optimized:

library(RColorBrewer)
library(maptools)
data(wrld_simpl)

ddf = read.table(text="
country value
'United States' 10
'United Kingdom' 30
'Sweden' 50
'Japan' 70
'China' 90
'Germany' 100
'France' 80
'Italy' 60
'Nepal' 40
'Nigeria' 20", header=TRUE)

Reds is the name of a color palette. See ?brewer.pal for other available palettes.

pal <- colorRampPalette(brewer.pal(9, 'Reds'))(length(ddf$value))
pal <- pal[with(ddf, findInterval(value, sort(unique(value))))]

col <- rep(grey(0.8), length(wrld_simpl@data$NAME))
col[match(ddf$country, wrld_simpl@data$NAME)] <- pal

plot(wrld_simpl, col = col)



来源:https://stackoverflow.com/questions/24136868/plot-map-with-values-for-countries-as-color-in-r

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!