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

有没有一种方法来创建边缘直方图散点图,就像下面的样本在 ggplot2?在 Matlab 中,它是 scatterhist()函数,并且也存在 R 的等价物。但是,我还没有在 ggplot2中看到它。

scatterplot with marginal histograms

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

 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()

并将它们与发布的 给你函数进行排列。但是长话短说: 有没有办法创建这些图表?

81097 次浏览

这不是一个完全响应的答案,但它是非常简单的。它说明了一种显示边际密度的替代方法,以及如何使用 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

enter image description here

gridExtra包应该可以在这里工作:

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:

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

plot

还有一点,就是为了节省搜索时间。

传说,轴标签,轴文本,勾画使情节彼此漂移,所以你的情节将看起来丑陋和不一致。

您可以通过使用这些主题设置中的一些来纠正这个问题,

+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))

所以结果看起来还不错:

an example

只是 邦德・德斯特的回答的一个非常小的变化,在分布的边际指标的一般精神。

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))

enter image description here

这可能有点晚了,但是我决定为此编写一个包(ggExtra) ,因为它涉及到一些代码,而且编写起来很繁琐。一揽子计划还试图解决一些共同问题,例如确保即使有一个标题或扩大了案文,情节仍然相互衔接。

基本思想与这里给出的答案相似,但是它稍微超出了这个范围。下面是一个例子,说明如何将边缘直方图添加到一个1000点的随机集合中。希望这将使得在未来添加直方图/密度图变得更加容易。

链接到 ggUltra 软件包

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")

enter image description here

由于在比较不同的组时,这种情节没有令人满意的解决方案,所以我编写了一个 功能来完成这项工作。

它适用于分组和未分组的数据,并接受额外的图形参数:

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

enter image description here

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)

enter image description here

我发现这个包(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))

这对我来说很有效:

虹膜集边缘直方图散点图

enter image description here

使用 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
)

enter image description here

或者更有吸引力的(默认情况下) 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)
)

enter image description here

更新:

按照@aickley 的建议,我使用了发展版本来创建情节。

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

像这样

目前,至少有一个 CRAN 软件包可以利用其边缘直方图进行散点图的绘制。

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

Sample plot from scatterHist

为了通过@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)
)

scatterplot with marginal histograms

要绘制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)
)

enter image description here

另一种使用 ggpubrcowplot的解决方案,但是在这里我们使用 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)

enter image description here

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

正如评论中指出的那样,使用 gridUltra 的最受欢迎的答案是可行的,但是对齐轴是很困难的。现在可以使用 ggUltra 包中的 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")

enter image description here

我尝试了这些选项,但是不满意的结果或凌乱的代码,需要使用到那里。幸运的是,Thomas Lin Pedersen 刚刚开发了一个名为 拼凑起来的软件包,它可以以一种非常优雅的方式完成工作。

如果要创建具有边缘直方图的散点图,首先必须分别创建这三个图。

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()在右上角添加了一个空的图形。所有其他的参数都应该是不言而喻的。

enter image description here

由于直方图在很大程度上取决于所选择的带宽,因此人们可能会争辩说更喜欢密度图。通过一些小的修改,例如眼球追踪数据就可以得到一个漂亮的图表。

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))

enter image description here

尽管目前还没有提供数据,但基本原则应该是明确的。