Colocación inteligente de etiquetas de puntos en R

102

1) ¿Existe alguna biblioteca / función R que implemente la colocación de etiquetas INTELIGENTES en el gráfico R? Intenté algunos, pero todos son problemáticos: muchas etiquetas se superponen entre sí o en otros puntos (u otros objetos en la trama, pero veo que esto es mucho más difícil de manejar).

2) Si no es así, ¿hay alguna manera de ayudar CÓMODAMENTE al algoritmo con la ubicación de la etiqueta para puntos problemáticos particulares? Se busca la solución más cómoda y eficiente.

Puede jugar y probar otras posibilidades con mi ejemplo reproducible y ver si puede lograr mejores resultados que yo:

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

Para el etiquetado, probé estas posibilidades, nadie es realmente bueno:

1) este es terrible:

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

2) este es bueno si no desea colocar etiquetas para todos los puntos, sino solo para los valores atípicos, pero aún así, las etiquetas a menudo se colocan incorrectamente:

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

3) este parecía prometedor, pero existe el problema de que las etiquetas están demasiado cerca de los puntos; Tuve que rellenarlos con espacios, pero esto no ayuda mucho:

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)

¡Gracias de antemano!

EDITAR: todo: prueba labcurve {Hmisc} .

TMS
fuente
2
Desafortunadamente, las respuestas a las preguntas R parecen estar divididas uniformemente entre StackOverflow y CrossValidated. En este caso, la pregunta es un duplicado de una de hace 4 días .
Ed Staub
3
Me encontré con un problema similar y escribí un paquete básico que usa simulación de campo de fuerza para ajustar la ubicación del objeto. Si bien es posible realizar muchas mejoras, incluida la integración con ggplot, etc., parece que se logra la tarea. A continuación se ilustra la funcionalidad. Si alguien se encuentra con el problema y busca una respuesta, con suerte esto será de alguna ayuda:install.packages("FField") library(FField) FFieldPtRepDemo()
gregk
¿Puedo pedirte que pruebes ggrepel ?
Kamil Slowikowski
querido @Joran, por favor ponga su comentario "6) Para los gráficos ggplot2, hay una nueva opción llamada ggrepel que parece gustar a muchas personas". en un comentario o una respuesta. Aquí solo incluí la lista de opciones que probé pero no son satisfactorias . Si es algo que funciona bien, entonces debería estar en una respuesta.
TMS

Respuestas:

49

Primero, aquí están los resultados de mi solución a este problema:

ingrese la descripción de la imagen aquí

Hice esto a mano en Preview (visor de imágenes / PDF muy básico en OS X) en solo unos minutos. ( Editar: el flujo de trabajo fue exactamente lo que esperaría: guardé el gráfico como PDF de R, lo abrí en Vista previa y creé cuadros de texto con las etiquetas deseadas (9pt Helvetica) y luego simplemente los arrastré con el mouse hasta que se veían bien. Luego exporté a PNG para subirlo a SO).

Ahora, antes de sucumbir al fuerte impulso de rechazar esto en el olvido y dejar comentarios sarcásticos sobre cómo se trata de automatizar este proceso, ¡escúchame!

Buscar soluciones algorítmicas está totalmente bien y (en mi humilde opinión) realmente interesante. Pero, para mí, las situaciones de etiquetado de puntos se dividen aproximadamente en tres categorías:

  1. Tiene una pequeña cantidad de puntos, ninguno que esté demasiado cerca . En este caso, es probable que una de las soluciones que enumeró en la pregunta funcione con ajustes bastante mínimos.
  2. Tiene una pequeña cantidad de puntos, algunos de los cuales están demasiado empaquetados para que las soluciones algorítmicas típicas den buenos resultados . En este caso, ya que sólo tienen un pequeño número de puntos, etiquetarlos con la mano (ya sea con un editor de imágenes o el ajuste fino de su llamada a text) no es que mucho esfuerzo.
  3. Tienes una cantidad bastante grande de puntos . En este caso, no debería etiquetarlos de todos modos, ya que es difícil procesar una gran cantidad de etiquetas visualmente.

: subirse a la caja de jabón:

Dado que la gente como nosotros amamos automatización, creo que a menudo caemos en la trampa de pensar que casi todos los aspectos de la producción de un buen gráfico estadístico deben automatizarse. Respetuosamente (¡humildemente!) No estoy de acuerdo.

No existe un entorno de trazado estadístico perfectamente general que cree automáticamente la imagen que tiene en su cabeza. Cosas como R, ggplot2, lattice, etc. hacen la mayor parte del trabajo; pero ese pequeño ajuste adicional, agregar una línea aquí, ajustar un margen allí, probablemente sea más adecuado para una herramienta diferente.

: bajando de la caja de jabón:

También me gustaría señalar que creo que todos podríamos crear diagramas de dispersión con <10-15 puntos que serán casi imposibles de etiquetar limpiamente, incluso a mano, y es probable que rompan cualquier solución automática que se le ocurra.

Finalmente, quiero reiterar que sé que esta no es la respuesta que está buscando. Y yo no diciendo que los intentos algorítmicos son inútiles o tontos. ¡Voté esta pregunta y con gusto votaré a favor de las soluciones algorítmicas interesantes!

