Placement intelligent des étiquettes de points dans R

102

1) Existe-t-il une bibliothèque / fonction R qui implémenterait le placement d'étiquettes INTELLIGENT dans le tracé R? J'en ai essayé mais ils sont tous problématiques - de nombreuses étiquettes se chevauchent ou se chevauchent (ou d'autres objets dans l'intrigue, mais je vois que c'est beaucoup plus difficile à gérer).

2) Si non, y a-t-il un moyen d'aider CONFORTABLEMENT l'algorithme avec le placement des étiquettes pour des points problématiques particuliers? La solution la plus confortable et la plus efficace recherchée.

Vous pouvez jouer et tester d'autres possibilités avec mon exemple reproductible et voir si vous êtes en mesure d'obtenir de meilleurs résultats que moi:

# data
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
0.9717, 0.9357)
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
"SaxRub", "TurMer", "TurPil", "TurPhi")

# basic plot
plot(x, y, asp=1)
abline(h = 1, col = "green")
abline(v = 1, col = "green")

Pour l'étiquetage, j'ai ensuite essayé ces possibilités, personne n'est vraiment bon:

1) celui-ci est terrible:

text(x, y, labels = ShortSci, cex= 0.7, offset = 10)

2) celui-ci est bon si vous ne voulez pas placer d'étiquettes pour tous les points, mais juste pour les valeurs aberrantes, mais quand même, les étiquettes sont souvent mal placées:

identify(x, y, labels = ShortSci, cex = 0.7)

3) celui-ci avait l'air prometteur mais il y a le problème des étiquettes trop proches des points; J'ai dû les remplir d'espaces mais cela n'aide pas beaucoup:

require(maptools)
pointLabel(x, y, labels = paste("  ", ShortSci, "  ", sep=""), cex=0.7)

4)

require(plotrix)
thigmophobe.labels(x, y, labels = ShortSci, cex=0.7, offset=0.5)

5)

require(calibrate)
textxy(x, y, labs=ShortSci, cx=0.7)

Merci d'avance!

EDIT: todo: essayez labcurve {Hmisc} .

TMS
la source
2
Les réponses aux questions R semblent, malheureusement, être réparties également entre StackOverflow et CrossValidated. Dans ce cas, la question est un double d' une question d'il y a 4 jours là-bas .
Ed Staub
3
J'ai rencontré un problème similaire et j'ai écrit un package de base qui utilise la simulation de champ de force pour ajuster l'emplacement de l'objet. Bien que de nombreuses améliorations soient possibles, y compris l'intégration avec ggplot, etc., la tâche semble être accomplie. Ce qui suit illustre la fonctionnalité. Si quelqu'un rencontre le problème et recherche une réponse, j'espère que cela vous aidera:install.packages("FField") library(FField) FFieldPtRepDemo()
gregk
Puis-je vous demander d'essayer ggrepel ?
Kamil Slowikowski le
cher @Joran, veuillez mettre votre commentaire "6) Pour les graphiques ggplot2, il existe une nouvelle option appelée ggrepel que beaucoup de gens semblent aimer." dans un commentaire ou une réponse. Ici, je n'ai inclus que la liste des options que j'ai essayées mais qui ne sont pas satisfaisantes . Si c'est quelque chose qui fonctionne bien, cela devrait être dans une réponse.
TMS

Réponses:

49

Tout d'abord, voici les résultats de ma solution à ce problème:

entrez la description de l'image ici

Je l'ai fait à la main dans Preview (visionneuse PDF / image très basique sur OS X) en quelques minutes. ( Modifier: Le flux de travail était exactement ce à quoi vous vous attendiez: j'ai enregistré le tracé au format PDF à partir de R, je l'ai ouvert dans Aperçu et créé des zones de texte avec les étiquettes souhaitées (9pt Helvetica), puis je les ai simplement traînés avec ma souris jusqu'à ce qu'ils aient l'air bien. Ensuite, j'ai exporté vers un PNG pour le téléchargement vers SO.)

Maintenant, avant de succomber à la forte envie de rejeter cela dans l'oubli et de laisser des commentaires sournois sur la façon dont le but est d'automatiser ce processus, écoutez-moi!

