ggplot2中具有边缘直方图的散点图


137

是否有办法像下面的示例中那样用边际直方图创建散点图ggplot2?在Matlab中,它是scatterhist()函数,R也存在等效项。但是,我还没有在ggplot2上看到它。

具有边际直方图的散点图

我通过创建单个图形开始尝试,但是不知道如何正确排列它们。

 require(ggplot2)
 x<-rnorm(300)
 y<-rt(300,df=2)
 xy<-data.frame(x,y)
     xhist <- qplot(x, geom="histogram") + scale_x_continuous(limits=c(min(x),max(x))) + opts(axis.text.x = theme_blank(), axis.title.x=theme_blank(), axis.ticks = theme_blank(), aspect.ratio = 5/16, axis.text.y = theme_blank(), axis.title.y=theme_blank(), background.colour="white")
     yhist <- qplot(y, geom="histogram") + coord_flip() + opts(background.fill = "white", background.color ="black")

     yhist <- yhist + scale_x_continuous(limits=c(min(x),max(x))) + opts(axis.text.x = theme_blank(), axis.title.x=theme_blank(), axis.ticks = theme_blank(), aspect.ratio = 16/5, axis.text.y = theme_blank(), axis.title.y=theme_blank() )


     scatter <- qplot(x,y, data=xy)  + scale_x_continuous(limits=c(min(x),max(x))) + scale_y_continuous(limits=c(min(y),max(y)))
none <- qplot(x,y, data=xy) + geom_blank()

并按此处发布的功能进行整理。但总而言之:有没有一种创建这些图的方法?


@DWin对,谢谢-但是我认为这几乎是我在问题中提供的解决方案。但是,我喜欢geom_rag()在下面给您的想法非常多!
勒布

1
从最近的博客文章,具有相同的话题:blog.mckuhn.de/2009/09/learning-ggplot2-2d-plot-with.html看起来也相当不错:)
勒布

图形库的新网站是:gallery.r-enthusiasts.com
IRTFM,2013年

@Seb,如果您认为有道理,可以考虑将ggExtra软件包的“可接受的答案”更改为该值
DeanAttali '16

Answers:


93

gridExtra软件包应该在这里工作。首先制作每个ggplot对象:

hist_top <- ggplot()+geom_histogram(aes(rnorm(100)))
empty <- ggplot()+geom_point(aes(1,1), colour="white")+
         theme(axis.ticks=element_blank(), 
               panel.background=element_blank(), 
               axis.text.x=element_blank(), axis.text.y=element_blank(),           
               axis.title.x=element_blank(), axis.title.y=element_blank())

scatter <- ggplot()+geom_point(aes(rnorm(100), rnorm(100)))
hist_right <- ggplot()+geom_histogram(aes(rnorm(100)))+coord_flip()

然后使用grid.arrange函数:

grid.arrange(hist_top, empty, scatter, hist_right, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

情节


6
1+用于演示放置,但是如果您希望内部散点图与边际直方图“对齐”,则不应重新进行随机采样。
IRTFM

1
你是对的。但是,它们是从相同的分布中采样的,因此,边际直方图在理论上应与散点图匹配。
oeo4b

8
在“理论”中,它们将渐近地“匹配”。实际上,它们匹配的次数非常小。使用提供的示例xy <- data.frame(x=rnorm(300), y=rt(300,df=2) )data=xy在ggplot调用中使用非常容易。
IRTFM

7
我不推荐这种解决方案,因为绘图轴通常不完全对齐。希望ggplot2的未来版本能够更轻松地对齐轴,甚至允许在绘图面板的侧面进行自定义注释(例如,网格中自定义的辅助轴功能)。
baptiste

9
不,他们一般不会。ggplot2当前输出一个可变的面板宽度,该宽度根据轴标签的范围等而变化。请查看ggExtra :: align.plots以查看当前对齐轴所需的hack类型。
baptiste

115

这不是一个完全响应的答案,但是非常简单。它说明了显示边际密度的另一种方法,以及如何将alpha级别用于支持透明性的图形输出:

scatter <- qplot(x,y, data=xy)  + 
         scale_x_continuous(limits=c(min(x),max(x))) + 
         scale_y_continuous(limits=c(min(y),max(y))) + 
         geom_rug(col=rgb(.5,0,0,alpha=.2))
scatter

在此处输入图片说明


5
这是显示密度的一种有趣方式。感谢您添加此答案。:)
Michelle

