ggplot: weighted.mean и stat_summary на графике с гранеными полосами - PullRequest
0 голосов
/ 29 апреля 2018

Я потратил слишком много времени, пытаясь найти решение для включения weighted.mean (или wtd.mean) в stat_summary и заставить его работать должным образом. Я просмотрел несколько страниц, пытаясь решить одну и ту же проблему, но ни одна из них не имела окончательного решения. Основная проблема заключается в том, что weighted.mean, когда-то помещенный в stat_summary, не может найти компонент веса, который, очевидно, не может быть передан из эстетики ggplot и / или stat_summary (поверьте, я пробовал; см. Примеры). Теперь, я попробовал различные подходы и даже подготовил план взвешенных средних, используя функцию на основе ddplyr (как предложено на другой странице), но, будучи немного неуклюжим, он не допускает фасетирование, поскольку он изменяет исходный кадр данных.

Ниже приведен фрейм данных, специально созданный для этой проблемы.

elements <- c("water","water","water","water","water","water","air","air","air","air","air","air","earth","earth","earth","earth","earth","earth","fire","fire","fire","fire","fire","fire","aether","aether","aether","aether","aether","aether")
shapes <- c("icosahedron","icosahedron","icosahedron","icosahedron","icosahedron","icosahedron","octahedron","octahedron","octahedron","octahedron","octahedron","octahedron","cube","cube","cube","cube","cube","cube","tetrahedron","tetrahedron","tetrahedron","tetrahedron","tetrahedron","tetrahedron","dodecahedron","dodecahedron","dodecahedron","dodecahedron","dodecahedron","dodecahedron")
greek_letter <- c("alpha","beta","gamma","delta","epsilon","zeta","alpha","beta","gamma","delta","epsilon","zeta","alpha","beta","gamma","delta","epsilon","zeta","alpha","beta","gamma","delta","epsilon","zeta","alpha","beta","gamma","delta","epsilon","zeta")
existence <- c("real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","real","not real","not real","not real","not real","not real","not real")
value <- c(0,0,0,5,7,0,0,1,0,20,3,0,0,2,2,1,8,0,0,8,10,4,2,0,0,0,0,1,1,0)
importance <- c(20,20,20,20,20,20,10,10,10,10,10,10,3,3,3,3,3,3,9,9,9,9,9,9,50,50,50,50,50,50)
platonic <- data.frame(elements,shapes,greek_letter,existence,value,importance)

(Примечание: я также добавил столбец "shape", даже если я не буду его использовать, просто чтобы напомнить мне, что я не хочу потерять какие-либо данные в процессе, но он должен быть доступен в конец.)

Первоначальной настройкой был ggplot только со «средним», который включает фасетирование, как в:

ggplot(data = platonic)+
  stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value), fun.y = "mean", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)+
  facet_wrap(~elements~existence)

Ниже приведен соответствующий код, но при использовании «weighted.mean» -> эстетика «w» игнорируется, поэтому предполагается, что все веса равны (по определению функции weighted.mean), что приводит к простое среднее

ggplot(data = platonic)+
  stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value, w=platonic$importance), fun.y = "weighted.mean", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)

Как видите, выдает предупреждение Предупреждение: игнорирование неизвестной эстетики: w

Я пробовал несколько способов заставить его «видеть» переменную веса, но безуспешно. В конце я понял, что наиболее многообещающим способом было бы переопределить функцию weight.mean так, чтобы ее значение по умолчанию «w» было функцией «x». Weighted.mean по-прежнему не видит никакой "w" -эстетики, но вычисляет ее по умолчанию. Чтобы добиться этого, я попытался вложить встроенную функцию (weighted.mean) в универсальную функцию, которая позволяет мне изменять аргументы.

Шаг за шагом.

Сначала я попробовал с "значит" (и это работает).

mean.modif <- function(x) {
  mean(x)
}

ggplot(data = platonic)+
      stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value), fun.y = "mean.modif", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)

Затем с weighted.mean

   weighted.mean.modif <- function(x,w) {
      weighted.mean(x,w)
    }

 ggplot(data = platonic)+
  stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value), fun.y = "mean.modif", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)

