2013-09-04 27 views
9

Widziałem wiele pytań (często związanych z Order Bars in ggplot2 bar graph) dotyczących sposobu (ponownego) zamawiania kategorii na wykresie słupkowym.Wykres słupkowy ggplot z kolejnością zależną od aspektu kategorii

To, czego szukam, to tylko dotyk inny, ale nie znalazłem na to dobrego sposobu: Mam wieloaspektowy wykres słupkowy i chcę zamówić oś x dla każdego aspektu niezależnie, zgodnie z do innej zmiennej (w moim przypadku ta zmienna jest samą wartością y, tzn. po prostu chcę, aby paski były coraz dłuższe w każdym aspekcie).

Prosty przykład, np. Order Bars in ggplot2 bar graph:

df <- data.frame(name=c('foo','bar','foo','bar'),period=c('old','old','recent','recent'),val=c(1.23,2.17,4.15,3.65)) 
p = ggplot(data = df, aes(x = reorder(name, val), y = val)) 
p = p + geom_bar(stat='identity') 
p = p + facet_grid(~period) 
p 

Co mamy jest następujący: enter image description here

Zważywszy, co chcę jest: enter image description here

+4

O mój Boże! Czy piszesz odnośnik do * How to Lie with Statistics *? – John

+1

Jedynym sposobem na zrobienie tego będzie utworzenie oddzielnych działek i użycie 'grid.arrange' z pakietu' gridExtra'. Zgadzam się jednak, że generalnie nie prowadzi to do bardzo ładnej fabuły. (Przekonasz się o tym dużo w ggplot, jeśli coś naprawdę trudno jest zrobić, prawdopodobnie dlatego, że próbuje powstrzymać cię od zrobienia czegoś głupiego.Nie zawsze, ale dużo ...) – joran

+0

Tak, dziękuję, niezbyt przydatne, ale dzięki. W kontekście, w którym go używamy, jest to ważna fabuła, a porządkowanie kategorii jest bardzo przemyślane. Tutaj sprowadziłem to do minimalnego przykładu, ale w naszej aplikacji sortujemy kilkanaście sygnałów w zależności od ich uświadomionej addytywności, a posiadanie krat w miejscu w jakimś aspekcie byłoby niedopuszczalne. –

Odpowiedz

18

Ok, więc wszystko filozofowania na bok, aw przypadku ktoś jest zainteresowany, tutaj jest brzydki hack to zrobić. Chodzi o to, aby używać różnych etykiet (myśl paste(period, name), z wyjątkiem tego, że zamieniam ten okres na 0-odstęp, 1-odstęp itd., Aby nie były wyświetlane). Potrzebuję tej działki i nie chcę, aby zorganizować grobs i tym podobne, bo ja mogę chcieć dzielić wspólną legendę itp

Przykład atomowej podane wcześniej staje:

df <- data.frame(name=c('foo','bar','foo','bar'), 
    period=c('old','old','recent','recent'), 
    val=c(1.23,2.17,4.15,3.65), 
    stringsAsFactors=F) 
df$n = as.numeric(factor(df$period)) 
df = ddply(df,.(period,name),transform, x=paste(c(rep(' ',n-1), name), collapse='')) 
df$x = factor(df$x, levels=df[order(df$val), 'x']) 
p = ggplot(data = df, aes(x = x, y = val)) 
p = p + geom_bar(stat='identity') 
p = p + facet_grid(~period, scale='free_x') 
p 

enter image description here Another przykładem, jeszcze trochę głupie ale bliżej moim konkretnym przypadku zastosowania, byłaby:

df <- ddply(mpg, .(year, manufacturer), summarize, mixmpg = mean(cty+hwy)) 
df$manufacturer = as.character(df$manufacturer) 
df$n = as.numeric(factor(df$year)) 
df = ddply(df, .(year,manufacturer), transform, 
    x=paste(c(rep(' ',n-1), manufacturer), collapse='')) 
df$x = factor(df$x, levels=df[order(df$mixmpg), 'x']) 
p = ggplot(data = df, aes(x = x, y = mixmpg)) 
p = p + geom_bar(stat='identity') 
p = p + facet_grid(~year, scale='free_x') 
p = p + theme(axis.text.x=element_text(angle=90,hjust=1,vjust=.5,colour='gray50')) 
p 

