Подтвердить что ты не робот

Установка шестиугольников в ggplot2 с одинаковым размером

Я пытаюсь сделать hexbin представление данных в нескольких категориях. Проблема в том, что фацетирование этих бункеров, похоже, делает их все разными.

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)

enter image description here

Можно ли установить что-то, чтобы сделать все эти ячейки физически одного и того же размера?

4b9b3361

Ответ 1

Как говорит Юлий, проблема в том, что hexGrob не получает информацию о размерах бункера и угадывает ее из различий, которые она находит в фасете.

Очевидно, было бы целесообразно передать dx и dy в hexGrob - не имея ширины и высоты шестиугольника, как указание круга по центру без предоставления радиуса.

Обход проблемы:

workaround

Стратегия resolution работает, если фасет содержит два соседних хаксагона, которые различаются как по x, так и по y. Итак, в качестве обходного пути я создам вручную data.frame, содержащую координаты центра x и y ячеек, а также фактор фасетирования и подсчеты:

В дополнение к библиотекам, указанным в вопросе, мне понадобится

library (reshape2)

а также bindata$factor на самом деле должен быть фактором:

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

Теперь вычислим основную сечение шестиугольника

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

Затем нам нужно вычислить числа, зависящие от bindata$factor

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

Поскольку у нас есть идентификаторы ячеек, мы можем объединить этот data.frame с соответствующими координатами:

hexdf <- data.frame (hcell2xy (h),  ID = [email protected])
hexdf <- merge (counts, hexdf)

Вот что выглядит 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 (используйте приведенную ниже команду), это дает правильные размеры бункера, но фигура имеет немного странный внешний вид: нарисованы 0 шестиугольников, но только там, где какая-либо другая грань имеет этот бункер. Чтобы отменить чертеж, мы можем установить счетчики на NA и сделать na.value полностью прозрачным (по умолчанию он равен 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")

выводит фигуру в верхней части сообщения.

Эта стратегия работает до тех пор, пока ширина бинов не будет корректной без фасетов. Если ширина бинов установлена ​​очень мала, значение resolution может по-прежнему давать слишком большие значения dx и dy. В этом случае мы можем поставить hexGrob двумя соседними ячейками (но отличающимися как x, так и y) с NA подсчетами для каждой грани.

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

Дополнительным преимуществом этого подхода является то, что мы можем удалить все строки с 0 отсчетами уже в counts, в этом случае уменьшая размер hexdf примерно на 3/4 (122 строки вместо 520):

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

Сюжет выглядит точно так же, как и выше, но вы можете визуализировать разницу с na.value не полностью прозрачным.


подробнее о проблеме

Проблема не уникальна для фасетирования, но встречается всегда, если слишком мало бункеров занято, так что никакие "диагональные" соседние ячейки не заполняются.

Здесь приведен ряд более минимальных данных, которые показывают проблему:

Во-первых, я трассирую hexBin, поэтому получаю все центральные координаты одной и той же гексагональной сетки, что ggplot2:::hexBin и объект, возвращаемый 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}))

Настройте очень маленький набор данных:

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

И сюжет:

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
  [email protected] cell  : int [1:3] 3 5 7
  [email protected] count : int [1:3] 1 1 1
  [email protected] xcm   : num [1:3] 3 2 1
  [email protected] ycm   : num [1:3] 1 2 3
  [email protected] xbins : num 2
  [email protected] shape : num 1
  [email protected] xbnds : num [1:2] 1 3
  [email protected] ybnds : num [1:2] 1 3
  [email protected] dimen : num [1:2] 4 3
  [email protected] n     : int 3
  [email protected] ncells: int 3
  [email protected] call  : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
  [email protected] xlab  : chr "x"
  [email protected] ylab  : chr "y"
  [email protected] cID   : NULL
  [email protected] cAtt  : int(0) 

Я повторяю сюжет, оставляя точку данных 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
  [email protected] cell  : int [1:2] 3 7
  [email protected] count : int [1:2] 1 1
  [email protected] xcm   : num [1:2] 3 1
  [email protected] ycm   : num [1:2] 1 3
  [email protected] xbins : num 2
  [email protected] shape : num 1
  [email protected] xbnds : num [1:2] 1 3
  [email protected] ybnds : num [1:2] 1 3
  [email protected] dimen : num [1:2] 4 3
  [email protected] n     : int 2
  [email protected] ncells: int 2
  [email protected] call  : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
  [email protected] xlab  : chr "x"
  [email protected] ylab  : chr "y"
  [email protected] cID   : NULL
  [email protected] cAtt  : int(0) 

