Pokaż% zamiast zliczeń na wykresach zmiennych kategorialnych

170

Planuję zmienną kategorialną i zamiast pokazywać liczby dla każdej wartości kategorii.

Szukam sposobu, aby ggplotwyświetlić procent wartości w tej kategorii. Oczywiście istnieje możliwość stworzenia innej zmiennej z wyliczonym procentem i wykreślenia tej jednej, ale muszę to robić kilkadziesiąt razy i mam nadzieję, że uda mi się to jednym poleceniem.

Eksperymentowałem z czymś takim

qplot(mydataf) +
  stat_bin(aes(n = nrow(mydataf), y = ..count../n)) +
  scale_y_continuous(formatter = "percent")

ale muszę go używać niepoprawnie, bo mam błędy.

Aby łatwo odtworzyć konfigurację, oto uproszczony przykład:

mydata <- c ("aa", "bb", NULL, "bb", "cc", "aa", "aa", "aa", "ee", NULL, "cc");
mydataf <- factor(mydata);
qplot (mydataf); #this shows the count, I'm looking to see % displayed.

W prawdziwym przypadku prawdopodobnie użyjęggplot zamiast qplot, ale właściwy sposób użycia stat_bin wciąż mi umyka .

Wypróbowałem również te cztery podejścia:

ggplot(mydataf, aes(y = (..count..)/sum(..count..))) + 
  scale_y_continuous(formatter = 'percent');

ggplot(mydataf, aes(y = (..count..)/sum(..count..))) + 
  scale_y_continuous(formatter = 'percent') + geom_bar();

ggplot(mydataf, aes(x = levels(mydataf), y = (..count..)/sum(..count..))) + 
  scale_y_continuous(formatter = 'percent');

ggplot(mydataf, aes(x = levels(mydataf), y = (..count..)/sum(..count..))) + 
  scale_y_continuous(formatter = 'percent') + geom_bar();

ale wszystkie 4 dają:

Error: ggplot2 doesn't know how to deal with data of class factor

Ten sam błąd pojawia się w prostym przypadku

ggplot (data=mydataf, aes(levels(mydataf))) +
  geom_bar()

więc wyraźnie chodzi o to, jak ggplotoddziałuje z pojedynczym wektorem. Drapię się w głowę, szukając w Google tego błędu, daje jeden wynik .

Wishihadabettername
źródło
2
Dane powinny być ramką danych, a nie nagim czynnikiem.
Hadley
1
dodanie do komentarza Hadleya, konwersja danych do ramki danych za pomocą mydataf = data.frame (mydataf) i zmiana nazwy na names (mydataf) = foo
załatwi sprawę

Odpowiedzi:

221

Odkąd udzielono odpowiedzi, nastąpiły pewne znaczące zmiany w ggplotskładni. Podsumowując dyskusję w komentarzach powyżej:

 require(ggplot2)
 require(scales)

 p <- ggplot(mydataf, aes(x = foo)) +  
        geom_bar(aes(y = (..count..)/sum(..count..))) + 
        ## version 3.0.0
        scale_y_continuous(labels=percent)

Oto odtwarzalny przykład przy użyciu mtcars:

 ggplot(mtcars, aes(x = factor(hp))) +  
        geom_bar(aes(y = (..count..)/sum(..count..))) + 
        scale_y_continuous(labels = percent) ## version 3.0.0

wprowadź opis obrazu tutaj

To pytanie jest obecnie numerem 1 w Google pod względem liczby ggplot vs histogram procentowy, więc miejmy nadzieję, że pomoże to wydestylować wszystkie informacje obecnie zawarte w komentarzach do zaakceptowanej odpowiedzi.

Uwaga: jeśli hpnie jest ustawiony jako współczynnik, ggplot zwraca:

wprowadź opis obrazu tutaj

Andrzej
źródło
12
Dzięki za tę odpowiedź. Masz jakiś pomysł, jak to zrobić z klasą?
WAF
3
Jak sugeruje. @ WAF, ta odpowiedź nie działa z danymi aspektowymi. Zobacz komentarz @ Erwana na stackoverflow.com/questions/22181132/…
LeeZamparo
1
Być może będziesz musiał poprzedzić percentpakiet, z którego pochodzi, aby powyższe działało (tak zrobiłem). ggplot(mtcars, aes(x = factor(hp))) + geom_bar(aes(y = (..count..)/sum(..count..))) + scale_y_continuous(labels = scales::percent)
mammykins
Aby obejść, użyj aspektów, geom_bar(aes(y = (..count..)/tapply(..count..,..PANEL..,sum)[..PANEL..]))zamiast tego użyj . Każdy aspekt powinien sumować się do 100%.
JWilliman,
Czy zmienne z „..” wokół nich nie zostały zastąpione poleceniem stat () -? ggplot2.tidyverse.org/reference/stat.html
Magnus
58

ten zmodyfikowany kod powinien działać

p = ggplot(mydataf, aes(x = foo)) + 
    geom_bar(aes(y = (..count..)/sum(..count..))) + 
    scale_y_continuous(formatter = 'percent')

jeśli twoje dane mają NA i nie chcesz, aby były uwzględniane w wykresie, przekaż na.omit (mydataf) jako argument do ggplot.

mam nadzieję że to pomoże.