La razón por la que publiqué esta respuesta es que creo que esta pregunta debería ser la pregunta canónica de "etiquetado de puntos en R" para futuros duplicados, y creo que las soluciones que involucran el etiquetado manual merecen un asiento en la mesa, eso es todo.

joran
fuente
10
Otra forma manual es guardar el gráfico como un SVG y editarlo usando Inkscape, luego producir PDF a partir de eso.
Spacedman
Hola joran, gracias por tu respuesta. OK, acepto esta solución, aunque creo que la computadora debería hacer esto mejor primero Y LUEGO solicitar la intervención manual. Aquí estoy buscando la solución más cómoda y rápida. ¿Podría describir cómo hizo la trama, paso a paso? ¿Qué generaste en R, exportar, mover las etiquetas en Vista previa, etc.?
TMS
1
@TomasT. Oh ya veo. En ese caso, "hice trampa", algo así. Genere un pdf con etiquetas usando uno de sus métodos anteriores y otro sin y usé el que tiene etiquetas como guía.
joran
1
+1 Esta es una gran respuesta. Alguna explicación de por qué aparece en meta-CV : vea los comentarios allí.
whuber
1
Mover un pequeño conjunto de etiquetas a mano parece sensato, pero también puede crearlas automáticamente primero y luego moverlas. De esa manera usted se está ahorrando mucho trabajo, y también reduce la probabilidad de errores de etiquetado ...
naught101
42

ggrepelparece prometedor cuando se aplica a ggplot2diagramas de dispersión.

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

ingrese la descripción de la imagen aquí

Sandy Muspratt
fuente
10

¿Has probado el paquete directlabels ?

Y, por cierto, los argumentos pos y offset pueden tomar vectores para permitirle colocarlos en las posiciones correctas cuando hay un número razonable de puntos en solo unas pocas ejecuciones de la gráfica.

Juan
fuente
¿Se puede utilizar el paquete directlabels con plot()trazado normal ? No tuve éxito intentándolo así que ... ¡Gracias! PD: @SpacedMan & Ben, limpié mis comentarios con respecto a la actualización de R, ya que no son tan interesantes, puedes hacer lo mismo.
TMS
6

¡Encontré alguna solución! Desafortunadamente, no es el mejor ni el ideal, pero es el que mejor funciona para mí ahora. Es mitad algorítmico, mitad manual, por lo que ahorra tiempo en comparación con la solución manual pura esbozada por joran.

¡Pasé por alto una parte muy importante de la ?identifyayuda!

El algoritmo utilizado para colocar etiquetas es el mismo que utiliza el texto si pos se especifica allí, con la diferencia de que la posición del puntero en relación con el punto identificado determina pos en identificar.

Entonces, si usa la identify()solución como escribí en mi pregunta, entonces puede afectar la posición de la etiqueta no haciendo clic directamente en ese punto, ¡sino haciendo clic al lado de ese punto relativamente en la dirección deseada! ¡Funciona genial!

La desventaja es que solo hay 4 posiciones (arriba, izquierda, abajo, derecha), pero agradecería más las otras 4 (arriba a la izquierda, arriba a la derecha, abajo a la izquierda, abajo a la derecha) ... use esto para etiquetar puntos donde no me molesta y el resto de los puntos que etiqueto directamente en mi presentación de Powerpoint, como propuso Joran :-)

PD: Todavía no he probado la solución directlabels lattice / ggplot, todavía prefiero usar la biblioteca de gráficos básica.

TMS
fuente
4

Te sugiero que eches un vistazo al wordcloudpaquete. Sé que este paquete no se centra exactamente en los puntos, sino en las propias etiquetas, y también el estilo parece bastante fijo. Pero aún así, los resultados que obtuve al usarlo fueron bastante impresionantes. También tenga en cuenta que la versión del paquete en cuestión se publicó aproximadamente en el momento en que hizo la pregunta, por lo que todavía es muy nueva.

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

comandante
fuente
3

Escribí una función R llamada addTextLabels()dentro de un paquete plotteR. El paquete se puede instalar directamente en su biblioteca de R usando el siguiente código:

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

Para el ejemplo proporcionado, utilicé el siguiente código para generar la figura de ejemplo vinculada a continuación.

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

Funciona seleccionando automáticamente una ubicación alternativa de una fina cuadrícula de puntos. Los puntos más cercanos en la cuadrícula se visitan primero y se seleccionan si no se superponen con puntos o etiquetas trazados. Eche un vistazo al código fuente , si está interesado.

Figura de ejemplo

Joseph Crispell
fuente
2

No es una respuesta, pero es demasiado larga para un comentario. Un enfoque muy simple que puede funcionar en casos simples, en algún lugar entre el posprocesamiento de joran y los algoritmos más sofisticados que se han presentado, es realizar in-placetransformaciones simples en el marco de datos.

Ilustro esto con ggplot2porque estoy más familiarizado con esa sintaxis que los gráficos 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)

Como puede ver, en este caso el resultado no es ideal, pero puede ser lo suficientemente bueno para algunos propósitos. Y es bastante sencillo, por lo general algo como esto es suficientewithin(df, y <- y+.01)

ingrese la descripción de la imagen aquí

PatrickT
fuente
2
En lugar de modificar el dfuso within, a menudo hago esto ajustando la estética: geom_text(aes(x = x - .01, y = y + .01), hjust = 0, vjust = 0)parece más limpio.
Gregor Thomas