31 votes

Existe-t-il un moyen de manipuler les ruptures d'échelle et les étiquettes de ggplot ?

ggplot fait généralement un bon travail de création de ruptures et d'étiquettes judicieuses dans les échelles.

Cependant, je trouve que dans les intrigues à plusieurs facettes et peut-être une formatter= les étiquettes ont tendance à devenir trop "denses" et à se surimprimer, comme c'est le cas sur cette photo :

df <- data.frame(
        fac=rep(LETTERS[1:10], 100),
        x=rnorm(1000)
)

ggplot(df, aes(x=x)) + 
  geom_bar(binwidth=0.5) + 
  facet_grid(~fac) + 
  scale_x_continuous(formatter="percent")

enter image description here

Je sais que je peux spécifier les ruptures et les étiquettes des échelles de manière explicite, en indiquant breaks= y scale= arguments pour scale_x_continuous .

Cependant, je traite des données d'enquête avec de nombreuses questions et une douzaine de croisements, et je dois donc trouver un moyen de le faire automatiquement.

Y a-t-il un moyen de dire ggplot de calculer automatiquement les pauses et les étiquettes, mais d'en avoir simplement moins, disons au point minimum, maximum et zéro ?

EDITAR: Idéalement, je ne veux pas spécifier les points minimum et maximum, mais en quelque sorte exploiter la formation d'échelles intégrée à ggplot, et utiliser les limites d'échelle calculées par défaut.

27voto

Chase Points 27342

Vous pouvez passer des arguments tels que min() y max() dans votre appel à ggplot pour spécifier dynamiquement les ruptures. Il semble que vous allez appliquer cette méthode à une grande variété de données, vous pouvez donc envisager de la généraliser dans une fonction et de modifier le formatage, mais cette approche devrait fonctionner :

ggplot(df, aes(x=x)) + 
  geom_bar(binwidth=0.5) + 
  facet_grid(~fac) + 
  scale_x_continuous(breaks = c(min(df$x), 0, max(df$x))
    , labels = c(paste( 100 * round(min(df$x),2), "%", sep = ""), paste(0, "%", sep = ""), paste( 100 * round(max(df$x),2), "%", sep = ""))
    )

ou faire pivoter le texte de l'axe des x avec opts(axis.text.x = theme_text(angle = 90, hjust = 0)) pour produire quelque chose comme :

enter image description here

Mise à jour

Dans la dernière version de ggplot2 le site breaks y labels arguments pour scale_x_continuous acceptent des fonctions, on peut donc faire quelque chose comme ce qui suit :

myBreaks <- function(x){
    breaks <- c(min(x),median(x),max(x))
    names(breaks) <- attr(breaks,"labels")
    breaks
}

ggplot(df, aes(x=x)) + 
  geom_bar(binwidth=0.5) + 
  facet_grid(~fac) + 
  scale_x_continuous(breaks = myBreaks,labels = percent_format()) + 
  opts(axis.text.x = theme_text(angle = 90, hjust = 1,size = 5))

1 votes

@Chase Merci. Oui, j'ai envisagé de faire cela, mais ce n'est pas idéal. La raison en est que les données peuvent être des pourcentages, des nombres de répondants, des scores t-stat, ou autre. Calculer la magnitude la plus proche pourrait être une option, mais ce que je veux vraiment faire, c'est utiliser l'échelle sur laquelle ggplot a été formé, puis masquer les étiquettes entre les points d'extrémité. En d'autres termes, je veux parfois que l'extrémité supérieure de l'échelle soit (par exemple) de 60 %. J'espère que cela a du sens.

0 votes

@Andrie - compris. Donc ce dont vous avez vraiment besoin ici, c'est d'une fonction qui interprète le type de données affichées sur l'axe des x (pourcentages, comptages, etc...) et modifie l'échelle en conséquence, n'est-ce pas ? Pouvez-vous utiliser class() sur les colonnes pour contribuer à cette information ? Ou d'autres données/métadonnées qui renseignent sur ce que vous tracez exactement ? Il ne devrait pas être trop difficile d'écrire une petite fonction pour générer le vecteur de ruptures et d'étiquettes à passer dans la fonction scale_x_continuous() en supposant que vous disposez de quelques informations pour savoir quoi et comment formater.

0 votes

@Chase J'espère que quelqu'un proposera une approche plus générique. Par exemple, lorsque l'on travaille avec des facettes et des échelles libres, par exemple facet_grid(~fac, scales="free"), les points de rupture haut et bas seront en général différents pour chaque facette. Donc, ce que je cherche vraiment, c'est de supprimer les étiquettes sans spécifier les ruptures.

5voto

Tim Goodman Points 7792

El scales Le paquet contient plusieurs breaks_* y label_* qui renvoient des fonctions (fermetures) qui sont utilisées par ggplot. Ainsi, vous pouvez écrire un wrapper pour ces fonctions qui modifie la sortie.

Par exemple :

library(ggplot2)

# Compute the list of breaks using original_func,
# then remove any of these that occur in remove_list
remove_breaks <- function(original_func, remove_list = list()) {
  function(x) {
    original_result <- original_func(x)
    original_result[!(original_result %in% remove_list)]
  }
}

# Compute the list of labels using original_func,
# then remove any of these that occur in remove_list
remove_labels <- function(original_func, remove_list = list()) {
  function(x) {
    original_result <- original_func(x)
    replace(original_result, original_result %in% remove_list, '')
  }
}

# Original plot
ggplot(data.frame(x=c(1,2,3,4,5,6,7,8), y = c(1,4,9,16,25,36,49,64))) + geom_line(aes(x, y)) +
  scale_x_continuous(breaks       = scales::breaks_pretty(9),
                     minor_breaks = scales::breaks_pretty(18),
                     labels       = scales::label_number_auto()) +
  scale_y_continuous(breaks       = scales::breaks_pretty(9),
                     minor_breaks = scales::breaks_pretty(18),
                     labels       = scales::label_number_auto())

# Remove some breaks from the x-axis, and remove some labels from the y-axis
ggplot(data.frame(x=c(1,2,3,4,5,6,7,8), y = c(1,4,9,16,25,36,49,64))) + geom_line(aes(x, y)) +
  scale_x_continuous(breaks       = remove_breaks(scales::breaks_pretty(9), seq(3,6)),
                     minor_breaks = remove_breaks(scales::breaks_pretty(18), seq(3,6,0.5)),
                     labels       = scales::label_number_auto()) +
  scale_y_continuous(breaks       = scales::breaks_pretty(9),
                     minor_breaks = scales::breaks_pretty(18),
                     labels       = remove_labels(scales::label_number_auto(), seq(20, 30)))

Bien sûr, avec mon simple remove_breaks y remove_labels vous devez toujours spécifier les valeurs à supprimer, mais vous pouvez facilement modifier ces fonctions pour qu'elles suppriment les valeurs maximales et minimales, qu'elles suppriment toute valeur dans une plage spécifiée, etc.

0 votes

J'ai écrit une réponse avant de remarquer l'ancienneté de la question.

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