web-dev-qa-db-fra.com

Nuage de points avec histogrammes marginaux dans ggplot2

Existe-t-il un moyen de créer des diagrammes de dispersion avec des histogrammes marginaux, comme dans l'exemple ci-dessous dans ggplot2? Dans Matlab, il s’agit de la fonction scatterhist() et il existe également des équivalents pour R. Cependant, je ne l'ai pas vu pour ggplot2.

scatterplot with marginal histograms

J'ai commencé par essayer de créer les graphiques simples, mais je ne sais pas comment les organiser correctement.

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

et en les organisant avec la fonction postée ici . Mais pour résumer, existe-t-il un moyen de créer ces graphiques?

128
Seb

Le paquetage gridExtra devrait fonctionner ici. Commencez par créer chacun des objets 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()

Ensuite, utilisez la fonction grid.arrange:

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

plot

88
oeo4b

Ce n’est pas une réponse totalement réactive, mais c’est très simple. Il illustre une autre méthode d'affichage des densités marginales et explique également comment utiliser les niveaux alpha pour une sortie graphique prenant en charge la transparence:

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

110
42-

C’est peut-être un peu tard, mais j’ai décidé de créer un paquet (ggExtra) car c’est un peu de code et il peut être fastidieux d’écrire. Le paquet tente également de résoudre un problème courant, comme s’assurer que même s’il existe un titre ou que le texte soit agrandi, les tracés restent alignés les uns sur les autres.

L'idée de base est similaire à celle apportée par les réponses ici, mais elle va un peu plus loin. Voici un exemple d’ajout d’histogrammes marginaux à un ensemble aléatoire de 1 000 points. J'espère que cela facilitera l'ajout d'histogrammes/graphiques de densité à l'avenir.

Lien vers le paquet 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")

enter image description here

83
DeanAttali

Un ajout, juste pour économiser du temps de recherche pour ceux qui le font après nous.

Légendes, étiquettes d'axe, textes d'axe, graduations font que les parcelles s'éloignent les unes des autres. Votre parcelle aura alors une apparence laide et incohérente.

Vous pouvez corriger cela en utilisant certains de ces paramètres de thème,

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

et aligner les échelles,

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

alors les résultats auront l'air OK:

an example

44
Lorinc Nyitrai

Juste une très petite variation de réponse de BondedDust , dans l’esprit général des indicateurs de distribution marginaux.

Edward Tufte a qualifié cette utilisation de "rug plots" de "tracé point-dash" et présente dans VDQI un exemple d'utilisation des lignes d'axe pour indiquer la plage de chaque variable. Dans mon exemple, les étiquettes des axes et les lignes de la grille indiquent également la distribution des données. Les étiquettes sont situées aux valeurs de résumé des cinq chiffres de Tukey (minimum, charnière inférieure, médiane, charnière supérieure, maximum), ce qui donne une impression rapide de la dispersion de chaque variable.

Ces cinq nombres sont donc une représentation numérique d'une boîte à moustaches. C'est un peu délicat, car les lignes de la grille irrégulièrement espacées suggèrent que les axes ont une échelle non linéaire (dans cet exemple, ils sont linéaires). Peut-être serait-il préférable d'omettre les lignes de la grille ou de les forcer à se trouver à des emplacements réguliers et de laisser les étiquettes afficher le résumé des cinq chiffres.

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

28
Ben

Comme il n’existait pas de solution satisfaisante pour ce type d’intrigue lorsqu’on comparait différents groupes, j’ai écrit un fonction pour le faire.

Il fonctionne à la fois pour les données groupées et non groupées et accepte des paramètres graphiques supplémentaires:

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

10
Hav0k

J'ai trouvé le package (ggpubr) qui semble très bien fonctionner pour résoudre ce problème et qui envisage plusieurs possibilités pour afficher les données.

Le lien vers le package est ici , et dans ce lien , vous trouverez un tutoriel de Nice pour l'utiliser. Par souci d'exhaustivité, je joins un des exemples que j'ai reproduit.

J'ai d'abord installé le paquet (il nécessite devtools)

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

Pour l'exemple particulier d'affichage d'histogrammes différents pour différents groupes, il est mentionné en relation avec ggExtra: "Une limitation de ggExtra est qu'il ne peut pas gérer plusieurs groupes dans le diagramme de dispersion et Dans le code R ci-dessous, nous proposons une solution utilisant le package cowplot. " Dans mon cas, j'ai dû installer le dernier paquet:

install.packages("cowplot")

Et j'ai suivi ce morceau de code:

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

Ce qui a bien fonctionné pour moi:

diagramme de dispersion des histogrammes marginaux de l'iris

enter image description here

7
Alf Pascu

Vous pouvez facilement créer des diagrammes de dispersion attrayants avec des histogrammes marginaux en utilisant ggstatsplot (il conviendra également de décrire un modèle):

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

Ou légèrement plus attrayant (par défaut) 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

UPDATE:

Comme suggéré par @aickley, j'ai utilisé la version de développement pour créer l'intrigue.

5
epo3

Pour répondre à la question posée par @ alf-pascu, la configuration manuelle de chaque parcelle et son organisation avec cowplot confère une grande souplesse quant aux parcelles principales et aux parcelles marginales (par rapport à certaines des autres solutions). . Les distributions par groupes en sont un exemple. Changer le tracé principal en tracé à densité 2D en est un autre.

Ce qui suit crée un diagramme de dispersion avec des histogrammes marginaux (correctement alignés).

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

Pour tracer un tracé de densité 2D à la place, modifiez simplement le tracé principal.

# 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

2
crsh

Une autre solution utilisant ggpubr et cowplot, mais ici nous créons des tracés en utilisant cowplot::axis_canvas et les ajouter à la parcelle d'origine avec 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

2
PoGibas

C'est une vieille question, mais j'ai pensé qu'il serait utile de poster une mise à jour ici car j'ai rencontré ce même problème récemment (merci à Stefanie Mueller pour l'aide!).

La réponse la plus élevée utilisant gridExtra fonctionne, mais l'alignement des axes est difficile/difficile, comme cela a été souligné dans les commentaires. Ce problème peut maintenant être résolu en utilisant la commande ggMarginal du package ggExtra, en tant que telle:

#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

1
Victoria Auyeung

De nos jours, il existe au moins un package CRAN qui crée le diagramme de dispersion avec ses histogrammes marginaux.

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

Sample plot from scatterHist

1
Pere

Vous pouvez utiliser la forme interactive ggExtra::ggMarginalGadget(yourplot) et choisir entre des boîtes à moustaches, des tracés de violon, des tracés de densité et des histogrammes très facilement.

comme ça

0
allan