everything finehexagon plotting messed up

  • обратите внимание, что результаты из hexBin находятся в одной и той же сетке (номера ячеек не изменились, только ячейка 5 больше не заполнена и, следовательно, не указана), размеры и диапазоны сетки не изменились. Но графические шестиугольники действительно сильно изменились.

  • Также обратите внимание, что hgridcent забывает возвращать центральные координаты первой ячейки (внизу слева).

Хотя он заселен:

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)

all messed up

Здесь рендеринг шестиугольников не может быть правильным - они не принадлежат к одной гексагональной сетке.

Ответ 2

Я попытался реплицировать решение с тем же набором данных, используя решетку hexbinplot. Первоначально это дало мне ошибку xbnds[1] < xbnds[2] is not fulfilled. Эта ошибка была вызвана неправильными числовыми векторами, определяющими диапазон значений, которые должны быть покрыты биннингами. Я изменил эти аргументы в hexbinplot, и это как-то сработало. Не уверен, что это поможет вам решить проблему с ggplot, но это, вероятно, некоторая отправная точка.

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

enter image description here

ИЗМЕНИТЬ

Хотя прямоугольные ячейки с stat_bin2d() работают просто отлично:

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

enter image description here

Ответ 3

Есть два исходных файла, которые нас интересуют: stat-binhex.r и geom-hex.r, в основном hexBin и hexGrob.

Как упоминалось в @Dinre, эта проблема не связана с огранкой. Мы можем видеть, что binwidth не игнорируется и используется специальным образом в hexBin, эта функция применяется для каждой грани отдельно. После этого для каждого аспекта применяется hexGrob. Чтобы убедиться, что вы можете их проверить, например,

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

Следовательно, это объясняет, почему размеры различаются - они зависят как от binwidth, так и от данных каждой грани.

Трудно отслеживать процесс из-за различных преобразований координат, но обратите внимание, что вывод hexBin

data.frame(
  hcell2xy(hb),
  count = [email protected],
  density = [email protected] / sum([email protected], na.rm=TRUE)
)

всегда выглядит довольно обычным и что hexGrob отвечает за рисование шестиугольников, искажение, т.е. оно имеет polygonGrob. В случае, когда в фасете имеется только один шестнадцатеричный бункер, появляется более серьезная аномалия.

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

в ?resolution мы можем видеть

Описание

 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.

по этой причине (resolution(x, FALSE) == 1 и resolution(y, FALSE) == 1) координаты x polygonGrob первой грани в вашем примере:

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

и если я не ошибаюсь, в этом случае родные единицы похожи на npc, поэтому они должны быть между 0 и 1. То есть, в случае одного шестнадцатеричного бина, он выходит за пределы диапазона из-за resolution(). Эта функция также является причиной искажения, о котором упоминал @Dinre, даже имея до нескольких шестнадцатеричных бункеров.

Итак, теперь нет возможности иметь шестнадцатеричные ячейки одинакового размера. Временное (и очень неудобное для большого числа факторов) решение может начинаться с чего-то вроде этого:

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)

enter image description here

Ответ 4

Я также немного поиграл с гексагонами в "ggplot2" , и я смог последовательно генерировать значительные искажения бинов, когда популяция факторов была уменьшена до 8 или ниже. Я не могу объяснить, почему это происходит, не копаясь в источнике пакета (чего я неохотно делаю), но могу сказать, что редкие факторы, похоже, последовательно разрушают построение шестнадцатеричного бина в "ggplot2" .

Это говорит мне о том, что размер и форма конкретного шестнадцатеричного бункера в "ggplot2" связаны с вычислением, которое уникально для каждой грани, вместо того, чтобы выполнять один расчет для группы и строить данные после этого. Это несколько подкрепляется тем фактом, что я могу воспроизвести искажение в любом данном аспекте, построив только этот единственный коэффициент, например:

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

Это похоже на то, что должно быть возложено на сопровождающего пакета Хэдли Уикхэма (h.wickham at gmail.com). Эта информация общедоступна из CRAN.

Обновление. Я отправил электронное письмо Хэдли Викхэму с вопросом, не взглянет ли он на этот вопрос, и он подтвердил, что это поведение действительно является ошибкой.