enter image description here Zamknij oczy, pomyśl o Imperium, i starają się cieszyć.

+0

I dodałem tę odpowiedź, ponieważ uważam, że to fajne, że można to zrobić bez 'grid.arrange', ale ponownie uważam, że może to być bardzo trudne, ponieważ nasze oczekiwania dotyczące fasetowanego wykresu są takie, że kategorie będą ułożone w taki sam sposób aspekty. Może to być oczekiwanie wrodzone lub historyczne, ale oczekiwanie jest jednak niemniej, a naruszenie go może być mylące. –

+0

Zgadzam się z @TylerRinker na obu rachunków i głosowałem odpowiednio. Inną opcją, która (IMHO) może być mniej myląca, może być całkowite wyciszenie etykiet osi i użycie tylko estetyki wypełnienia (jeśli jest tylko kilka taktów) lub oznaczenie ich wewnątrz wykresu powyżej każdego słupka. – joran

+0

Dzięki. Zasadniczo proponujesz, aby x był rangą (która jest spójną wartością liczbową) i narysował tekst kategorii gdzieś wewnątrz każdego paska, a nie jako etykietę. Może to stanowić problem, jeśli pasek jest niewielki dla niektórych kategorii, ale zawsze jestem otwarty na różnorodność opinii. Być może możesz podać przykład, np. używając danych 'mpg', abyśmy mogli zobaczyć, jak będzie wyglądać. Będąc wielbicielem Tufte, używanie przydziałów nie byłoby moim pierwszym wyborem, ale pasuje do tego, co Tyler nazwałby "historycznymi oczekiwaniami" (w tym przypadku tych z mojej firmy) ... –

1

Spróbuj, to naprawdę proste (prostu zignorować ostrzeżenia)

df <-data.frame(name = c('foo', 'bar', 'foo', 'bar'), 
       period = c('old', 'old', 'recent', 'recent'), 
       val = c(1.23, 2.17, 4.15, 3.65)) 

d1 <- df[order(df$period, df$val), ] 
sn <- factor(x = 1:4, labels = d1$name) 
d1$sn <- sn 
p <- ggplot(data = d1, aes(x = sn, y = val)) 
p <- p + geom_bar(stat = 'identity') 
p <- p + facet_wrap(~ period, scale = 'free_x') 
p 
+0

Dla zachowania kompletności: Ostrzeżenia do zignorowania czytaj: 'zduplikowane poziomy w czynnikach są przestarzałe. – Uwe

4

Jest to stary, ale to pytanie jest używany jako cel dupe. Warto więc zaproponować rozwiązanie, które będzie korzystało z najnowszych ulepszeń pakietu ggplot2, a mianowicie parametru labels na scale_x_discrete(). Pozwala to uniknąć use duplicate levels, która jest przestarzała lub do manipulate factor labels by prepending a varying number of spaces.

Przygotować dane

Tutaj mpg zbiór danych jest używany, aby mieć porównanie do this answer. Do manipulowania danymi jest tutaj użyty pakiet data.table, ale możesz użyć dowolnego pakietu do tego celu.

library(data.table) # version 1.10.4 
library(ggplot2)  # version 2.2.1 
# aggregate data 
df <- as.data.table(mpg)[, .(mixmpg = mean(cty + hwy)), by = .(year, manufacturer)] 
# create dummy var which reflects order when sorted alphabetically 
df[, ord := sprintf("%02i", frank(df, mixmpg, ties.method = "first"))] 

Tworzenie działka

# `ord` is plotted on x-axis instead of `manufacturer` 
ggplot(df, aes(x = ord, y = mixmpg)) + 
    # geom_col() is replacement for geom_bar(stat = "identity") 
    geom_col() + 
    # independent x-axis scale in each facet, 
    # drop absent factor levels (actually not required here) 
    facet_wrap(~ year, scales = "free_x", drop = TRUE) + 
    # use named character vector to replace x-axis labels 
    scale_x_discrete(labels = df[, setNames(as.character(manufacturer), ord)]) + 
    # replace x-axis title 
    xlab(NULL) + 
    # rotate x-axis labels 
    theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=.5)) 

enter image description here