在R中使用data.tables绘制凸包ggplot

10

我在这里找到了一个使用ggplot和ddply绘制凸包形状的好例子: Drawing outlines around multiple geom_point groups with ggplot

我想尝试类似的东西——创建类似于Ashby Diagram的图表——来练习使用data.table包:

test<-function()
{
library(data.table)
library(ggplot2)

set.seed(1)

这里我定义了一个简单的表格:

dt<-data.table(xdata=runif(15),ydata=runif(15),level=rep(c("a","b","c"),each=5),key="level")

然后我按照层级定义船体位置:

hulls<-dt[,as.integer(chull(.SD)),by=level]
setnames(hulls,"V1","hcol")

那么我的想法是将船体与 dt 合并,以便最终可以操纵船体,使其达到 ggplot 正确的形式(如下所示):

ashby<-ggplot(dt,aes(x=xdata,y=ydata,color=level))+
        geom_point()+
        geom_line()+
        geom_polygon(data=hulls,aes(fill=level))
}

但是无论我尝试如何合并hulls和dt,都会出现错误。例如,merge(hulls,dt)会产生如脚注1所示的错误。

这似乎应该很简单,我相信我只是漏掉了一些显而易见的东西。非常感谢任何指向类似帖子的方向或关于如何为ggplot准备hull的想法。或者如果您认为坚持ddply方法最好,请让我知道。

不希望出现的输出示例:

test<-function(){
    library(data.table)
    library(ggplot2)
    dt<-data.table(xdata=runif(15),ydata=runif(15),level=rep(c("a","b","c"),each=5),key="level")
    set.seed(1)
    hulls<-dt[,as.integer(chull(.SD)),by=level]
    setnames(hulls,"V1","hcol")
    setkey(dt, 'level') #setting the key seems unneeded
    setkey(hulls, 'level')
    hulls<-hulls[dt, allow.cartesian = TRUE]
    ggplot(dt,aes(x=xdata,y=ydata,color=level))+
            geom_point()+
            geom_polygon(data=hulls,aes(fill=level))
}
结果会生成一堆交叉的多边形: 不受欢迎的结果 脚注1:

Error in vecseq(f__, len__, if (allow.cartesian) NULL else as.integer(max(nrow(x), : Join results in 60 rows; more than 15 = max(nrow(x),nrow(i)). Check for duplicate key values in i, each of which join to the same group in x over and over again. If that's ok, try including j and dropping by (by-without-by) so that j runs for each group to avoid the large allocation. If you are sure you wish to proceed, rerun with allow.cartesian=TRUE. Otherwise, please search for this error message in the FAQ, Wiki, Stack Overflow and datatable-help for advice.


我猜这里有一种优雅的方法可行。有了这个能力,我认为该方法可以轻松扩展,从而制作出类似于Ashby图的图表,比如:http://commons.wikimedia.org/wiki/File:Ashby_plot_big.jpg - Docuemada
2
请注意,在自己的函数中调用library可能是不必要的(如果您计划多次调用该函数,则效率低下)。 - Victor K.
1个回答

10

以下是您要做的。生成一些随机数据:

library(ggplot2)
library(data.table)
# You have to set the seed _before_ you generate random data, not after
set.seed(1) 
dt <- data.table(xdata=runif(15), ydata=runif(15), level=rep(c("a","b","c"), each=5),
  key="level")

这就是魔法发生的地方:

hulls <- dt[, .SD[chull(xdata, ydata)], by = level]

绘制结果:

ggplot(dt,aes(x=xdata,y=ydata,color=level)) +
    geom_point() +
    geom_polygon(data = hulls,aes(fill=level,alpha = 0.5))

生成

enter image description here

它之所以有效,是因为 chull 返回一个索引向量,需要从数据中选择以形成凸包。然后我们使用 .SD[...] 对每个单独的数据框进行子集操作,并使用 data.tablelevel 进行连接。


allow.cartesian是一个我之前不知道的技巧,谢谢。就像你的例子一样,我遇到的问题是如何保留层级信息。这个问题的作者:https://dev59.com/sWYq5IYBdhLWcg3whQ40 能够使用ddply创建多个组的凸包形状。我基本上正在尝试使用data.table方法重复作者原始问题的输出。 - Docuemada
请问您所说的“保留层级信息”是什么意思?也就是说,根据上面给出的'hulls'和'dt',您想要输出的是什么? - Victor K.
hulls[dt, allow.cartesian = TRUE] 返回一个包含60行的数据表。然而,对于三个外壳“a”,“b”和“c”,我不需要60个点来描述形状,因为一些点位于形状内部。我将在我的问题中提供一个不希望的输出示例以帮助说明。顺便说一句,allow.cartesian加1分。 - Docuemada
3
hulls <- dt[dt[, .I[chull(xdata, ydata)], by = level]$V1] 可以更有效率地改写为 data.table 不会构建 .SD - mnel
感谢 @mnel - .I 是个不错的技巧,我应该记得更经常使用它。不过,我会保留我的解决方案,因为对于新的 data.table 用户来说,这样更容易解释。 - Victor K.
@VictorK。非常流畅的解决方案,谢谢你,并感谢您的解释。 - Docuemada

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