но он по-прежнему не читает "w" (так как "w" не указано), поэтому возвращает нормальное среднее значение.

Затем я попытался указать аргумент "w" в качестве столбца весов в кадре данных

weighted.mean.modif1 <- function(x,w=platonic$importance) {
  weighted.mean(x,w)
}

ggplot(data = platonic)+
  stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value), fun.y = "mean.modif", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)

но это не работает. Предупреждающее сообщение гласит: Сбой вычисления в stat_summary(): 'x' и 'w' должны иметь одинаковую длину

Застряв, я попытался сгенерировать случайную серию чисел, но такой же длины, что и "x", и это неожиданно сработало.

weighted.mean.modif2 <- function(x,w=runif(x, min = 0, max = 100)) {
  weighted.mean(x,w)
}
ggplot(data = platonic)+
  stat_summary(mapping = aes(x=platonic$greek_letter, y=platonic$value), fun.y = "weighted.mean.modif2", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)

Очевидно, что есть способ обмануть его, но бесполезно, если я могу использовать только случайные веса.

Я попытался напечатать «х» внутри функции, а затем применил ее, и, хотя она что-то производит, даже «среднее» больше не работает должным образом.

mean.modif3 <- function(x) {
  mean(x)
  print(x)
}

Итак, сложная часть, которую я не могу понять, это как правильно соотнести значение по умолчанию «w» с «x», чтобы при вызове weighted.mean в stat_summary не читать «w» , в любом случае использует правильные веса.

Как я уже упоминал, существует также обходной путь ddply для получения средневзвешенного графика - поскольку он основан на создании нового исходного кадра данных с только уже организованными переменными и взвешенными средними, но не допускает огранки !!!

weighted.fictious <- function(xxxx, yyyy) {
  ddply(xxxx, .(yyyy), function(m) data.frame(fictious_weightedmean=weighted.mean(m$value, m$importance, na.rm = FALSE)))
}

ggplot(data = weighted.fictious(xxxx = platonic, yyyy = platonic$greek_letter), aes(x=yyyy, y=fictious_weightedmean))+
  geom_bar(stat = "identity")

Спасибо!

1 Ответ

0 голосов
/ 29 апреля 2018

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

Ваш базовый примерный график на самом деле неверен. Он показывает «эфир» как средство для дельты и эпсилона 5 и 7 соответственно, что явно не соответствует исходным данным (оба эти значения равны 1). Но эти являются значениями для первого элемента во фрейме данных («вода»). Ошибка возникает из-за того, что ggplot строит свои фасеты в алфавитном порядке, в то же время вы передаете необработанные векторы (platonic$value, а не просто value), что приводит к тому, что вещи отображаются в неправильном положении. При работе с ggplot вы всегда должны передавать необработанное имя столбца без кавычек, чтобы ggplot мог понять, как работать со связанными данными.

Правильная версия вашего основного сюжета будет:

g <- ggplot(data = platonic)+
  stat_summary(mapping = aes(x=greek_letter, y=value), fun.y = "mean", geom = "bar", na.rm = TRUE, inherit.aes = FALSE)+
  facet_wrap(~elements~existence)
print(g)

enter image description here

Что касается использования weighted.mean, как я уже говорил выше, единственно разумный план действий здесь - это рассчитать его отдельно и построить результат:

platonic.weighted <- platonic %>% 
  group_by(elements, existence, greek_letter) %>% 
  summarize(value = weighted.mean(value, weights = importance))

Поскольку результирующий фрейм данных по-прежнему содержит все имена столбцов, использованные на первом графике, вы можете просто поменять новый набор данных:

g.weighted <- g %+% platonic.weighted

В этом примере два участка идентичны, но ваш пробег может отличаться.

Ваш вопрос немного неясен относительно ожидаемого конечного результата, но из приведенного примера я предполагаю, что вы хотите получить взвешенное среднее значение для каждой греческой буквы. Мы можем использовать summarize, чтобы сделать это легко, или, если вы действительно хотите, вы можете использовать mutate вместо того, чтобы вставить столбец весов без потери исходных данных:

platonic.weighted <- platonic %>% 
  group_by(greek_letter) %>% 
  mutate(weighted.letter = weighted.mean(value, weights = importance))
...