Заказать столбцы в ggplot2 гистограмма

244

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

    Name   Position
1   James  Goalkeeper
2   Frank  Goalkeeper
3   Jean   Defense
4   Steve  Defense
5   John   Defense
6   Tim    Striker

Итак, я пытаюсь построить гистограмму, показывающую количество игроков в соответствии с позицией

p <- ggplot(theTable, aes(x = Position)) + geom_bar(binwidth = 1)

но на графике показан первый балл вратаря, затем защита, и, наконец, форвард. Я бы хотел, чтобы график был заказан так, чтобы защитный бар был ближе всего к оси y, к вратарю и, наконец, к форварду. Благодаря

  • 9
    ggplot не может переупорядочить их для вас без необходимости возиться с таблицей (или фреймом данных)?
Теги:
ggplot2
r-faq

11 ответов

178
Лучший ответ

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

## set the levels in order we want
theTable <- within(theTable, 
                   Position <- factor(Position, 
                                      levels=names(sort(table(Position), 
                                                        decreasing=TRUE))))
## plot
ggplot(theTable,aes(x=Position))+geom_bar(binwidth=1)

Изображение 4316

В самом общем смысле нам просто нужно установить уровни факторов в желаемом порядке. Если значение не указано, уровни фактора будут отсортированы в алфавитном порядке. Однако есть несколько способов изменить порядок на определенную последовательность в зависимости от ситуации. Например, мы могли бы сделать:

levels(theTable$Position) <- c(...)

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

theTable$Position <- factor(theTable$Position, levels = c(...))
  • 1
    @Gavin: 2 упрощения: поскольку вы уже используете within , нет необходимости использовать theTable$Position , и вы можете просто выполнить sort(-table(...)) для уменьшения порядка.
  • 2
    @Prasad был оставлен после тестирования, так что спасибо за указание на это. Что касается последнего, я предпочитаю явно запрашивать обратную сортировку, чем - вы используете, так как гораздо проще получить намерение от decreasing = TRUE чем замечать - во всем остальном коде.
Показать ещё 8 комментариев
188

@GavinSimpson: reorder является мощным и эффективным решением для этого:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
  • 6
    Действительно +1, и особенно в этом случае, когда существует логический порядок, который мы можем использовать численно. Если мы рассмотрим произвольный порядок категорий и не хотим алфавитный порядок, то точно так же просто (проще?) Указать уровни напрямую, как показано.
124

Используя scale_x_discrete (limits = ...), чтобы указать порядок столбцов.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
  • 9
    Ваше решение является наиболее подходящим для моей ситуации, так как я хочу запрограммировать построение графика с использованием x в качестве произвольного столбца, выраженного переменной в data.frame. Другие предложения будет сложнее выразить расположение порядка х с помощью выражения, включающего переменную. Спасибо! Если есть интерес, я могу поделиться своим решением, используя ваше предложение. Еще одна проблема, добавив scale_x_discrete (limit = ...), я обнаружил, что в правой части графика есть пустое пространство шириной с гистограмму. Как я могу избавиться от пустого пространства? Как это не служит какой-либо цели.
  • 0
    Это кажется необходимым для заказа гистограммы
Показать ещё 4 комментария
73

Я думаю, что уже предоставленные решения чересчур многословны. Более краткий способ сделать частотный сортированный штрих-код с ggplot -

ggplot(theTable, aes(x=reorder(Position, -table(Position)[Position]))) + geom_bar()

Это похоже на то, что предложил Алекс Браун, но немного короче и работает без определения какой-либо функции.

Обновление

Я думаю, что мое прежнее решение было хорошим в то время, но в настоящее время я бы предпочел использовать forcats::fct_infreq, который является уровнем коэффициента сортировки по частоте:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()
  • 0
    Я не понимаю второй аргумент, чтобы изменить порядок функций и что это делает. Можете ли вы объяснить, что происходит?
  • 1
    @ user3282777 вы пробовали документы stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Показать ещё 2 комментария
21

Как и reorder() в ответе Алекса Брауна, мы могли бы также использовать forcats::fct_reorder(). Он будет в основном сортировать коэффициенты, указанные в 1-м аргументе, в соответствии со значениями во втором аргументе после применения указанной функции (по умолчанию = медиана, что мы и используем здесь, просто как раз одно значение для каждого уровня фактора).

Жаль, что в вопросе OP требуемый заказ также является алфавитным, поскольку это порядок сортировки по умолчанию при создании факторов, поэтому скроет, что делает эта функция. Чтобы это стало яснее, я заменил "Вратарь" на "Zoalkeeper".

library(tidyverse)
library(forcats)