Ramnath
źródło
37
Zauważ, że w wersji 0.9.0 ggplot2 formatterargument nie będzie już działał. Zamiast tego będziesz chciał coś takiego labels = percent_format()).
joran
25
A w wersji 0.9.0 musisz załadować scalesbibliotekę przed użyciem percent_format(), w przeciwnym razie nie zadziała. 0.9.0 nie wczytuje już automatycznie pakietów wspierających.
Andrew
1
Zobacz ? stat_bin. Pokazuje, jakie dodatkowe kolumny są dodawane do ramki danych ggplot2. Wszystkie dodatkowe kolumny mają postać ..variable...
Ramnath
1
Czy zamiana na aes(y = (..count..)/sum(..count..))prostą ma sens aes(y = ..density..)? Wizualnie daje to bardzo podobny (ale wciąż inny) obraz
Alexander Kosenkov
6
W ggplot 0.9.3.1.0, będziesz chciał najpierw załadować scalesbibliotekę, a następnie użyć, scale_y_continuous(labels=percent)jak wspomniano w dokumentacji
adilapapaya
49

Z ggplot2 w wersji 2.1.0 tak jest

+ scale_y_continuous(labels = scales::percent)
Fabian Hertwig
źródło
37

Według stanu na marzec 2017 r. W wersji ggplot22.2.1 najlepsze rozwiązanie można znaleźć w książce Hadley Wickham's R for data science:

ggplot(mydataf) + stat_count(mapping = aes(x=foo, y=..prop.., group=1))

stat_countoblicza dwie zmienne: countjest używana domyślnie, ale możesz wybrać, propktóra pokazuje proporcje.

Olivier Ma
źródło
3
To najlepsza odpowiedź na czerwiec 2017, działa z wypełnianiem według grup i facetingiem.
Skumin
1
Z jakiegoś powodu nie pozwala mi to na użycie fillmapowania (nie jest zgłaszany żaden błąd, ale nie jest dodawany kolor wypełnienia).
Max Candocia,
@MaxCandocia musiałem usunąć group = 1, aby uzyskać mapowanie wypełnienia. może to pomoże
Tjebo
1
Jeśli jednak usunę groupparametr, nie pokazuje on odpowiednich wartości procentowych, ponieważ wszystko należy do własnej grupy dla każdej unikalnej wartości x.
Max Candocia,
20

Jeśli chcesz procentowe na osi y i oznakowane na barach:

library(ggplot2)
library(scales)
ggplot(mtcars, aes(x = as.factor(am))) +
  geom_bar(aes(y = (..count..)/sum(..count..))) +
  geom_text(aes(y = ((..count..)/sum(..count..)), label = scales::percent((..count..)/sum(..count..))), stat = "count", vjust = -0.25) +
  scale_y_continuous(labels = percent) +
  labs(title = "Manual vs. Automatic Frequency", y = "Percent", x = "Automatic Transmission")

wprowadź opis obrazu tutaj

Podczas dodawania etykiet słupków możesz pominąć oś Y, aby uzyskać bardziej przejrzysty wykres, dodając na końcu:

  theme(
        axis.text.y=element_blank(), axis.ticks=element_blank(),
        axis.title.y=element_blank()
  )

wprowadź opis obrazu tutaj

Sam Firke
źródło
6

Jeśli chcesz mieć etykiety procentowe , ale rzeczywiste wartości N na osi y, spróbuj tego:

    library(scales)
perbar=function(xx){
      q=ggplot(data=data.frame(xx),aes(x=xx))+
      geom_bar(aes(y = (..count..)),fill="orange")
       q=q+    geom_text(aes(y = (..count..),label = scales::percent((..count..)/sum(..count..))), stat="bin",colour="darkgreen") 
      q
    }
    perbar(mtcars$disp)
Steve Powell
źródło
6

Oto obejście dla danych aspektowych. (Zaakceptowana odpowiedź @Andrew nie działa w tym przypadku.) Chodzi o to, aby obliczyć wartość procentową za pomocą dplyr, a następnie użyć geom_col do utworzenia wykresu.

library(ggplot2)
library(scales)
library(magrittr)
library(dplyr)

binwidth <- 30

mtcars.stats <- mtcars %>%
  group_by(cyl) %>%
  mutate(bin = cut(hp, breaks=seq(0,400, binwidth), 
               labels= seq(0+binwidth,400, binwidth)-(binwidth/2)),
         n = n()) %>%
  group_by(cyl, bin) %>%
  summarise(p = n()/n[1]) %>%
  ungroup() %>%
  mutate(bin = as.numeric(as.character(bin)))

ggplot(mtcars.stats, aes(x = bin, y= p)) +  
  geom_col() + 
  scale_y_continuous(labels = percent) +
  facet_grid(cyl~.)

To jest fabuła:

wprowadź opis obrazu tutaj

ACNB
źródło
3

Zauważ, że jeśli twoja zmienna jest ciągła, będziesz musiał użyć geom_histogram (), ponieważ funkcja ta pogrupuje zmienną według "pojemników".

df <- data.frame(V1 = rnorm(100))

ggplot(df, aes(x = V1)) +  
  geom_histogram(aes(y = (..count..)/sum(..count..))) 

# if you use geom_bar(), with factor(V1), each value of V1 will be treated as a
# different category. In this case this does not make sense, as the variable is 
# really continuous. With the hp variable of the mtcars (see previous answer), it 
# worked well since hp was not really continuous (check unique(mtcars$hp)), and one 
# can want to see each value of this variable, and not to group it in bins.
ggplot(df, aes(x = factor(V1))) +  
  geom_bar(aes(y = (..count..)/sum(..count..))) 
Rtist
źródło