为什么geom_tile只能绘制我的数据子集而不能绘制更多的数据?

4
我正在尝试绘制一幅地图,但我无法弄清楚为什么以下内容不起作用:
这是一个最简示例。
testdf <- structure(list(x = c(48.97, 44.22, 44.99, 48.87, 43.82, 43.16, 38.96, 38.49, 44.98, 43.9), y = c(-119.7, -113.7, -109.3, -120.6,  -109.6, -121.2, -114.2, -118.9, -109.7, -114.1), z = c(0.001216,  0.001631, 0.001801, 0.002081, 0.002158, 0.002265, 0.002298, 0.002334, 0.002349, 0.00249)), .Names = c("x", "y", "z"), row.names = c(NA, 10L), class = "data.frame")

这适用于1-8行:
ggplot(data = testdf[1,], aes(x,y,fill = z)) + geom_tile()
ggplot(data = testdf[1:8,], aes(x,y,fill = z)) + geom_tile()

但是不适用于9行:

ggplot(data = testdf[1:9,], aes(x,y,fill = z)) + geom_tile()

我正在寻找一种在非规则网格上绘制数据的方法。并不一定要使用geom_tile,任何关于点的空间填充插值都可以。

完整数据集可以在gist上获得。

上面的testdf是完整数据集的一个小子集,这是美国高分辨率光栅数据集(>7500行)。

require(RCurl) # requires libcurl; sudo apt-get install libcurl4-openssl-dev
tmp <- getURL("https://gist.github.com/raw/4635980/f657dcdfab7b951c7b8b921b3a109c7df1697eb8/test.csv")
testdf <- read.csv(textConnection(x))

我尝试过的方法:

  1. using geom_point works, but does not have the desired effect:

    ggplot(data = testdf, aes(x,y,color=z)) + geom_point()
    
  2. if I convert either x or y to a vector 1:10, the plot works as expected:

    newdf <- transform(testdf, y =1:10)
    
    ggplot(data = newdf[1:9,], aes(x,y,fill = z)) + geom_tile()
    
    newdf <- transform(testdf, x =1:10)
    ggplot(data = newdf[1:9,], aes(x,y,fill = z)) + geom_tile()
    

sessionInfo()R version 2.15.2 (2012-10-26) Platform: x86_64-pc-linux-gnu (64-bit)


> attached base packages: [1] stats     graphics  grDevices utils    
> datasets  methods   base     

> other attached packages: [1] reshape2_1.2.2 maps_2.3-0    
> betymaps_1.0   ggmap_2.2      ggplot2_0.9.3 

> loaded via a namespace (and not attached):  [1] colorspace_1.2-0   
> dichromat_1.2-4     digest_0.6.1        grid_2.15.2        
> gtable_0.1.2        labeling_0.1         [7] MASS_7.3-23        
> munsell_0.4         plyr_1.8            png_0.1-4          
> proto_0.3-10        RColorBrewer_1.0-5  [13] RgoogleMaps_1.2.0.2
> rjson_0.2.12        scales_0.2.3        stringr_0.6.2      
> tools_2.15.2

你有更多关于数据来自的光栅的信息吗?例如投影信息。 - Simon O'Hanlon
@SimonO101 它们是在一个30x30公里的网格上生成的。 - Abe
好的。您需要对数据进行一些重新采样。由于点不是均匀分布的,因此您无法使用geom_rastergeom_tile。请参阅我的答案,了解详细信息和使用geom_raster的解决方案。 - Simon O'Hanlon
以下代码在你的系统上能运行吗? - Simon O'Hanlon
Abe - 我按照你正确建议的修改进行了申请,但在我接受之前被审核人员拒绝了!你是完全正确的,这个脚本需要RCurl。 - Simon O'Hanlon
@SimonO101 是的 - 它运行得很好。感谢您的回答!但我仍然卡住了(并将在您的回答下面发表评论)。 - Abe
4个回答

