Pesan Bar dalam grafik batang ggplot2

301

Saya mencoba membuat grafik batang di mana batang terbesar akan terdekat dengan sumbu y dan batang terpendek akan menjadi yang terjauh. Jadi ini seperti Tabel yang saya miliki

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

Jadi saya mencoba membuat grafik batang yang akan menunjukkan jumlah pemain sesuai dengan posisi

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

tetapi grafik menunjukkan bar kiper pertama kemudian pertahanan, dan akhirnya striker. Saya ingin grafik dipesan sehingga bar pertahanan paling dekat dengan sumbu y, kiper, dan akhirnya striker. Terima kasih

Julio Diaz
sumber
12
ggplot tidak bisa memesan ulang mereka untuk Anda tanpa harus main-main dengan tabel (atau dataframe)?
tumultous_rooster
1
@ MattO'Brien Saya merasa luar biasa bahwa ini tidak dilakukan dalam satu perintah, sederhana
Euler_Salter
@Zimano Sayang sekali itulah yang Anda dapatkan dari komentar saya. Pengamatan saya terhadap pencipta ggplot2, bukan OP
Euler_Salter
2
@Euler_Salter Terima kasih telah mengklarifikasi, permintaan maaf saya yang tulus karena telah menerkam Anda seperti itu. Saya telah menghapus komentar asli saya.
Zimano

Jawaban:

214

Kuncinya dengan pemesanan adalah mengatur level faktor dalam urutan yang Anda inginkan. Faktor yang dipesan tidak diperlukan; informasi tambahan dalam faktor yang dipesan tidak diperlukan dan jika data ini digunakan dalam model statistik apa pun, parametrisasi yang salah dapat terjadi - perbedaan polinomial tidak sesuai untuk data nominal seperti ini.

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

angka barplot

Dalam pengertian yang paling umum, kita hanya perlu mengatur level faktor sesuai urutan yang diinginkan. Jika dibiarkan tidak ditentukan, level faktor akan diurutkan berdasarkan abjad. Anda juga dapat menentukan urutan level dalam panggilan ke faktor seperti di atas, dan cara lain juga dimungkinkan.

theTable$Position <- factor(theTable$Position, levels = c(...))
Gavin Simpson
sumber
1
@ Gavin: 2 penyederhanaan: karena Anda sudah menggunakan within, tidak perlu digunakan theTable$Position, dan Anda bisa melakukannya sort(-table(...))untuk mengurangi pesanan.
Prasad Chalasani
2
@Prasad yang pertama adalah sisa dari pengujian jadi terima kasih untuk menunjukkannya. Sejauh yang terakhir, saya lebih suka secara eksplisit meminta jenis terbalik dari yang -Anda gunakan karena jauh lebih mudah untuk mendapatkan maksud dari decreasing = TRUEdaripada memperhatikan -di seluruh sisa kode.
Gavin Simpson
2
@GavinSimpson; Saya pikir bagian tentang levels(theTable$Position) <- c(...)mengarah ke perilaku yang tidak diinginkan di mana entri aktual dari frame data akan disusun ulang, dan bukan hanya tingkat faktor. Lihat pertanyaan ini . Mungkin Anda harus memodifikasi atau menghapus garis-garis itu?
Anton
2
Sangat setuju dengan Anton. Saya baru saja melihat pertanyaan ini dan mencari-cari saran buruk di mana levels<-. Saya akan mengedit bagian itu, setidaknya untuk sementara.
Gregor Thomas
2
@Anton Terima kasih atas sarannya (dan Gregor untuk hasil editnya); Saya tidak akan pernah melakukan ini levels<-()hari ini. Ini adalah sesuatu dari 8 tahun yang lalu dan saya tidak dapat mengingat apakah ada yang berbeda saat itu atau apakah saya hanya salah, tetapi bagaimanapun juga, itu salah dan harus dihapus! Terima kasih!
Gavin Simpson
220

@GavinSimpson: reorderadalah solusi yang kuat dan efektif untuk ini:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
Alex Brown
sumber
7
Memang +1, dan terutama dalam kasus ini di mana ada urutan logis yang dapat kita eksploitasi secara numerik. Jika kita mempertimbangkan pemesanan kategori secara sewenang-wenang dan kita tidak ingin abjad maka itu sama mudahnya (lebih mudah?) Untuk menentukan level secara langsung seperti yang ditunjukkan.
Gavin Simpson
2
Ini yang paling rapi. Batalkan kebutuhan untuk memodifikasi kerangka data asli
T.Fung
Indah, perhatikan saja bahwa Anda dapat melakukan ini sedikit lebih ringkas, jika semua yang Anda inginkan adalah memesan dengan fungsi panjang dan urutan naik tidak masalah, yang merupakan hal yang sering ingin saya lakukan:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem
146