21
应该注意的是,这种方法比放置边际直方图更为普遍。实际上,在我从未见过的带有边际直方图的文章中,有地毯图是很常见的。
徐望

非常有趣且直观的替代答案!而且非常简单!难怪投票比正确答案还要多。我的理解是,这本质上是一维热图:无论拥挤的地方,地毯都更黑。我唯一担心的是,热图的分辨率不如直方图高。例如。当地块很小时,所有地毯都将被挤压在一起,这使人很难察觉其分布。虽然直方图不受限制。谢谢你的主意!
HongboZhu

94

这可能有点晚了,但是我决定为此制作一个程序包(ggExtra),因为它涉及一些代码并且编写起来很繁琐。该软件包还尝试解决一些常见问题,例如确保即使有标题或文本被放大,地块仍将彼此对齐。

基本思想与此处给出的答案相似,但超出了此范围。这是一个如何将边际直方图添加到1000个点的随机集合的示例。希望这使得将来添加直方图/密度图变得更加容易。

链接到ggExtra包

library(ggplot2)
df <- data.frame(x = rnorm(1000, 50, 10), y = rnorm(1000, 50, 10))
p <- ggplot(df, aes(x, y)) + geom_point() + theme_classic()
ggExtra::ggMarginal(p, type = "histogram")

在此处输入图片说明


1
非常感谢您的包裹。开箱即用!
heroxbd 2015年

使用此包装是否可以绘制按颜色分组的对象的边际密度图?
GegznaV

不,它没有这种逻辑
DeanAttali

1
@jjrr我不确定什么是行不通的,还有什么问题,但是github上最近有一个关于在笔记本中渲染的问题,还有一个解决方案,这可能对github.com/daattali/
DeanAttali

1
@GegznaV,如果您仍在寻找按颜色分组边际密度图的方法,则可以使用ggExtra 0.9:ggMarginal(p,type =“ density”,size = 5,groupColour = TRUE)
MartineJ

46

另外,只是为我们之后的人们节省了一些搜索时间。

图例,坐标轴标签,坐标轴文本,刻度线使图线彼此偏离,因此您的图线看起来难看且不一致。

您可以使用其中一些主题设置来更正此问题,

+theme(legend.position = "none",          
       axis.title.x = element_blank(),
       axis.title.y = element_blank(),
       axis.text.x = element_blank(),
       axis.text.y = element_blank(), 
       plot.margin = unit(c(3,-5.5,4,3), "mm"))

并对齐刻度,

+scale_x_continuous(breaks = 0:6,
                    limits = c(0,6),
                    expand = c(.05,.05))

因此结果看起来不错:

一个例子


3
看到这个以更可靠的解决方案来对齐绘图面板
baptiste 2014年

是。我的答案已经过时,请使用建议的@baptiste解决方案。
Lorinc Nyitrai,2015年

@LorincNyitrai您能否分享生成此图的代码。我也有一个条件,我想在ggplot2中制作一个精确召回散点图,其中有2组的边际分布,但我无法进行2组的边际分布。谢谢
新手

@Newbie,这个答案是3岁,尽可能的过时。使用rdocumentation.org/packages/gtable/versions/0.2.0/topics/gtable或类似的名称。
Lorinc Nyitrai

29

按照边际分布指标的一般精神,对BondedDust的答案仅作很小的改动

爱德华·塔夫特Edward Tufte)将这种地毯图的使用称为“点划线图”,并且在VDQI中有一个示例,该示例使用轴线指示每个变量的范围。在我的示例中,轴标签和网格线还指示数据的分布。标签位于Tukey的五个数字摘要的值(最小值,下铰链,中位数,上铰链,最大值)上,从而快速显示每个变量的分布范围。