11
由于geom_tile()(或更合适的geom_raster())这两个几何图形要求你的瓦片间距是均匀的,而它们不是均匀的,所以你不能使用它们。你需要将你的数据转换为点,并对其进行重新采样,生成一个均匀间距的栅格,然后使用geom_raster()绘制。你需要接受你需要稍微重新采样原始数据才能按照你的意愿进行绘制。
你还应该阅读有关地图投影的raster:::projectionrgdal:::spTransform的更多信息。
require( RCurl )
require( raster )
require( sp )
require( ggplot2 )
tmp <- getURL("https://gist.github.com/geophtwombly/4635980/raw/f657dcdfab7b951c7b8b921b3a109c7df1697eb8/test.csv")
testdf <- read.csv(textConnection(tmp))
spdf <- SpatialPointsDataFrame( data.frame( x = testdf$y , y = testdf$x ) , data = data.frame( z = testdf$z ) )

# Plotting the points reveals the unevenly spaced nature of the points
spplot(spdf)

在此输入图片描述

# You can see the uneven nature of the data even better here via the moire pattern
plot(spdf)

输入图像描述

# Make an evenly spaced raster, the same extent as original data
e <- extent( spdf )

# Determine ratio between x and y dimensions
ratio <- ( e@xmax - e@xmin ) / ( e@ymax - e@ymin )

# Create template raster to sample to
r <- raster( nrows = 56 , ncols = floor( 56 * ratio ) , ext = extent(spdf) )
rf <- rasterize( spdf , r , field = "z" , fun = mean )

# Attributes of our new raster (# cells quite close to original data)
rf
class       : RasterLayer 
dimensions  : 56, 135, 7560  (nrow, ncol, ncell)
resolution  : 0.424932, 0.4248191  (x, y)
extent      : -124.5008, -67.13498, 25.21298, 49.00285  (xmin, xmax, ymin, ymax)

# We can then plot this using `geom_tile()` or `geom_raster()`
rdf <- data.frame( rasterToPoints( rf ) )    
ggplot( NULL ) + geom_raster( data = rdf , aes( x , y , fill = layer ) )

在此输入图像描述

# And as the OP asked for geom_tile, this would be...
ggplot( NULL ) + geom_tile( data = rdf , aes( x , y , fill = layer ) , colour = "white" )

在此输入图片描述

当然,我应该补充一下这个数据是没有意义的。你真正需要做的是获取SpatialPointsDataFrame,为其分配正确的投影信息,然后通过spTransform将其转换为经纬度坐标,并将转换后的点栅格化。实际上,你需要更多有关栅格数据的信息。你现在得到的是一个近似值,但最终它并不是数据的真实反映。


提前道歉,我有些迟钝,需要阅读一下,但我不理解最后一部分。为什么数据是无意义的?与重采样相关的不确定性很小,并且数据集具有纬度和经度,因此,例如,我可以看到中西部比西海岸的值更高。项目除了绘图所需的信息之外还添加了什么信息?rf RasterLayer对象中使用的投影是否错误?关于这些数据还有更多信息,请参见gis.SE。我在尝试分配gridded() <- TRUE时卡住了。 - Abe
好的,这并不完全没有意义,但实际上我们所做的是在第一张图片上叠加了一个常规网格,并根据底层图片中的位置为常规网格分配值。这样做是不正确的。通过重新投影转换数据将导致一些数据点根据其纬度和经度的函数发生更大的位移。如果您不关心准确性,只想得到一个概览,那么也许您可以使用这种方法,但我认为在出版中无法进行很好的防御。也许@PaulHiemstra可以再详细解释一下? - Simon O'Hanlon
@SimonO101 是的,你做到了 - 感谢你的帮助。鉴于生成地图所使用的假设(它不是“数据”,而是模型输出)以及颜色比例尺的有限分辨率,我认为在映射过程中引入一些误差是可以被证明的 - 我的一般经验法则是忽略总不确定性的 <5%左右的东西。 - Abe