Menggunakan scale_x_discrete (limits = ...)untuk menentukan urutan bilah.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
QIBIN LI
sumber
12
Solusi Anda adalah yang paling cocok dengan situasi saya, karena saya ingin memprogram untuk memplot dengan x menjadi kolom arbitrer yang diekspresikan oleh variabel dalam data.frame. Saran lain akan lebih sulit untuk mengekspresikan pengaturan urutan x dengan ekspresi yang melibatkan variabel. Terima kasih! Jika ada minat, saya dapat membagikan solusi saya menggunakan saran Anda. Hanya satu masalah lagi, menambahkan scale_x_discrete (batas = ...), saya menemukan bahwa ada ruang kosong selebar bagan-bar, di sebelah kanan grafik. Bagaimana saya bisa menyingkirkan ruang kosong? Karena tidak melayani tujuan apa pun.
Yu Shen
Ini tampaknya perlu untuk memesan bilah histogram
geotheory
9
QIBIN: Wow ... jawaban lain di sini berfungsi, tetapi jawaban Anda sejauh ini tampaknya bukan hanya yang paling ringkas dan anggun, tetapi yang paling jelas ketika berpikir dari dalam kerangka ggplot. Terima kasih.
Dan Nguyen
Ketika saya mencoba solusi ini, pada data saya, tidak membuat grafik NAS. Apakah ada cara untuk menggunakan solusi ini dan memilikinya grafik NAs?
user2460499
Ini adalah solusi yang elegan dan sederhana - terima kasih !!
Kalif Vaughn
91

Saya pikir solusi yang sudah disediakan terlalu bertele-tele. Cara yang lebih ringkas untuk melakukan barplot frekuensi diurutkan dengan ggplot adalah

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

Ini mirip dengan apa yang disarankan Alex Brown, tetapi sedikit lebih pendek dan berfungsi tanpa definisi fungsi apa pun.

Memperbarui

Saya pikir solusi lama saya bagus pada saat itu, tetapi saat ini saya lebih suka menggunakan forcats::fct_infreqyang menyortir tingkat faktor berdasarkan frekuensi:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()
Holger Brandl
sumber
Saya tidak mengerti argumen kedua untuk menyusun ulang fungsi dan apa fungsinya. Bisakah Anda menjelaskan apa yang sedang terjadi?
user3282777
1
@ user3282777 apakah Anda sudah mencoba dokumen stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Holger Brandl
1
Solusi bagus! Senang melihat orang lain menggunakan solusi rapi!
Mike
29

Seperti reorder()pada jawaban Alex Brown, kita juga bisa menggunakan forcats::fct_reorder(). Ini pada dasarnya akan mengurutkan faktor-faktor yang ditentukan dalam argumen 1, sesuai dengan nilai-nilai dalam argumen 2 setelah menerapkan fungsi yang ditentukan (default = median, yang kita gunakan di sini hanya memiliki satu nilai per tingkat faktor).

Sangat memalukan bahwa dalam pertanyaan OP, pesanan yang diperlukan juga alfabet karena itu adalah urutan penyortiran default saat Anda membuat faktor, sehingga akan menyembunyikan apa yang sebenarnya dilakukan fungsi ini. Untuk membuatnya lebih jelas, saya akan mengganti "Kiper" dengan "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')

masukkan deskripsi gambar di sini

pengguna2739472
sumber
1
Solusi terbaik IMHO sebagai forcats adalah juga dplyr paket yang rapi.
c0bra
jempol untuk Zoalkeeper
otwtm
23

Penataan ulang faktor berdasarkan dplyr sederhana dapat memecahkan masalah ini:

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
sumber
19

Anda hanya perlu menentukan Positionkolom untuk menjadi faktor yang dipesan di mana level dipesan berdasarkan jumlah mereka:

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

(Perhatikan bahwa table(Position)menghasilkan penghitungan frekuensi Positionkolom.)

Maka ggplotfungsi Anda akan menampilkan bar dalam mengurangi urutan hitungan. Saya tidak tahu apakah ada opsi geom_baruntuk melakukan ini tanpa harus secara eksplisit membuat faktor yang dipesan.

