Заказать столбцы в 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')

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


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

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

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


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

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()удаляет неприглядный «зазор» между стержнями и осью.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.