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

301

Я пытаюсь создать гистограмму, где самый большой столбец будет ближайшим к оси 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)

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

Хулио Диас
источник
12
ggplot не может переупорядочить их для вас без необходимости возиться с таблицей (или фреймом данных)?
tumultous_rooster
1
@ MattO'Brien Мне кажется невероятным, что это не делается ни одной простой командой
Euler_Salter
@Zimano Жаль, что это то, что вы получаете из моего комментария. Мое наблюдение было в отношении создателей ggplot2, а не ОП
Euler_Salter
2
@Euler_Salter Спасибо за разъяснения, мои искренние извинения за то, что я так на вас прыгнул. Я удалил свое оригинальное замечание.
Зимано

Ответы:

214

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

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

фигура барплота

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

theTable$Position <- factor(theTable$Position, levels = c(...))
Гэвин Симпсон
источник
1
@Gavin: 2 упрощения: так как вы уже используете within, нет необходимости использовать theTable$Position, и вы можете просто сделать sort(-table(...))для уменьшения порядка.
Прасад Чаласани
2
@Prasad был оставлен после тестирования, так что спасибо за указание на это. Что касается последнего, я предпочитаю явно запрашивать обратную сортировку, чем то, что -вы используете, поскольку гораздо легче получить намерение, decreasing = TRUEчем замечать это -во всем остальном коде.
Гэвин Симпсон
2
@GavinSimpson; Я думаю, что часть о levels(theTable$Position) <- c(...)приводит к нежелательному поведению, в котором переупорядочиваются фактические записи фрейма данных, а не только уровни фактора. Смотрите этот вопрос . Может быть, вы должны изменить или удалить эти строки?
Антон
2
Полностью согласен с Антоном. Я только что увидел этот вопрос и стал копаться в том, откуда у них плохой совет levels<-. Я собираюсь редактировать эту часть, по крайней мере, предварительно.
Грегор Томас
2
@Anton Спасибо за предложение (и Грегору за редактирование); Я бы никогда не сделал это levels<-()сегодня. Это что-то из 8 лет назад, и я не могу вспомнить, были ли тогда другие вещи или я был просто неправ, но независимо от этого, это неправильно и должно быть стерто! Спасибо!
Гэвин Симпсон
220

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

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
Алекс Браун
источник
7
Действительно +1, и особенно в этом случае, когда существует логический порядок, который мы можем использовать численно. Если мы рассмотрим произвольное упорядочение категорий и не хотим алфавитного алфавита, то точно так же просто (проще?) Указать уровни напрямую, как показано.
Гэвин Симпсон
2
Это самое аккуратное. Свести на нет необходимость изменять исходный
фрейм данных
Прекрасно, только что заметил, что вы можете сделать это немного более лаконично, если все, что вам нужно, это упорядочить по функции длины, и порядок возрастания в порядке, что я часто хочу делать:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem
146

Используется scale_x_discrete (limits = ...)для указания порядка баров.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
КИБИН ЛИ
источник
12
Ваше решение является наиболее подходящим для моей ситуации, так как я хочу запрограммировать построение графика с использованием x в качестве произвольного столбца, выраженного переменной в data.frame. Другие предложения будет сложнее выразить расположение порядка х с помощью выражения, включающего переменную. Спасибо! Если есть интерес, я могу поделиться своим решением, используя ваше предложение. Еще одна проблема, добавив scale_x_discrete (limit = ...), я обнаружил, что в правой части графика есть пустое пространство шириной с гистограмму. Как я могу избавиться от пустого пространства? Как это не служит какой-либо цели.
Ю Шен
Это кажется необходимым для заказа
столбцов
9
QIBIN: Wow ... другие ответы здесь работают, но ваш ответ на сегодняшний день кажется не просто самым кратким и элегантным, но наиболее очевидным, если думать в рамках ggplot. Спасибо.
Дэн Нгуен
Когда я попробовал это решение, по моим данным, оно не отображало NA. Есть ли способ использовать это решение и иметь его график NA?
user2460499
Это элегантное и простое решение - спасибо!
Калиф Вон
91