9
这不是关于geom_tile()问题的答案,而是另一种绘制数据的方法。
由于您拥有30公里网格的x和y坐标(我假设这个网格的中心),因此您可以使用geom_point()来绘制数据。您应该选择适当的shape=值。形状15将绘制矩形。
另一个问题是x和y值 - 在绘制数据时,它们应该作为x=yy=x绘制,以对应纬度和经度。 coord_equal()将确保正确的纵横比(我在网络上找到了这个比率的示例解决方案)。
ggplot(data = testdf, aes(y,x,colour=z)) + geom_point(shape=15)+
  coord_equal(ratio=1/cos(mean(testdf$x)*pi/180))

enter image description here


4

答案:

数据已绘制,但只是非常小。


从这里开始:

"Tile plot as densely as possible, assuming that every tile is the same size.

考虑以下这个情节。
ggplot(data = testdf[1:2,], aes(x,y,fill = z)) + geom_tile()

enter image description here

上图中有两个瓦片。 geom_tile 尝试在每个瓦片的大小相同的情况下使绘图尽可能密集。 在这里,我们可以制作出两个这样大的瓷砖而不重叠,足以容纳 4 个瓦片。

试试以下绘图,并查看结果图告诉您什么:

df1 <- data.frame(x=c(1:3),y=(1:3))
#     df1
#  x   y
#1 1   1
#2 2   2
#3 3   3
ggplot(data = df1[1,], aes(x,y)) + geom_tile()   
ggplot(data = df1[1:2,], aes(x,y)) + geom_tile() 
ggplot(data = df1[1:3,], aes(x,y)) + geom_tile()

与这个例子相比较:
 df2 <- data.frame(x=c(1:3),y=c(1,20,300))
 df2
 # x   y
#1 1   1
#2 2  20
#3 3 300

 ggplot(data = df2[1,], aes(x,y)) + geom_tile()
 ggplot(data = df2[1:2,], aes(x,y)) + geom_tile()
 ggplot(data = df2[1:3,], aes(x,y)) + geom_tile()

请注意,前两个图对于df1df2是相同的,但是df2的第三个图不同。这是因为我们可以使瓷砖的最大尺寸位于(x[1],y[1])和(x[2],y[2])之间。再多就会重叠,这样留下了许多空间,在y=300处的最后第3个瓷砖。

geom_tile还有一个width参数,尽管我不确定这在这里有多明智。您确定不想使用其他选项来处理这种稀疏数据吗?

(您的完整数据仍将绘制出来:请参见ggplot(data = testdf, aes(x,y)) + geom_tile(width=1000))


1
是的,但也许您可以添加一些关于geom_tile如何根据点之间的距离选择瓷砖大小的解释...? - joran
你还有什么其他建议?这里只提供了最简单的示例;完整的数据集在这里:https://www.betydb.org//miscanthusyield.csv - Abe
没错,这是一个7500行的美国栅格图,网格间距为30公里;我只是在自己寻找答案时简化了问题,并为了清晰起见删除了先前的评论和链接,并将完整的数据集示例添加到了我的问题中。我会尝试使用“width”并回复您。我认为问题可能是投影的问题... - Abe
我在这个问题上设置了赏金,寻求绘制我拥有的数据的解决方案。 - Abe

1
如果想使用 geom_tile,我认为您需要先进行聚合:
# NOTE: tmp.csv downloaded from https://gist.github.com/geophtwombly/4635980/raw/f657dcdfab7b951c7b8b921b3a109c7df1697eb8/test.csv
testdf <- read.csv("~/Desktop/tmp.csv") 

# combine x,y coordinates by rounding
testdf$x2 <- round(testdf$x, digits=0)
testdf$y2 <- round(testdf$y, digits=0)

# aggregate on combined coordinates
library(plyr)
testdf <- ddply(testdf, c("x2", "y2"), summarize,
                z = mean(z))

# plot aggregated data using geom_tile
ggplot(data = testdf, aes(y2,x2,fill=z)) +
  geom_tile() +
  coord_equal(ratio=1/cos(mean(testdf$x2)*pi/180)) # copied from @Didzis Elferts answer--nice!

一旦我们完成这些步骤,我们可能会得出结论,即像@Didzis Elferts建议的那样,geom_point()更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接