theTable <- data.frame(
                Name = c('James', 'Frank', 'Jean', 'Steve', 'John', 'Tim'),
                Position = c('Zoalkeeper', 'Zoalkeeper', 'Defense',
                             'Defense', 'Defense', 'Striker'))

theTable %>%
    count(Position) %>%
    mutate(Position = fct_reorder(Position, n, .desc = TRUE)) %>%
    ggplot(aes(x = Position, y = n)) + geom_bar(stat = 'identity')

Изображение 4317

  • 0
    ИМХО лучшее решение, как forcats, так же, как и dplyr пакет tidyverse.
18

Простое переупорядочение факторов на основе dplyr может решить эту проблему:

library(dplyr)

#reorder the table and reset the factor to that ordering
theTable %>%
  group_by(Position) %>%                              # calculate the counts
  summarize(counts = n()) %>%
  arrange(-counts) %>%                                # sort by counts
  mutate(Position = factor(Position, Position)) %>%   # reset factor
  ggplot(aes(x=Position, y=counts)) +                 # plot 
    geom_bar(stat="identity")                         # plot histogram
16

Вам просто нужно указать столбец Position как упорядоченный коэффициент, где уровни упорядочены по их подсчетам:

theTable <- transform( theTable,
       Position = ordered(Position, levels = names( sort(-table(Position)))))

(Обратите внимание, что table(Position) создает частотный счетчик столбца Position.)

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

  • 0
    Я не полностью проанализировал ваш код там, но я почти уверен, что reorder() из библиотеки статистики выполняет ту же задачу.
  • 0
    @ Чейз, как вы предлагаете использовать reorder() в этом случае? Фактор, требующий переупорядочения, должен быть переупорядочен какой-то функцией, и я изо всех сил пытаюсь найти хороший способ сделать это.
Показать ещё 4 комментария
10

В дополнение к forcats :: fct_infreq, указанному @HolgerBrandl, есть forcats :: fct_rev, который меняет порядок факторов.

theTable <- data.frame(
    Position= 
        c("Zoalkeeper", "Zoalkeeper", "Defense",
          "Defense", "Defense", "Striker"),
    Name=c("James", "Frank","Jean",
           "Steve","John", "Tim"))

p1 <- ggplot(theTable, aes(x = Position)) + geom_bar()
p2 <- ggplot(theTable, aes(x = fct_infreq(Position))) + geom_bar()
p3 <- ggplot(theTable, aes(x = fct_rev(fct_infreq(Position)))) + geom_bar()

gridExtra::grid.arrange(p1, p2, p3, nrow=3)             

Изображение 4318

  • 0
    "fct_infreq (Position)" - это маленькая вещь, которая так много делает, спасибо !!
9

Я согласен с zach, что подсчет в dplyr является лучшим решением. Я нашел, что это самая короткая версия:

dplyr::count(theTable, Position) %>%
          arrange(-n) %>%
          mutate(Position = factor(Position, Position)) %>%
          ggplot(aes(x=Position, y=n)) + geom_bar(stat="identity")

Это также будет значительно быстрее, чем переупорядочение уровней факторов заранее, поскольку счетчик выполняется в dplyr не в ggplot или с использованием table.

7

Если столбцы диаграммы взяты из числовой переменной, как в приведенной ниже таблице данных, вы можете использовать более простое решение:

ggplot(df, aes(x = reorder(Colors, -Qty, sum), y = Qty)) 
+ geom_bar(stat = "identity")  

Знак минус перед переменной сортировки (-Qty) управляет направлением сортировки (восходящий/нисходящий)

Вот некоторые данные для тестирования:

df <- data.frame(Colors = c("Green","Yellow","Blue","Red","Yellow","Blue"),  
                 Qty = c(7,4,5,1,3,6)
                )

**Sample data:**
  Colors Qty
1  Green   7
2 Yellow   4
3   Blue   5
4    Red   1
5 Yellow   3
6   Blue   6

Когда я нашел эту тему, это был ответ, который я искал. Надеюсь, это полезно для других.

0

Еще одна альтернатива, использующая переупорядочение для упорядочения уровней фактора. В возрастающем (n) или в убывающем порядке (-n) на основе количества. Очень похоже на использование fct_reorder из пакета forcats:

В порядке убывания

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, -n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

Изображение 4319

По возрастанию

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

Изображение 4320

Фрейм данных:

df <- structure(list(Position = structure(c(3L, 3L, 1L, 1L, 1L, 2L), .Label = c("Defense", 
"Striker", "Zoalkeeper"), class = "factor"), Name = structure(c(2L, 
1L, 3L, 5L, 4L, 6L), .Label = c("Frank", "James", "Jean", "John", 
"Steve", "Tim"), class = "factor")), class = "data.frame", row.names = c(NA, 
-6L))

Ещё вопросы

Сообщество Overcoder
Наверх
Меню