Я думаю, что уже предоставленные решения слишком многословны. Более краткий способ сделать полосу сортировки по частоте с помощью 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()
Хольгер Брандл
источник
Я не понимаю второй аргумент, чтобы изменить порядок функций и что она делает. Можете ли вы объяснить, что происходит?
user3282777 20.09.15
1
@ user3282777 Вы пробовали документы stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Хольгер Брандл
1
Отличное решение! Приятно видеть других, использующих решения Tidyverse!
Майк
29

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

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

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

введите описание изображения здесь

user2739472
источник
1
ИМХО, лучшее решение для forcats - это также dplyr пакет tidyverse.
C0bra
Спасибо за Zoalkeeper
otwtm
23

Простое переупорядочение факторов на основе 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
Zach
источник
19

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

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

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

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

Прасад Чаласани
источник
Я не полностью проанализировал ваш код там, но я почти уверен, reorder()что библиотека статистики выполняет ту же задачу.
Погоня
@ Чейз, как вы предлагаете использовать reorder()в этом случае? Фактор, требующий переупорядочения, должен быть переупорядочен какой-либо функцией, и я изо всех сил пытаюсь найти хороший способ сделать это.
Гэвин Симпсон
Хорошо, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))это один способ, а другой, with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))но они такие же запутанные ...
Гэвин Симпсон
Я немного упростил ответ, чтобы использовать, sortа неorder
Прасад Чаласани
@Gavin - возможно, я неправильно понял оригинальный код Прасада (у меня нет R на этом компьютере для тестирования ...), но это выглядело так, как будто он переупорядочивал категории, основываясь на частоте, что reorderискусно делать. Я согласен на этот вопрос, что нужно что-то более сложное. Извините за путаницу.
Погоня
17

В дополнение к 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)             

выходной график

Роберт Макдональд
источник
"fct_infreq (Position)" это маленькая вещь, которая так много делает, спасибо !!
Пол
12

Я согласен с Заком, что подсчет в 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.

Александру Папиу
источник
12

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

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

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

JColares
источник
8

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

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

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

введите описание изображения здесь

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

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

введите описание изображения здесь

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

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))
mpalanco
источник
5

Поскольку мы смотрим только на распределение одной переменной («Положение»), а не на отношение между двумя переменными , возможно, гистограмма была бы более подходящим графиком. У ggplot есть geom_histogram (), который упрощает:

ggplot(theTable, aes(x = Position)) + geom_histogram(stat="count")

введите описание изображения здесь

Используя geom_histogram ():

Я думаю, что geom_histogram ( ) немного странно, так как обрабатывает непрерывные и дискретные данные по-разному.

Для непрерывных данных вы можете просто использовать geom_histogram () без параметров. Например, если мы добавим в числовой вектор «Оценка» ...

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

и использовать geom_histogram () в переменной «Score» ...

ggplot(theTable, aes(x = Score)) + geom_histogram()

введите описание изображения здесь

Для дискретных данных, таких как «Положение», мы должны указать вычисляемую статистику, вычисляемую эстетикой, чтобы получить значение y для высоты столбцов, используя stat = "count":

 ggplot(theTable, aes(x = Position)) + geom_histogram(stat = "count")

Примечание: любопытно и запутанно, вы также можете использовать stat = "count"для непрерывных данных, и я думаю, что это обеспечивает более эстетически приятный график.

ggplot(theTable, aes(x = Score)) + geom_histogram(stat = "count")

введите описание изображения здесь

Изменения : расширенный ответ в ответ на полезные предложения DebanjanB .

несомненно
источник
0

Я нахожу это очень раздражающим, что ggplot2не предлагает «автоматического» решения для этого. Вот почему я создал bar_chart()функцию в ggcharts.

ggcharts::bar_chart(theTable, Position)

введите описание изображения здесь

По умолчанию bar_chart()сортирует бары и отображает горизонтальный график. Чтобы изменить этот набор horizontal = FALSE. Кроме того, bar_chart()удаляет неприглядный «зазор» между стержнями и осью.

Томас Найтманн
источник