【问题标题】:plotting pie graphs on map in ggplot在ggplot的地图上绘制饼图
【发布时间】:2012-04-28 22:40:21
【问题描述】:

这可能是一个愿望清单的事情,不确定(即,可能需要创建geom_pie 才能发生这种情况)。我今天看到了一张地图 (LINK),上面有饼图,如图所示。

我不想争论饼图的优点,这更像是一个练习,我可以在 ggplot 中做到这一点吗?

我在下面提供了一个数据集(从我的投递箱加载),其中包含用于制作纽约州地图的地图数据和一些纯粹捏造的按县划分的种族百分比数据。我已经将这个种族构成作为与主要数据集的合并和一个称为键的单独数据集。我还认为 Bryan Goodrich 在另一篇文章 (HERE) 中对我的回复(关于将县名居中)对这个概念有所帮助。

我们如何用 ggplot2 制作上面的地图?

数据集和没有饼图的地图:

load(url("http://dl.dropbox.com/u/61803503/nycounty.RData"))
head(ny); head(key)  #view the data set from my drop box
library(ggplot2)
ggplot(ny, aes(long, lat, group=group)) +  geom_polygon(colour='black', fill=NA)

#  Now how can we plot a pie chart of race on each county 
#  (sizing of the pie would also be controllable via a size 
#  parameter like other `geom_` functions).

提前感谢您的想法。

编辑:我刚刚在junkcharts 看到另一个案例,它为这种能力而尖叫:

【问题讨论】:

  • 为什么选择 ggplot2?您可以使用基本图形(可能还有 sp 包)轻松绘制地图,然后使用 plotrix 包中的 floating.pie 将饼图粘贴在顶部
  • @Spaceman 我想我习惯于在 ggplot 中进行映射。但是 ggplot 的优点是可以访问facet_grid,这对于同时处理多个 chorolopleths 来说非常好。
  • 将其扩展到小条形图、直方图堆叠条等也会很有趣(这可能已经可行)
  • @hadley 的 a draft paper 有一些相似的想法
  • 除了 Spacedman 的建议之外,您还可以查看 John Colby 的 this related answer。 IMO 这些类型的地图通常被更好地描绘为一系列小型多张地图(请参阅 GIS 网站上的this question 并进行相关讨论),但我很感激想要尝试制作它们!另一个类似的选择是星图(或雷达图)。与从头开始编写饼图相比,他们编写几何图形不会那么乏味。

标签: r ggplot2


【解决方案1】:

三年后这个问题解决了。我已经将许多流程放在一起,感谢@Guangchuang Yu 的优秀 ggtree 包,这可以很容易地完成。请注意,从(2015 年 9 月 3 日)起,您需要安装 1.0.18 版的 ggtree,但这些最终会渗透到各自的存储库中。

我使用了以下资源来制作这个(链接将提供更详细的信息):

  1. ggtree blog
  2. move ggplot legend
  3. correct ggtree version
  4. centering things in polygons

代码如下:

load(url("http://dl.dropbox.com/u/61803503/nycounty.RData"))
head(ny); head(key)  #view the data set from my drop box

if (!require("pacman")) install.packages("pacman")
p_load(ggplot2, ggtree, dplyr, tidyr, sp, maps, pipeR, grid, XML, gtable)

getLabelPoint <- function(county) {Polygon(county[c('long', 'lat')])@labpt}

df <- map_data('county', 'new york')                 # NY region county data
centroids <- by(df, df$subregion, getLabelPoint)     # Returns list
centroids <- do.call("rbind.data.frame", centroids)  # Convert to Data Frame
names(centroids) <- c('long', 'lat')                 # Appropriate Header

pops <-  "http://data.newsday.com/long-island/data/census/county-population-estimates-2012/" %>%
     readHTMLTable(which=1) %>%
     tbl_df() %>%
     select(1:2) %>%
     setNames(c("region", "population")) %>%
     mutate(
         population = {as.numeric(gsub("\\D", "", population))},
         region = tolower(gsub("\\s+[Cc]ounty|\\.", "", region)),
         #weight = ((1 - (1/(1 + exp(population/sum(population)))))/11) 
         weight = exp(population/sum(population)),
         weight = sqrt(weight/sum(weight))/3
     )


race_data_long <- add_rownames(centroids, "region") %>>%
    left_join({distinct(select(ny, region:other))}) %>>%
    left_join(pops) %>>%
    (~ race_data) %>>%
    gather(race, prop, white:other) %>%
    split(., .$region)

