Замовити бруски в 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)

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


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(...))для зменшення замовлення.
Prasad Chalasani

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
Це найменше. Зніміть необхідність зміни оригінальних фреймів даних
T.Fung

Прекрасно, щойно зауважив, що ти можеш це зробити трохи коротше, якщо все, що ти хочеш, - це замовлення за функцією довжини і порядком зростання - це добре, що я часто хочу зробити: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. Інші пропозиції було б важче виразити розташування порядку x виразом із змінною. Дякую! Якщо є інтерес, я можу поділитися своїм рішенням, використовуючи вашу пропозицію. Ще в одному випуску, додавши scale_x_discrete (межі = ...), я виявив, що в правій частині діаграми є порожній простір, широкий, як і гістограма. Як я можу позбутися порожнього простору? Так як це не виконує жодної мети.
Ю. Шень

Це здається необхідним для замовлення гістограмів
геотеорія

9
QIBIN: Вау ... інші відповіді тут спрацьовують, але ця відповідь на сьогоднішній день здається не просто найбільш стислою та вишуканою, але найбільш очевидною, коли думаєте зсередини ggplot. Дякую.
Dan Nguyen

Коли я спробував це рішення, за моїми даними, це не відобразило NA. Чи є спосіб скористатися цим рішенням і мати його графічні NA?
користувач2460499

Це елегантне та просте рішення - дякую !!
Каліф Вон

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

Я не розумію другого аргументу про перепорядкування функції і що це робить. Чи можете ви ласкаво пояснити, що відбувається?
користувач3282777

1
@ user3282777 Ви спробували документи stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Холгер Брандл

1
Чудове рішення! Приємно бачити інших, хто використовує нестандартні рішення!
Майк

29

Як і reorder()у відповіді Алекса Брауна, ми також могли використатиforcats::fct_reorder() . В основному це буде сортувати фактори, зазначені в 1-му аргументі, відповідно до значень у 2-му аргументі після застосування вказаної функції (за замовчуванням = медіана, для чого ми тут використовуємо лише одне значення на рівень фактору).

Прикро, що в питанні щодо ОП необхідний порядок є також в алфавітному порядку, оскільки це порядок сортування за замовчуванням при створенні факторів, тому буде приховано, що ця функція насправді виконує. Щоб зробити це більш зрозумілим, я заміню "Воротар" на "Золкер".

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
Найкращим рішенням IMHO як forcats є також розробка пакету.
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()що бібліотека статистики виконує те саме завдання.
Чейз

@Chase, як ви пропонуєте використовувати 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)             

gplot вихід


"fct_infreq (Позиція)" - це маленька річ, яка робить так багато, дякую !!
Пол

12

Я погоджуюся з 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.


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.