249 votes

Comment puis-je tracer deux histogrammes ensemble en R?

Je suis en train d'utiliser R et j'ai deux data frames : carrots et cucumbers. Chaque data frame a une seule colonne numérique qui répertorie la longueur de toutes les carottes mesurées (total : 100k carottes) et des concombres (total : 50k concombres).

Je souhaite tracer deux histogrammes - la longueur des carottes et des concombres - sur le même graphique. Ils se chevauchent, donc je suppose que j'ai aussi besoin d'un peu de transparence. Je dois également utiliser des fréquences relatives et non des nombres absolus, car le nombre d'instances dans chaque groupe est différent.

Quelque chose comme ça serait bien, mais comment puis-je le créer à partir de mes deux tables ?

Densité superposée

0 votes

Au fait, quel logiciel envisagez-vous d'utiliser? Pour l'open source, je recommanderais gnuplot.info [gnuplot]. Dans sa documentation, je crois que vous trouverez certaines techniques et scripts d'exemple pour faire ce que vous voulez.

1 votes

Je suis en train d'utiliser R comme l'indique la balise (message édité pour le préciser)

1 votes

Quelqu'un a posté un extrait de code pour le faire dans ce fil : stackoverflow.com/questions/3485456/…

299voto

Dirk Eddelbuettel Points 134700

Voici une solution encore plus simple utilisant des graphiques de base et alpha-blending (qui ne fonctionne pas sur tous les périphériques graphiques):

set.seed(42)
p1 <- hist(rnorm(500,4))                     # centré sur 4
p2 <- hist(rnorm(500,6))                     # centré sur 6
plot( p1, col=rgb(0,0,1,1/4), xlim=c(0,10))  # premier histogramme
plot( p2, col=rgb(1,0,0,1/4), xlim=c(0,10), add=T)  # deuxième

La clé est que les couleurs sont semi-transparentes.

Éditer, plus de deux ans plus tard : Comme cela vient d'obtenir un vote positif, je me suis dit autant ajouter une image de ce que le code produit car l'alpha-blending est tellement utile:

enter image description here

8 votes

+1 merci à tous, cela peut-il être converti en un gistogramme plus lisse (comme had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.‌png)?

3 votes

Pourquoi avez-vous séparé les commandes plot ? Vous pouvez mettre toutes ces options dans les commandes hist et simplement les écrire en deux lignes.

0 votes

@John Comment le ferais-tu?

215voto

John Points 11714

Cette image à laquelle vous avez fait référence était pour des courbes de densité, pas des histogrammes.

Si vous avez été en train de lire sur ggplot alors peut-être que la seule chose qui vous manque est de combiner vos deux cadres de données en un seul long.

Donc, commençons par quelque chose comme ce que vous avez, deux ensembles de données séparés et combinez-les.

carrots <- data.frame(length = rnorm(100000, 6, 2))
cukes <- data.frame(length = rnorm(50000, 7, 2.5))

# Maintenant, combinez vos deux dataframes en un seul.
# D'abord ajoutez une nouvelle colonne dans chacun qui sera
# une variable pour identifier d'où elles viennent plus tard.
carrots$veg <- 'carrot'
cukes$veg <- 'cuke'

# et combinez dans votre nouveau cadre de données vegLengths
vegLengths <- rbind(carrots, cukes)

Après cela, ce qui est inutile si vos données sont déjà au format long, vous avez seulement besoin d'une ligne pour faire votre graphique.

ggplot(vegLengths, aes(length, fill = veg)) + geom_density(alpha = 0.2)

entrer la description de l'image ici

Maintenant, si vous vouliez vraiment des histogrammes, ce qui suit fonctionnera. Notez que vous devez changer la position de l'argument par défaut "stack". Vous pourriez passer à côté de cela si vous n'avez pas vraiment une idée de ce à quoi vos données devraient ressembler. Une alpha plus élevée a l'air mieux ici. Notez également que j'ai fait des histogrammes de densité. Il est facile de supprimer le y = ..densité.. pour le ramener aux dénombrements.

ggplot(vegLengths, aes(length, fill = veg)) + 
   geom_histogram(alpha = 0.5, aes(y = ..densité..), position = 'identity')

entrer la description de l'image ici

Une chose supplémentaire, j'ai commenté sur la question de Dirk que tous les arguments pourraient simplement être dans la commande hist. On m'a demandé comment cela pourrait être fait. Ce qui suit produit exactement la figure de Dirk.

set.seed(42)
hist(rnorm(500,4), col=rgb(0,0,1,1/4), xlim=c(0,10))
hist(rnorm(500,6), col=rgb(1,0,0,1/4), xlim=c(0,10), add = TRUE)

9 votes

Si vous souhaitez rester avec des histogrammes, utilisez ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge"). Cela permettra de créer des histogrammes entrelacés, comme dans MATLAB.

1 votes

Merci pour la réponse! La partie 'position="identity"' est en fait importante car sinon les barres sont empilées ce qui est trompeur lorsqu'elles sont combinées avec une densité qui semble par défaut être "identité", c'est-à-dire superposée et non pas empilée.

45voto

chrisamiller Points 1236

Voici une fonction que j'ai écrite qui utilise une pseudo-transparence pour représenter des histogrammes superposés