pies <- setNames(lapply(1:length(race_data_long), function(i){
    ggplot(race_data_long[[i]], aes(x=1, prop, fill=race)) +
        geom_bar(stat="identity", width=1) + 
        coord_polar(theta="y") + 
        theme_tree() + 
        xlab(NULL) + 
        ylab(NULL) + 
        theme_transparent() +
        theme(plot.margin=unit(c(0,0,0,0),"mm"))
}), names(race_data_long))


e1 <- ggplot(race_data_long[[1]], aes(x=1, prop, fill=race)) +
        geom_bar(stat="identity", width=1) + 
        coord_polar(theta="y") 

leg1 <- gtable_filter(ggplot_gtable(ggplot_build(e1)), "guide-box") 


p <- ggplot(ny, aes(long, lat, group=group)) +  
    geom_polygon(colour='black', fill=NA) +
    theme_bw() +
    annotation_custom(grob = leg1, xmin = -77.5, xmax = -78.5, ymin = 44, ymax = 45) 



n <- length(pies)

for (i in 1:n) {

    nms <- names(pies)[i]
    dat <- race_data[which(race_data$region == nms)[1], ]
    p <- subview(p, pies[[i]], x=unlist(dat[["long"]])[1], y=unlist(dat[["lat"]])[1], dat[["weight"]], dat[["weight"]])

}

print(p)

【讨论】:

【解决方案2】:

这个功能应该在 ggplot 中,我认为它很快就会出现在 ggplot 中,但它目前在基础图中可用。我想我会发布这个只是为了比较。

load(url("http://dl.dropbox.com/u/61803503/nycounty.RData"))

library(plotrix)
e=10^-5
myglyff=function(gi) {
floating.pie(mean(gi$long),
             mean(gi$lat),
             x=c(gi[1,"white"]+e,
                 gi[1,"black"]+e,
                 gi[1,"hispanic"]+e,
                 gi[1,"asian"]+e,
                 gi[1,"other"]+e),
              radius=.1) #insert size variable here
}

g1=ny[which(ny$group==1),]
plot(g1$long,
     g1$lat,
     type='l',
     xlim=c(-80,-71.5),
     ylim=c(40.5,45.1))

myglyff(g1)

for(i in 2:62)
  {gi=ny[which(ny$group==i),]
    lines(gi$long,gi$lat)
    myglyff(gi)
  }

此外,在基本图形中可能有(可能有)更优雅的方法。

如您所见,有很多问题需要解决。县的填充颜色。饼图往往太小或重叠。纬度和经度不进行投影,因此县的大小被扭曲了。

无论如何,我对其他人的想法很感兴趣。

【讨论】:

  • "这个功能应该在 ggplot 中,我认为它很快就会出现在 ggplot 中。" -- 有谁知道这个功能现在在 ggplot2 中是否可用?我只能找到这个 SO 页面,docs.ggplot2 中没有任何内容
  • 上面引用的论文和幻灯片演示让我这么说。不确定它现在的位置。不确定哈德利·威克姆是否会再来这里,但他会问。 @哈德利
  • 也许这就是 hadley 论文中使用的 geom:docs.ggplot2.org/current/geom_segment.html
【解决方案3】:

我已经编写了一些代码来使用网格图形来做到这一点。这里有一个例子:https://qdrsite.wordpress.com/2016/06/26/pies-on-a-map/

这里的目标是将饼图与地图上的特定点相关联,而不一定是区域。对于这个特定的解决方案,有必要将地图坐标(纬度和经度)转换为 (0,1) 比例,以便将它们绘制在地图上的适当位置。网格包用于打印到包含绘图面板的视口。

代码:

# Pies On A Map
# Demonstration script
# By QDR

# Uses NLCD land cover data for different sites in the National Ecological Observatory Network.
# Each site consists of a number of different plots, and each plot has its own land cover classification.
# On a US map, plot a pie chart at the location of each site with the proportion of plots at that site within each land cover class.

# For this demo script, I've hard coded in the color scale, and included the data as a CSV linked from dropbox.