因此,这五个数字是箱线图的数字表示。这有点棘手,因为网格线间距不均匀表明轴具有非线性比例(在此示例中为线性)。也许最好省略网格线或将其强制放置在常规位置,仅让标签显示五个数字摘要即可。

x<-rnorm(300)
y<-rt(300,df=10)
xy<-data.frame(x,y)

require(ggplot2); require(grid)
# make the basic plot object
ggplot(xy, aes(x, y)) +        
  # set the locations of the x-axis labels as Tukey's five numbers   
  scale_x_continuous(limit=c(min(x), max(x)), 
                     breaks=round(fivenum(x),1)) +     
  # ditto for y-axis labels 
  scale_y_continuous(limit=c(min(y), max(y)),
                     breaks=round(fivenum(y),1)) +     
  # specify points
  geom_point() +
  # specify that we want the rug plot
  geom_rug(size=0.1) +   
  # improve the data/ink ratio
  theme_set(theme_minimal(base_size = 18))

在此处输入图片说明


12

由于在比较不同的组时没有令人满意的解决方案,因此我编写了一个函数来执行此操作。

它适用于分组数据和未分组数据,并接受其他图形参数:

marginal_plot(x = iris$Sepal.Width, y = iris$Sepal.Length)

在此处输入图片说明

marginal_plot(x = Sepal.Width, y = Sepal.Length, group = Species, data = iris, bw = "nrd", lm_formula = NULL, xlab = "Sepal width", ylab = "Sepal length", pch = 15, cex = 0.5)

在此处输入图片说明


9

我发现该软件包(ggpubr)对于该问题似乎非常有效,并且考虑了显示数据的几种可能性。

包的链接在这里,在此链接中,您将找到一个使用它的不错的教程。为了完整起见,我附上我复制的示例之一。