Prasad Chalasani
sumber
Saya tidak sepenuhnya mem-parsing kode Anda di sana, tapi saya cukup yakin reorder()dari perpustakaan statistik menyelesaikan tugas yang sama.
Chase
@Chase, bagaimana Anda mengusulkan menggunakan reorder()dalam kasus ini? Faktor yang membutuhkan penataan ulang perlu ditata ulang oleh beberapa fungsi itu sendiri dan saya berjuang untuk melihat cara yang baik untuk melakukan itu.
Gavin Simpson
ok, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))adalah satu cara, dan yang lain with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))tetapi ini sama berbelit-belit ...
Gavin Simpson
Saya menyederhanakan jawaban sedikit untuk digunakan sortdaripadaorder
Prasad Chalasani
@ Gavin - mungkin saya salah mengerti kode asli Prasad (saya tidak punya R pada mesin ini untuk menguji ...) tetapi sepertinya dia menata ulang kategori berdasarkan frekuensi, yang reordermahir melakukannya. Saya setuju untuk pertanyaan ini bahwa diperlukan sesuatu yang lebih terlibat. Maaf bila membingungkan.
Chase
17

Selain forcats :: fct_infreq, disebutkan oleh @HolgerBrandl, ada forcats :: fct_rev, yang membalik urutan faktor.

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)             

keluaran gplot

Robert McDonald
sumber
"fct_infreq (Posisi)" adalah hal kecil yang melakukan banyak hal, terima kasih !!
Paul
12

Saya setuju dengan zach bahwa menghitung dalam dplyr adalah solusi terbaik. Saya menemukan ini sebagai versi terpendek:

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

Ini juga akan secara signifikan lebih cepat daripada menata ulang tingkat faktor sebelumnya karena penghitungan dilakukan dalam dplyr bukan di ggplot atau menggunakan table.

Alexandru Papiu
sumber
12

Jika kolom bagan berasal dari variabel numerik seperti dalam kerangka data di bawah, Anda dapat menggunakan solusi yang lebih sederhana:

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

Tanda minus sebelum variabel sortir (-Qty) mengontrol arah sortir (naik / turun)

Berikut beberapa data untuk pengujian:

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

Ketika saya menemukan utas ini, itulah jawaban yang saya cari. Semoga bermanfaat bagi orang lain.

JColares
sumber
8

Alternatif lain menggunakan pemesanan ulang untuk memesan tingkat faktor. Dalam urutan naik (n) atau menurun (-n) berdasarkan hitungan. Sangat mirip dengan yang menggunakan fct_reorderdari forcatspaket:

Urutan menurun

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

masukkan deskripsi gambar di sini

Urutan naik

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

masukkan deskripsi gambar di sini

Bingkai data:

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
sumber
5

Karena kita hanya melihat distribusi variabel tunggal ("Posisi") yang bertentangan dengan melihat hubungan antara dua variabel , maka mungkin histogram akan menjadi grafik yang lebih tepat. ggplot memiliki geom_histogram () yang memudahkan:

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

masukkan deskripsi gambar di sini

Menggunakan geom_histogram ():

Saya pikir geom_histogram ( ) sedikit aneh karena memperlakukan data kontinu dan berbeda.

Untuk data kontinu , Anda bisa menggunakan geom_histogram () tanpa parameter. Misalnya, jika kita menambahkan vektor "Skor" numerik ...

    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

dan gunakan geom_histogram () pada variabel "Skor" ...

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

masukkan deskripsi gambar di sini

Untuk data diskrit seperti "Posisi", kita harus menentukan statistik terhitung yang dihitung oleh estetika untuk memberikan nilai y untuk ketinggian batang menggunakan stat = "count":

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

Catatan: Aneh dan membingungkan Anda juga dapat menggunakan stat = "count"untuk data kontinu dan saya pikir ini memberikan grafik yang lebih estetis.

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

masukkan deskripsi gambar di sini

Suntingan : Jawaban yang diperluas untuk menanggapi saran DebanjanB yang bermanfaat.

pasti
sumber
0

Saya merasa sangat menjengkelkan yang ggplot2tidak menawarkan solusi 'otomatis' untuk ini. Itu sebabnya saya membuat bar_chart()fungsi di ggcharts.

ggcharts::bar_chart(theTable, Position)

masukkan deskripsi gambar di sini

Secara default bar_chart()menyortir bilah dan menampilkan plot horizontal. Untuk mengubah set itu horizontal = FALSE. Selain itu, bar_chart()menghilangkan 'celah' yang tidak sedap dipandang antara batang dan sumbu.

Thomas Neitmann
sumber