# Custom color scale (taken from the official NLCD legend)
nlcdcolors <- structure(c("#7F7F7F", "#FFB3CC", "#00B200", "#00FFFF", "#006600", "#E5CC99", "#00B2B2", "#FFFF00", "#B2B200", "#80FFCC"), .Names = c("unknown", "cultivatedCrops", "deciduousForest", "emergentHerbaceousWetlands", "evergreenForest", "grasslandHerbaceous", "mixedForest", "pastureHay", "shrubScrub", "woodyWetlands"))

# NLCD data for the NEON plots
nlcdtable_long <- read.csv(file='https://www.dropbox.com/s/x95p4dvoegfspax/demo_nlcdneon.csv?raw=1', row.names=NULL, stringsAsFactors=FALSE)

library(ggplot2)
library(plyr)
library(grid)

# Create a blank state map. The geom_tile() is included because it allows a legend for all the pie charts to be printed, although it does not
statemap <- ggplot(nlcdtable_long, aes(decimalLongitude,decimalLatitude,fill=nlcdClass)) +
geom_tile() +
borders('state', fill='beige') + coord_map() +
scale_x_continuous(limits=c(-125,-65), expand=c(0,0), name = 'Longitude') +
scale_y_continuous(limits=c(25, 50), expand=c(0,0), name = 'Latitude') +
scale_fill_manual(values = nlcdcolors, name = 'NLCD Classification')

# Create a list of ggplot objects. Each one is the pie chart for each site with all labels removed.
pies <- dlply(nlcdtable_long, .(siteID), function(z)
ggplot(z, aes(x=factor(1), y=prop_plots, fill=nlcdClass)) +
geom_bar(stat='identity', width=1) +
coord_polar(theta='y') +
scale_fill_manual(values = nlcdcolors) +
theme(axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.position="none",
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
plot.background=element_blank()))

# Use the latitude and longitude maxima and minima from the map to calculate the coordinates of each site location on a scale of 0 to 1, within the map panel.
piecoords <- ddply(nlcdtable_long, .(siteID), function(x) with(x, data.frame(
siteID = siteID[1],
x = (decimalLongitude[1]+125)/60,
y = (decimalLatitude[1]-25)/25
)))

# Print the state map.
statemap

# Use a function from the grid package to move into the viewport that contains the plot panel, so that we can plot the individual pies in their correct locations on the map.
downViewport('panel.3-4-3-4')

# Here is the fun part: loop through the pies list. At each iteration, print the ggplot object at the correct location on the viewport. The y coordinate is shifted by half the height of the pie (set at 10% of the height of the map) so that the pie will be centered at the correct coordinate.
for (i in 1:length(pies)) 
  print(pies[[i]], vp=dataViewport(xData=c(-125,-65), yData=c(25,50), clip='off',xscale = c(-125,-65), yscale=c(25,50), x=piecoords$x[i], y=piecoords$y[i]-.06, height=.12, width=.12))

结果如下:

【讨论】:

    【解决方案4】:

    我偶然发现了一个看起来像这样的函数:“mapplots”包中的“add.pie”。

    包中的示例如下。

    plot(NA,NA, xlim=c(-1,1), ylim=c(-1,1) )
    add.pie(z=rpois(6,10), x=-0.5, y=0.5, radius=0.5)
    add.pie(z=rpois(4,10), x=0.5, y=-0.5, radius=0.3)
    

    【讨论】:

      【解决方案5】:

      与 OP 的原始要求略有不同,但这似乎是一个合适的答案/更新。

      如果您想要交互式 Google 地图,从 googleway v2.6.0 开始,您可以在地图图层的 info_windows 内添加图表。

      有关文档和示例,请参阅 ?googleway::google_charts

      library(googleway)
      
      set_key("GOOGLE_MAP_KEY")
      
      ## create some dummy chart data
      markerCharts <- data.frame(stop_id = rep(tram_stops$stop_id, each = 3))
      markerCharts$variable <- c("yes", "no", "maybe")
      markerCharts$value <- sample(1:10, size = nrow(markerCharts), replace = T)
      
      chartList <- list(
        data = markerCharts
        , type = 'pie'
        , options = list(
          title = "my pie"
          , is3D = TRUE
          , height = 240
          , width = 240
          , colors = c('#440154', '#21908C', '#FDE725')
          )
        )
      
      google_map() %>%
        add_markers(
          data = tram_stops
          , id = "stop_id"
          , info_window = chartList
        )
      

      【讨论】:

        猜你喜欢
        • 2021-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-14
        • 2018-12-26
        • 2014-05-12
        相关资源
        最近更新 更多