La recherche de solutions algorithmiques est tout à fait bien et (à mon humble avis) vraiment intéressante. Mais, pour moi, les situations d'étiquetage de points se répartissent en trois catégories environ:

  1. Vous avez un petit nombre de points, aucun qui est terriblement rapproché . Dans ce cas, l'une des solutions que vous avez énumérées dans la question fonctionnera probablement avec un ajustement assez minime.
  2. Vous avez un petit nombre de points, dont certains sont trop serrés pour que les solutions algorithmiques typiques donnent de bons résultats . Dans ce cas, étant donné que vous n'avez qu'un petit nombre de points, les étiqueter à la main (soit avec un éditeur d'image ou en affinant votre appel text) n'est pas si compliqué.
  3. Vous avez un assez grand nombre de points . Dans ce cas, vous ne devriez vraiment pas les étiqueter de toute façon, car il est difficile de traiter un grand nombre d'étiquettes visuellement.

: grimper sur la caisse à savon:

Puisque les gens comme nous aiment automatisation, je pense que nous tombons souvent dans le piège de penser que presque tous les aspects de la production d'un bon graphique statistique devraient être automatisés. Je suis respectueusement (humblement!) En désaccord.

Il n'y a pas d'environnement de traçage statistique parfaitement général qui crée automatiquement l'image que vous avez dans votre tête. Des choses comme R, ggplot2, lattice etc. font la plupart du travail; mais ce petit ajustement supplémentaire, ajouter une ligne ici, ajuster une marge là-bas, est probablement mieux adapté à un outil différent.

: descente de la caisse à savon:

Je voudrais également noter que je pense que nous pourrions tous proposer des nuages ​​de points avec <10-15 points qu'il sera presque impossible d'étiqueter proprement, même à la main, et ceux-ci briseront probablement toute solution automatique que quelqu'un trouvera.

Enfin, je tiens à répéter que je sais que ce n'est pas la réponse que vous recherchez. Et je ne suis pas dis que les tentatives algorithmiques sont inutiles ou stupides. J'ai voté pour cette question et je serai heureux de voter pour des solutions algorithmiques intéressantes!

La raison pour laquelle j'ai posté cette réponse est que je pense que cette question devrait être la question canonique «étiquetage de point dans R» pour les futurs doublons, et je pense que les solutions impliquant un étiquetage manuel méritent une place à la table, c'est tout.

joran
la source
10
Une autre méthode manuelle consiste à enregistrer le tracé au format SVG et à le modifier à l'aide d'Inkscape, puis à produire un fichier PDF à partir de celui-ci.
Spacedman
Salut joran, merci pour votre réponse. OK, j'accepte cette solution, même si je pense que l'ordinateur devrait d'abord faire cela au mieux ET PUIS demander une intervention manuelle. Ici, je recherche la solution la plus confortable et la plus rapide. Pourriez-vous s'il vous plaît décrire comment vous avez fait l'intrigue, étape par étape? Qu'est-ce que vous avez généré dans R, exportez, déplacez les étiquettes dans Aperçu, etc.?
TMS
1
@TomasT. Oh je vois. Dans ce cas, j'ai "triché", en quelque sorte. J'ai généré un pdf avec des étiquettes en utilisant l'une de vos méthodes ci-dessus et une sans et j'ai utilisé celle avec des étiquettes comme guide.
joran
1
+1 C'est une excellente réponse. Une explication de pourquoi apparaît sur le méta-CV : voir les commentaires ici.
whuber
1
Déplacer un petit jeu d'étiquettes à la main semble judicieux, mais vous pouvez tout aussi bien les créer d'abord automatiquement , puis les déplacer. De cette façon, vous vous
épargnez
42

ggrepelsemble prometteur lorsqu'il est appliqué à des ggplot2nuages de points.

# data
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
0.9717, 0.9357)
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
"SaxRub", "TurMer", "TurPil", "TurPhi")


df <- data.frame(x = x, y = y, z = ShortSci)
library(ggplot2)
library(ggrepel)

ggplot(data = df, aes(x = x, y = y)) + theme_bw() + 

    geom_text_repel(aes(label = z), 
       box.padding = unit(0.45, "lines")) +

    geom_point(colour = "green", size = 3)

entrez la description de l'image ici

Sandy Muspratt
la source
10

Avez-vous essayé le package directlabels ?

Et, BTW, les arguments pos et offset peuvent prendre des vecteurs pour vous permettre de les placer dans les bonnes positions lorsqu'il y a un nombre raisonnable de points en seulement quelques séries de tracé.

John
la source
Le package directlabels peut-il être utilisé avec un plot()tracé normal ? Je n'ai pas réussi à essayer donc ... Merci! PS: @SpacedMan & Ben, j'ai nettoyé mes commentaires concernant la mise à jour R, car ils ne sont pas tellement intéressants - vous pouvez faire de même.
TMS
6

