modificar los leyenda ggtitle ggplot etiquetas escala ejes como cambiar r ggplot2 hexagonal-tiles

los - Configuración de contenedores hexadecimales en ggplot2 al mismo tamaño



leyenda en ggplot2 (4)

Estoy tratando de hacer una representación hexbin de datos en varias categorías. El problema es que facetar estos contenedores parece hacer que todos ellos sean de diferentes tamaños.

set.seed(1) #Create data bindata <- data.frame(x=rnorm(100), y=rnorm(100)) fac_probs <- dnorm(seq(-3, 3, length.out=26)) fac_probs <- fac_probs/sum(fac_probs) bindata$factor <- sample(letters, 100, replace=TRUE, prob=fac_probs) library(ggplot2) #Actual plotting library(hexbin) ggplot(bindata, aes(x=x, y=y)) + geom_hex() + facet_wrap(~factor)

¿Es posible configurar algo para que todos estos contenedores sean físicamente del mismo tamaño?


Como dice Julius, el problema es que hexGrob no obtiene la información sobre los tamaños de los contenedores y lo adivina a partir de las diferencias que encuentra dentro de la faceta .

Obviamente, tendría sentido entregar dx y dy a un hexGrob , ya que no tener el ancho y la altura de un hexágono es como especificar un círculo por centro sin dar el radio.

Solución:

La estrategia de resolution funciona, si la faceta contiene dos haxagones adyacentes que difieren tanto en x como en y. Entonces, como solución alternativa, construiré manualmente un data.frame que contiene las coordenadas del centro xey de las celdas, y el factor de facetado y los conteos:

Además de las bibliotecas especificadas en la pregunta, necesitaré

library (reshape2)

y también el bindata$factor realidad debe ser un factor:

bindata$factor <- as.factor (bindata$factor)

Ahora, calcula la rejilla hexagonal básica

h <- hexbin (bindata, xbins = 5, IDs = TRUE, xbnds = range (bindata$x), ybnds = range (bindata$y))

A continuación, debemos calcular los recuentos en función del bindata$factor

counts <- hexTapply (h, bindata$factor, table) counts <- t (simplify2array (counts)) counts <- melt (counts) colnames (counts) <- c ("ID", "factor", "counts")

Como tenemos las ID de celda, podemos combinar este data.frame con las coordenadas apropiadas:

hexdf <- data.frame (hcell2xy (h), ID = h@cell) hexdf <- merge (counts, hexdf)

Así es como se ve el data.frame:

> head (hexdf) ID factor counts x y 1 3 e 0 -0.3681728 -1.914359 2 3 s 0 -0.3681728 -1.914359 3 3 y 0 -0.3681728 -1.914359 4 3 r 0 -0.3681728 -1.914359 5 3 p 0 -0.3681728 -1.914359 6 3 o 0 -0.3681728 -1.914359

ggplot ting (use el comando a continuación) esto produce los tamaños de bin correctos, pero la figura tiene una apariencia un poco extraña: se dibujan 0 hexágonos de conteo, pero solo cuando alguna otra faceta tiene este bin rellenado. Para suprimir el dibujo, podemos establecer los conteos allí en NA y hacer que el na.value completamente transparente (por defecto es grey50):

hexdf$counts [hexdf$counts == 0] <- NA ggplot(hexdf, aes(x=x, y=y, fill = counts)) + geom_hex(stat="identity") + facet_wrap(~factor) + coord_equal () + scale_fill_continuous (low = "grey80", high = "#000040", na.value = "#00000000")

Se obtiene la cifra en la parte superior del post.

Esta estrategia funciona siempre que los binwidths sean correctos sin facetado. Si los binwidths se establecen muy pequeños, la resolution aún puede producir dx y dy demasiado grandes. En ese caso, podemos suministrar hexGrob con dos contenedores adyacentes (pero diferenciándose tanto en x como en y) con conteos de NA para cada faceta.

dummy <- hgridcent (xbins = 5, xbnds = range (bindata$x), ybnds = range (bindata$y), shape = 1) dummy <- data.frame (ID = 0, factor = rep (levels (bindata$factor), each = 2), counts = NA, x = rep (dummy$x [1] + c (0, dummy$dx/2), nlevels (bindata$factor)), y = rep (dummy$y [1] + c (0, dummy$dy ), nlevels (bindata$factor)))

Una ventaja adicional de este enfoque es que podemos eliminar todas las filas con 0 conteos ya en counts , en este caso reduciendo el tamaño de hexdf en aproximadamente 3/4 (122 filas en lugar de 520):