我首先安装了该软件包(需要安装devtools

if(!require(devtools)) install.packages("devtools")
devtools::install_github("kassambara/ggpubr")

对于显示不同组的不同直方图的特定示例,它提到ggExtra:“的一个局限性ggExtra是它无法处理散点图和边际图中的多个组。在下面的R代码中,我们提供了解决方案cowplot。” 就我而言,我必须安装后一个软件包:

install.packages("cowplot")

我遵循了这段代码:

# Scatter plot colored by groups ("Species")
sp <- ggscatter(iris, x = "Sepal.Length", y = "Sepal.Width",
            color = "Species", palette = "jco",
            size = 3, alpha = 0.6)+
border()                                         
# Marginal density plot of x (top panel) and y (right panel)
xplot <- ggdensity(iris, "Sepal.Length", fill = "Species",
               palette = "jco")
yplot <- ggdensity(iris, "Sepal.Width", fill = "Species", 
               palette = "jco")+
rotate()
# Cleaning the plots
sp <- sp + rremove("legend")
yplot <- yplot + clean_theme() + rremove("legend") 
xplot <- xplot + clean_theme() + rremove("legend")
# Arranging the plot using cowplot
library(cowplot)
plot_grid(xplot, NULL, sp, yplot, ncol = 2, align = "hv", 
      rel_widths = c(2, 1), rel_heights = c(1, 2))

对我来说很好

虹膜设置边际直方图散点图

在此处输入图片说明


您要怎么做才能使中间的地块变成正方形?
JAQuent

点的形状是什么意思?尝试添加的参数shape = 19ggscatter此处
Alf Pascu,

7

您可以使用ggstatsplot使用边际直方图轻松创建有吸引力的散点图(它也可以拟合并描述模型):

data(iris)

library(ggstatsplot)

ggscatterstats(
  data = iris,                                          
  x = Sepal.Length,                                                  
  y = Sepal.Width,
  xlab = "Sepal Length",
  ylab = "Sepal Width",
  marginal = TRUE,
  marginal.type = "histogram",
  centrality.para = "mean",
  margins = "both",
  title = "Relationship between Sepal Length and Sepal Width",
  messages = FALSE
)

在此处输入图片说明

或更具吸引力(默认情况下)ggpubr

devtools::install_github("kassambara/ggpubr")
library(ggpubr)

ggscatterhist(
  iris, x = "Sepal.Length", y = "Sepal.Width",
  color = "Species", # comment out this and last line to remove the split by species
  margin.plot = "histogram", # I'd suggest removing this line to get density plots
  margin.params = list(fill = "Species", color = "black", size = 0.2)
)

在此处输入图片说明

更新:

正如@aickley所建议的那样,我使用了开发版本来创建情节。


1
y轴上的直方图是错误的,因为它只是x轴上的直方图的副本。该问题仅在github.com/kassambara/ggpubr/issues/85最近才得到修复。
aickley

7

这是一个老问题,但是我认为在这里发布更新会很有用,因为我最近遇到了同样的问题(感谢Stefanie Mueller的帮助!)。

如注释中所指出的,使用gridExtra提出的最合适的答案是可行的,但是对齐轴非常困难。现在可以使用ggExtra软件包中的命令ggMarginal来解决此问题,如下所示:

#load packages
library(tidyverse) #for creating dummy dataset only
library(ggExtra)

#create dummy data
a = round(rnorm(1000,mean=10,sd=6),digits=0)
b = runif(1000,min=1.0,max=1.6)*a
b = b+runif(1000,min=9,max=15)

DummyData <- data.frame(var1 = b, var2 = a) %>% 
  filter(var1 > 0 & var2 > 0)

#plot
p = ggplot(DummyData, aes(var1, var2)) + geom_point(alpha=0.3)
ggMarginal(p, type = "histogram")

在此处输入图片说明


刚意识到这已由原始ggExtra软件包开发人员发布在另一个答案中。由于上述原因,建议您改成接受的答案!
维多利亚·欧阳

6

我尝试了这些选项,但对结果或到达该位置所需的混乱代码不满意。幸运的是,托马斯·林·佩德森(Thomas Lin Pedersen)刚刚开发了一个名为patchwork的软件包,它以一种非常优雅的方式完成了工作。

如果要使用边际直方图创建散点图,则首先必须分别创建这三个图。

library(ggplot2)

x <- rnorm(300)
y <- rt(300, df = 2)
xy <- data.frame(x, y)

plot1 <- ggplot(xy, aes(x = x, y = y)) + 
  geom_point() 

dens1 <- ggplot(xy, aes(x = x)) + 
  geom_histogram(color = "black", fill = "white") + 
  theme_void()

dens2 <- ggplot(xy, aes(x = y)) + 
  geom_histogram(color = "black", fill = "white") + 
  theme_void() + 
  coord_flip()

剩下要做的唯一一件事就是用简单的方法添加这些图,+并使用函数指定布局plot_layout()

library(patchwork)

dens1 + plot_spacer() + plot1 + dens2 + 
  plot_layout(
    ncol = 2, 
    nrow = 2, 
    widths = c(4, 1),
    heights = c(1, 4)
  ) 

该函数plot_spacer()在右上角添加一个空白图。所有其他论点应该是不言自明的。

在此处输入图片说明

由于直方图很大程度上取决于所选的二进制宽度,因此人们可能会争辩说更喜欢密度图。通过一些小的修改,例如对于眼睛跟踪数据,将获得漂亮的图。

library(ggpubr)

plot1 <- ggplot(df, aes(x = Density, y = Face_sum, color = Group)) + 
  geom_point(aes(color = Group), size = 3) + 
  geom_point(shape = 1, color = "black", size = 3) + 
  stat_smooth(method = "lm", fullrange = TRUE) +
  geom_rug() + 
  scale_y_continuous(name = "Number of fixated faces", 
                     limits = c(0, 205), expand = c(0, 0)) + 
  scale_x_continuous(name = "Population density (lg10)", 
                     limits = c(1, 4), expand = c(0, 0)) + 
  theme_pubr() +
  theme(legend.position = c(0.15, 0.9)) 

dens1 <- ggplot(df, aes(x = Density, fill = Group)) + 
  geom_density(alpha = 0.4) + 
  theme_void() + 
  theme(legend.position = "none")

dens2 <- ggplot(df, aes(x = Face_sum, fill = Group)) + 
  geom_density(alpha = 0.4) + 
  theme_void() + 
  theme(legend.position = "none") + 
  coord_flip()

dens1 + plot_spacer() + plot1 + dens2 + 
  plot_layout(ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4))