J'ai trouvé une solution! Ce n'est malheureusement pas ultime et idéal, mais c'est celui qui fonctionne le mieux pour moi maintenant. C'est à moitié algoritmique, à moitié manuel, ce qui fait gagner du temps par rapport à la solution manuelle pure esquissée par joran.

J'ai négligé une partie très importante de l' ?identifyaide!

L'algorithme utilisé pour placer les étiquettes est le même que celui utilisé par le texte si pos y est spécifié, la différence étant que la position du pointeur par rapport au point identifié détermine pos in identifier.

Donc, si vous utilisez la identify()solution comme je l'ai écrit dans ma question, alors vous pouvez affecter la position de l'étiquette en ne cliquant pas directement sur ce point, mais en cliquant à côté de ce point relativement dans la direction souhaitée !!! Fonctionne très bien!

L'inconvénient est qu'il n'y a que 4 positions (en haut, à gauche, en bas, à droite), mais j'apprécierais plus les 4 autres (en haut à gauche, en haut à droite, en bas à gauche, en bas à droite) ... utilisez ceci pour étiqueter les points où cela ne me dérange pas et le reste des points que j'étiquette directement dans ma présentation Powerpoint, comme l'a proposé joran :-)

PS: Je n'ai pas encore essayé la solution de directlabels lattice / ggplot, je préfère toujours utiliser la bibliothèque de tracés de base.

TMS
la source
4

Je vous suggère de jeter un oeil à la wordcloud ensemble. Je sais que ce package ne se concentre pas exactement sur les points mais sur les étiquettes elles-mêmes, et le style semble également être plutôt fixe. Mais quand même, les résultats que j'ai obtenus en l'utilisant étaient assez étonnants. Notez également que la version du package en question a été publiée à peu près au moment où vous avez posé la question, elle est donc encore très récente.

http://blog.fellstat.com/?cat=11

maj
la source
3

J'ai écrit une fonction R appelée addTextLabels()dans un package plotteR. Le package peut être directement installé dans votre bibliothèque R à l'aide du code suivant:

install.packages("devtools")
library("devtools")
install_github("JosephCrispell/basicPlotteR")

Pour l'exemple fourni, j'ai utilisé le code suivant pour générer l'exemple de figure lié ci-dessous.

# Load the plotteR library
library(plotteR)

# Create vectors storing the X and Y coordinates
x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
      0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
      0.9717, 0.9357)

# Store the labels to be plotted in a vector
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
             "SaxRub", "TurMer", "TurPil", "TurPhi")

# Plot the X and Y coordinates without labels
plot(x, y, asp=1)
abline(h = 1, col = "green")
abline(v = 1, col = "green")

# Add non-overlapping text labels
addTextLabels(x, y, ShortSci, cex=0.9, col.background=rgb(0,0,0, 0.75), 
              col.label="white")

Il fonctionne en sélectionnant automatiquement un emplacement alternatif à partir d'une fine grille de points. Les points les plus proches de la grille sont visités en premier et sélectionnés s'ils ne se chevauchent pas avec des points ou des étiquettes tracés. Jetez un œil au code source , si cela vous intéresse.

Exemple de figure

Joseph Crispell
la source
2

Pas de réponse, mais trop long pour un commentaire. Une approche très simple qui peut fonctionner sur des cas simples, quelque part entre le post-traitement de joran et les algorithmes plus sophistiqués qui ont été présentés est de fairein-place des transformations simples dans le dataframe.

J'illustre cela ggplot2parce que je suis plus familier avec cette syntaxe que les tracés de base R.

df <- data.frame(x = x, y = y, z = ShortSci)
library("ggplot2")
ggplot(data = df, aes(x = x, y = y, label = z)) + theme_bw() + 
    geom_point(shape = 1, colour = "green", size = 5) + 
    geom_text(data = within(df, c(y <- y+.01, x <- x-.01)), hjust = 0, vjust = 0)

Comme vous pouvez le voir, dans ce cas, le résultat n'est pas idéal, mais il peut être suffisant à certaines fins. Et c'est assez facile, généralement quelque chose comme ça suffitwithin(df, y <- y+.01)

entrez la description de l'image ici

PatrickT
la source
2
Plutôt que de modifier l' dfusage within, je le fais souvent en ajustant l'esthétique: ça geom_text(aes(x = x - .01, y = y + .01), hjust = 0, vjust = 0)semble plus propre.
Gregor Thomas