counts <- counts [counts$counts > 0 ,] hexdf <- data.frame (hcell2xy (h), ID = h@cell) hexdf <- merge (counts, hexdf) hexdf <- rbind (hexdf, dummy)

La trama se ve exactamente igual a la anterior, pero puede visualizar la diferencia con un valor de na.value no es totalmente transparente.

más sobre el problema

El problema no es exclusivo de las facetas, pero ocurre siempre si hay muy pocas bandejas ocupadas, de modo que no se rellenan bandejas adyacentes "en diagonal".

Aquí hay una serie de datos más mínimos que muestran el problema:

Primero, trazo hexBin para obtener todas las coordenadas centrales de la misma cuadrícula hexagonal que ggplot2:::hexBin y el objeto devuelto por hexbin :

trace (ggplot2:::hexBin, exit = quote ({trace.grid <<- as.data.frame (hgridcent (xbins = xbins, xbnds = xbnds, ybnds = ybnds, shape = ybins/xbins) [1:2]); trace.h <<- hb}))

Configurar un conjunto de datos muy pequeño:

df <- data.frame (x = 3 : 1, y = 1 : 3)

Y la trama:

p <- ggplot(df, aes(x=x, y=y)) + geom_hex(binwidth=c(1, 1)) + coord_fixed (xlim = c (0, 4), ylim = c (0,4)) p # needed for the tracing to occur p + geom_point (data = trace.grid, size = 4) + geom_point (data = df, col = "red") # data pts str (trace.h) Formal class ''hexbin'' [package "hexbin"] with 16 slots ..@ cell : int [1:3] 3 5 7 ..@ count : int [1:3] 1 1 1 ..@ xcm : num [1:3] 3 2 1 ..@ ycm : num [1:3] 1 2 3 ..@ xbins : num 2 ..@ shape : num 1 ..@ xbnds : num [1:2] 1 3 ..@ ybnds : num [1:2] 1 3 ..@ dimen : num [1:2] 4 3 ..@ n : int 3 ..@ ncells: int 3 ..@ call : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds) ..@ xlab : chr "x" ..@ ylab : chr "y" ..@ cID : NULL ..@ cAtt : int(0)

Repito la trama, omitiendo el punto de datos 2:

p <- ggplot(df [-2,], aes(x=x, y=y)) + geom_hex(binwidth=c(1, 1)) + coord_fixed (xlim = c (0, 4), ylim = c (0,4)) p p + geom_point (data = trace.grid, size = 4) + geom_point (data = df, col = "red") str (trace.h) Formal class ''hexbin'' [package "hexbin"] with 16 slots ..@ cell : int [1:2] 3 7 ..@ count : int [1:2] 1 1 ..@ xcm : num [1:2] 3 1 ..@ ycm : num [1:2] 1 3 ..@ xbins : num 2 ..@ shape : num 1 ..@ xbnds : num [1:2] 1 3 ..@ ybnds : num [1:2] 1 3 ..@ dimen : num [1:2] 4 3 ..@ n : int 2 ..@ ncells: int 2 ..@ call : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds) ..@ xlab : chr "x" ..@ ylab : chr "y" ..@ cID : NULL ..@ cAtt : int(0)

  • tenga en cuenta que los resultados del hexbin están en la misma cuadrícula (los números de celda no cambiaron, simplemente la celda 5 ya no se rellena y, por lo tanto, no se enumeran), las dimensiones y los rangos de la cuadrícula no cambiaron. Pero los hexágonos trazados cambiaron dramáticamente.

  • Observe también que hgridcent olvida de devolver las coordenadas del centro de la primera celda (abajo a la izquierda).

Aunque se llena:

df <- data.frame (x = 1 : 3, y = 1 : 3) p <- ggplot(df, aes(x=x, y=y)) + geom_hex(binwidth=c(0.5, 0.8)) + coord_fixed (xlim = c (0, 4), ylim = c (0,4)) p # needed for the tracing to occur p + geom_point (data = trace.grid, size = 4) + geom_point (data = df, col = "red") + # data pts geom_point (data = as.data.frame (hcell2xy (trace.h)), shape = 1, size = 6)

Aquí, la representación de los hexágonos no puede ser correcta, no pertenecen a una cuadrícula hexagonal.


Hay dos archivos de origen que nos interesan: stat-binhex.r y geom-hex.r , principalmente funciones hexBin y hexGrob .

Como mencionó @Dinre, este problema no está realmente relacionado con las facetas. Lo que podemos ver es que binwidth no se ignora y se usa de una manera especial en hexBin , esta función se aplica para cada faceta por separado. Después de eso, se aplica hexGrob para cada faceta. Para asegurarse de que puede inspeccionarlos con por ejemplo