plotOverlappingHist <- function(a, b, colors=c("white","gray20","gray50"),
                                breaks=NULL, xlim=NULL, ylim=NULL){

  ahist=NULL
  bhist=NULL

  if(!(is.null(breaks))){
    ahist=hist(a,breaks=breaks,plot=F)
    bhist=hist(b,breaks=breaks,plot=F)
  } else {
    ahist=hist(a,plot=F)
    bhist=hist(b,plot=F)

    dist = ahist$breaks[2]-ahist$breaks[1]
    breaks = seq(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks),dist)

    ahist=hist(a,breaks=breaks,plot=F)
    bhist=hist(b,breaks=breaks,plot=F)
  }

  if(is.null(xlim)){
    xlim = c(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks))
  }

  if(is.null(ylim)){
    ylim = c(0,max(ahist$counts,bhist$counts))
  }

  overlap = ahist
  for(i in 1:length(overlap$counts)){
    if(ahist$counts[i] > 0 & bhist$counts[i] > 0){
      overlap$counts[i] = min(ahist$counts[i],bhist$counts[i])
    } else {
      overlap$counts[i] = 0
    }
  }

  plot(ahist, xlim=xlim, ylim=ylim, col=colors[1])
  plot(bhist, xlim=xlim, ylim=ylim, col=colors[2], add=T)
  plot(overlap, xlim=xlim, ylim=ylim, col=colors[3], add=T)
}

Voici une autre manière de le faire en utilisant le support de R pour les couleurs transparentes

a=rnorm(1000, 3, 1)
b=rnorm(1000, 6, 1)
hist(a, xlim=c(0,10), col="red")
hist(b, add=T, col=rgb(0, 1, 0, 0.5) )

Les résultats ressemblent à ceci : texte alternatif

0 votes

+1 pour une option disponible sur tous les appareils graphiques (par exemple, postscript)

25voto

nullglob Points 3257

Voici un exemple de comment vous pouvez le faire dans les graphiques R "classiques" :

## générer des données aléatoires
longueursCarotte <- rnorm(1000,15,5)
longueursConcombre <- rnorm(200,20,7)
## calculer les histogrammes - ne pas encore afficher
histCarotte <- hist(longueursCarotte,plot = FALSE)
histConcombre <- hist(longueursConcombre,plot = FALSE)
## calculer la plage du graphique
xlim <- range(histConcombre$breaks,histCarotte$breaks)
ylim <- range(0,histConcombre$density,
              histCarotte$density)
## afficher le premier graphique
plot(histCarotte,xlim = xlim, ylim = ylim,
     col = rgb(1,0,0,0.4),xlab = 'Longueurs',
     freq = FALSE, ## fréquence relative, pas absolue
     main = 'Répartition des carottes et des concombres')
## afficher le deuxième graphique dessus
opar <- par(new = FALSE)
plot(histConcombre,xlim = xlim, ylim = ylim,
     xaxt = 'n', yaxt = 'n', ## ne pas ajouter d'axes
     col = rgb(0,0,1,0.4), add = TRUE,
     freq = FALSE) ## fréquence relative, pas absolue
## ajouter une légende dans le coin
legend('topleft',c('Carottes','Concombres'),
       fill = rgb(1:0,0,0:1,0.4), bty = 'n',
       border = NA)
par(opar)

Le seul problème avec cela est que cela semble bien mieux si les intervalles d'histogramme sont alignés, ce qui peut devoir être fait manuellement (dans les arguments passés à hist).

0 votes

Très bien. Cela m'a aussi rappelé celui-ci stackoverflow.com/questions/3485456/…

0 votes

J'augmente cela car cette réponse est la seule (en dehors de celles dans ggplot ) qui tient directement compte si vos deux histogrammes ont des tailles d'échantillons sensiblement différentes.

0 votes

J'aime cette méthode, notez que vous pouvez synchroniser les pauses en les définissant avec seq (). Par exemple : breaks=seq (min(data$some_property), max(data$some_property), by=(max_prop - min_prop)/20)

23voto

John Points 11714

Voici la version comme celle de ggplot2 que j'ai donnée seulement en base R. J'ai copié une partie de @nullglob.

Générer les données

carottes <- rnorm(100000,5,2)
concombres <- rnorm(50000,7,2.5)

Vous n'avez pas besoin de le mettre dans un data frame comme avec ggplot2. L'inconvénient de cette méthode est que vous devez écrire beaucoup plus de détails du graphique. L'avantage est que vous avez le contrôle sur plus de détails du graphique.

## calculer la densité - ne pas encore tracer
densitéCarotte <- density(carottes)
densitéConcombre <- density(concombres)
## calculer la plage du graphique
xlim <- range(densitéConcombre$x,densitéCarotte$x)
ylim <- range(0,densitéConcombre$y, densitéCarotte$y)
#choisir les couleurs
couleurCarotte <- rgb(1,0,0,0.2)
couleurConcombre <- rgb(0,0,1,0.2)
## tracer les carottes et configurer la plupart des paramètres du graphique
plot(densitéCarotte, xlim = xlim, ylim = ylim, xlab = 'Longueurs',
     main = 'Distribution des carottes et des concombres', 
     panel.first = grid())
#mettre nos tracés de densité
polygon(densitéCarotte, densité = -1, col = couleurCarotte)
polygon(densitéConcombre, densité = -1, col = couleurConcombre)
## ajouter une légende dans le coin
legend('topleft',c('Carottes','Concombres'),
       fill = c(couleurCarotte, couleurConcombre), bty = 'n',
       border = NA)

entrez la description de l'image ici

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X