在此处输入图片说明

尽管此时尚未提供数据,但基本原理应明确。


4

要以@ alf-pascu的答案为基础,手动设置每个图并对其进行排列cowplot就相对于主要图和边缘图而言都具有很大的灵活性(与其他解决方案相比)。按组分配就是一个例子。将主图更改为2D密度图是另一种方法。

以下创建带有(正确对齐的)边际直方图的散点图。

library("ggplot2")
library("cowplot")

# Set up scatterplot
scatterplot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3, alpha = 0.6) +
  guides(color = FALSE) +
  theme(plot.margin = margin())


# Define marginal histogram
marginal_distribution <- function(x, var, group) {
  ggplot(x, aes_string(x = var, fill = group)) +
    geom_histogram(bins = 30, alpha = 0.4, position = "identity") +
    # geom_density(alpha = 0.4, size = 0.1) +
    guides(fill = FALSE) +
    theme_void() +
    theme(plot.margin = margin())
}

# Set up marginal histograms
x_hist <- marginal_distribution(iris, "Sepal.Length", "Species")
y_hist <- marginal_distribution(iris, "Sepal.Width", "Species") +
  coord_flip()

# Align histograms with scatterplot
aligned_x_hist <- align_plots(x_hist, scatterplot, align = "v")[[1]]
aligned_y_hist <- align_plots(y_hist, scatterplot, align = "h")[[1]]

# Arrange plots
plot_grid(
  aligned_x_hist
  , NULL
  , scatterplot
  , aligned_y_hist
  , ncol = 2
  , nrow = 2
  , rel_heights = c(0.2, 1)
  , rel_widths = c(1, 0.2)
)

具有边际直方图的散点图

要绘制2D密度图,只需更改主图即可。

# Set up 2D-density plot
contour_plot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  stat_density_2d(aes(alpha = ..piece..)) +
  guides(color = FALSE, alpha = FALSE) +
  theme(plot.margin = margin())

# Arrange plots
plot_grid(
  aligned_x_hist
  , NULL
  , contour_plot
  , aligned_y_hist
  , ncol = 2
  , nrow = 2
  , rel_heights = c(0.2, 1)
  , rel_widths = c(1, 0.2)
)

在此处输入图片说明


3

使用ggpubr和的另一种解决方案cowplot,但是在这里我们使用创建图表,cowplot::axis_canvas并使用将它们添加到原始图表中cowplot::insert_xaxis_grob

library(cowplot) 
library(ggpubr)

# Create main plot
plot_main <- ggplot(faithful, aes(eruptions, waiting)) +
  geom_point()

# Create marginal plots
# Use geom_density/histogram for whatever you plotted on x/y axis 
plot_x <- axis_canvas(plot_main, axis = "x") +
  geom_density(aes(eruptions), faithful)
plot_y <- axis_canvas(plot_main, axis = "y", coord_flip = TRUE) +
  geom_density(aes(waiting), faithful) +
  coord_flip()

# Combine all plots into one
plot_final <- insert_xaxis_grob(plot_main, plot_x, position = "top")
plot_final <- insert_yaxis_grob(plot_final, plot_y, position = "right")
ggdraw(plot_final)

在此处输入图片说明


2

如今,至少有一个CRAN程序包使散点图具有其边缘直方图。

library(psych)
scatterHist(rnorm(1000), runif(1000))

来自scatterHist的样本图


0

您可以使用交互式形式,ggExtra::ggMarginalGadget(yourplot) 并在箱形图,小提琴图,密度图和直方图之间进行选择。

像那样

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.