trace(ggplot2:::hexGrob, quote(browser())) trace(ggplot2:::hexBin, quote(browser()))

Por lo tanto, esto explica por qué los tamaños difieren: dependen del binwidth y de los datos de cada faceta.

Es difícil realizar un seguimiento del proceso debido a varias transformadas de coordenadas, pero observe que la salida de hexBin

data.frame( hcell2xy(hb), count = hb@count, density = hb@count / sum(hb@count, na.rm=TRUE) )

Parece que siempre parece bastante común y que hexGrob es responsable de dibujar contenedores hexadecimales, distorsión, es decir, tiene polygonGrob . En el caso de que solo haya un contenedor hexadecimal en una faceta, existe una anomalía más grave.

dx <- resolution(x, FALSE) dy <- resolution(y, FALSE) / sqrt(3) / 2 * 1.15

En ?resolution podemos ver

Descripción

The resolution is is the smallest non-zero distance between adjacent values. If there is only one unique value, then the resolution is defined to be one.

por esta razón ( resolution(x, FALSE) == 1 y resolution(y, FALSE) == 1 ) las coordenadas x de polygonGrob de la primera faceta en su ejemplo son

[1] 1.5native 1.5native 0.5native -0.5native -0.5native 0.5native

y si no estoy equivocado, en este caso, las unidades nativas son como npc, por lo que deberían estar entre 0 y 1. Es decir, en el caso de un solo contenedor hexadecimal, se sale del rango debido a la resolution() . Esta función también es la razón de distorsión que mencionó @Dinre incluso cuando tiene hasta varios contenedores de hex.

Así que por ahora no parece haber una opción para tener contenedores hexadecimales de igual tamaño. Una solución temporal (y muy inconveniente para una gran cantidad de factores) podría comenzar con algo como esto:

library(gridExtra) set.seed(2) bindata <- data.frame(x = rnorm(100), y = rnorm(100)) fac_probs <- c(10, 40, 40, 10) bindata$factor <- sample(letters[1:4], 100, replace = TRUE, prob = fac_probs) binwidths <- list(c(0.4, 0.4), c(0.5, 0.5), c(0.5, 0.5), c(0.4, 0.4)) plots <- mapply(function(w,z){ ggplot(bindata[bindata$factor == w, ], aes(x = x, y = y)) + geom_hex(binwidth = z) + theme(legend.position = ''none'') }, letters[1:4], binwidths, SIMPLIFY = FALSE) do.call(grid.arrange, plots)


Intenté replicar su solución con el mismo conjunto de datos utilizando un hexbinplot enrejado. Inicialmente, me dio un error xbnds[1] < xbnds[2] is not fulfilled . Este error se debió a vectores numéricos incorrectos que especificaron el rango de valores que debería cubrir el agrupamiento. Cambié esos argumentos en hexbinplot , y de alguna manera funcionó. No estoy seguro de si te ayuda a resolverlo con ggplot, pero probablemente sea un punto de partida.

library(lattice) library(hexbin) hexbinplot(y ~ x | factor, bindata, xbnds = "panel", ybnds = "panel", xbins=5, layout=c(7,3))

EDITAR

Aunque los contenedores rectangulares con stat_bin2d() funcionan bien:

ggplot(bindata, aes(x=x, y=y, group=factor)) + facet_wrap(~factor) + stat_bin2d(binwidth=c(0.6, 0.6))


También jugué un poco con las gráficas hexadecimales en ''ggplot2'', y pude producir de manera consistente una distorsión significativa de los contenedores cuando la población de un factor se redujo a 8 o menos. No puedo explicar por qué sucede esto sin excavar en la fuente del paquete (lo cual me resisto a hacer), pero puedo decirles que factores dispersos parecen destruir constantemente el trazado de cubos hexagonales en ''ggplot2''.

Esto me sugiere que el tamaño y la forma de un contenedor hexadecimal en particular en ''ggplot2'' está relacionado con un cálculo que es único para cada faceta, en lugar de hacer un solo cálculo para el grupo y trazar los datos posteriormente. Esto se ve algo reforzado por el hecho de que puedo reproducir la distorsión en cualquier faceta dada al trazar solo ese único factor, de esta manera:

ggplot(bindata[bindata$factor=="e",], aes(x=x, y=y)) + geom_hex()

Esto se siente como algo que debe ser elevado al mantenedor del paquete, Hadley Wickham (h.wickham en gmail.com). Esta información está disponible públicamente de CRAN.

Actualización : envié un correo electrónico a Hadley Wickham para preguntarle si echaría un vistazo a esta pregunta, y confirmó que este comportamiento es de hecho un error.