2016-04-01 22 views
7

Aby wykreślić 9 histogramy na wykresie ggplot użyłem następujące dane:ggplot2: Jak wymusić liczbę aspektów przy zbyt małej liczbie działek?

       id    variable  value 
1      Segment III | RIM BlackBerry Pearl | 5.600000 
2     Display size | RIM BlackBerry Pearl | 6.500000 
3    Voice/call quality | RIM BlackBerry Pearl | 5.600000 
4 Instant messaging availability | RIM BlackBerry Pearl | 7.200000 
5     Media quality | RIM BlackBerry Pearl | 6.100000 
6   Ease of use for typing | RIM BlackBerry Pearl | 5.700000 
7  Speed in accessing email | RIM BlackBerry Pearl | 6.400000 
8      Segment II | RIM BlackBerry Pearl | 5.545455 
9     Value for money | RIM BlackBerry Pearl | 6.000000 
10     Segment III |  Palm Treo 700p | 4.320000 
11     Display size |  Palm Treo 700p | 6.500000 
12    Voice/call quality |  Palm Treo 700p | 8.000000 
13 Instant messaging availability |  Palm Treo 700p | 5.100000 
14     Media quality |  Palm Treo 700p | 7.000000 
15   Ease of use for typing |  Palm Treo 700p | 6.200000 
16  Speed in accessing email |  Palm Treo 700p | 6.500000 
17      Segment II |  Palm Treo 700p | 4.454545 
18    Value for money |  Palm Treo 700p | 5.400000 
19     Segment III |   Motorola Q | 4.680000 
20     Display size |   Motorola Q | 7.400000 
21    Voice/call quality |   Motorola Q | 4.800000 
22 Instant messaging availability |   Motorola Q | 5.300000 
23     Media quality |   Motorola Q | 6.900000 
24   Ease of use for typing |   Motorola Q | 7.400000 
25  Speed in accessing email |   Motorola Q | 8.000000 
26      Segment II |   Motorola Q | 3.121212 
27    Value for money |   Motorola Q | 4.200000 
28     Segment III |   Nokia 9300 | 4.360000 
29     Display size |   Nokia 9300 | 6.400000 
30    Voice/call quality |   Nokia 9300 | 7.800000 
31 Instant messaging availability |   Nokia 9300 | 6.700000 
32     Media quality |   Nokia 9300 | 5.900000 
33   Ease of use for typing |   Nokia 9300 | 4.500000 
34  Speed in accessing email |   Nokia 9300 | 6.300000 
35      Segment II |   Nokia 9300 | 7.181818 
36    Value for money |   Nokia 9300 | 4.600000 
37     Segment III | Sony Ericsson M600i | 4.360000 
38     Display size | Sony Ericsson M600i | 7.300000 
39    Voice/call quality | Sony Ericsson M600i | 8.000000 
40 Instant messaging availability | Sony Ericsson M600i | 1.500000 
41     Media quality | Sony Ericsson M600i | 7.800000 
42   Ease of use for typing | Sony Ericsson M600i | 5.000000 
43  Speed in accessing email | Sony Ericsson M600i | 8.100000 
44      Segment II | Sony Ericsson M600i | 3.606061 
45    Value for money | Sony Ericsson M600i | 4.000000 
46     Segment III |   Sidekick3 | 7.040000 
47     Display size |   Sidekick3 | 7.200000 
48    Voice/call quality |   Sidekick3 | 6.300000 
49 Instant messaging availability |   Sidekick3 | 7.200000 
50     Media quality |   Sidekick3 | 6.400000 
51   Ease of use for typing |   Sidekick3 | 6.800000 
52  Speed in accessing email |   Sidekick3 | 6.200000 
53      Segment II |   Sidekick3 | 3.424242 
54    Value for money |   Sidekick3 | 5.300000 

Potem używany następujący kod:

ggplot(data = data_sub, aes(x = variable, y = value)) + 
    geom_bar(stat = "identity") + 
    facet_wrap(~id, ncol = 3) + 
    coord_flip() + 
    theme(axis.title.x = element_blank(), 
     axis.title.y = element_blank(), 
     panel.grid = element_blank(), 
     legend.position = "none") 

I got:

Moje pytanie:

Kiedy mam mniej wykresów, na przykład tylko jeden, chciałbym zachować to formowanie. Jednak otrzymuję tylko duży wykres podobny do poniższego (nie zwracaj uwagi na legendy).

Jak mogę uzyskać coś takiego? enter image description here

+0

bym użyć 'drop = FAŁSZ' 'facet_wrap' w celu uniknięcia spadku stężenia czynnika nieużywane, lub ręcznie utworzyć siatkę przy użyciu' gridExtra :: grid.arrange' lub 'cowplot 'pakiet. – lukeA

+0

może [ta odpowiedź przez baptystę] (http://stackoverflow.com/a/32583612/3283824) na podobne pytanie, które miałem jakiś czas temu daje ci kilka pomysłów .. – beetroot

Odpowiedz

2

Jednym ze sposobów jest stworzenie wykresu dla każdego poziomu czynnika niepusty i puste zastępczy dla każdego pustego poziomu czynnika:

Po pierwsze, za pomocą wbudowanego mtcars ramki danych, ustawiamy się zmienną szlifowaniem jako czynnik z 9 poziomy, ale tylko 5 stopni z każdej transmisji danych,

library(ggplot2) 
library(grid) 
library(gridExtra) 

d = mtcars 
set.seed(4193) 
d$cyl = sample(1:9, nrow(d), replace=TRUE) 
d$cyl <- factor(d$cyl, levels=sort(unique(d$cyl))) 
d <- subset(d, cyl %in% c(1,5,7:9)) 

# Identify factor levels without any data 
blanks = which(table(d$cyl)==0) 

# Initialize a list 
pl = list() 

Poniższa pętla for przechodzi przez każdy poziom zmiennej faceting i tworzy wykres poziomu z danymi lub nullGrob (tj. Pusty symbol zastępczy, w którym działka byłaby, gdyby istniały dane dla tego poziomu czynnika) i dodaje go do lista pl.

for (i in 1:length(levels(d$cyl))) { 

    if(i %in% blanks) { 

    pl[[i]] = nullGrob() 

    } else { 

    pl[[i]] = ggplot(d[d$cyl %in% levels(d$cyl)[i], ], aes(x=am, y=wt)) + 
     geom_point() + 
     facet_grid(.~ cyl) 

    } 
} 

Teraz rozłożyć działek i dodać ramkę wokół nich:

do.call(grid.arrange, c(pl, ncol=3)) 
grid.rect(.5, .5, gp=gpar(lwd=2, fill=NA, col="black")) 

enter image description here

UPDATE: Cechą chciałbym dodać do mojej odpowiedzi jest usunięcie oś etykiety dla działek, które nie znajdują się w lewej kolumnie lub w dolnym wierszu (aby przypominały bardziej format w OP). Poniżej znajduje się moja niezbyt udana próba.

Problem pojawiający się podczas usuwania tyknięć osi i/lub etykiet z niektórych działek polega na tym, że powierzchnie działek są różnej wielkości na różnych poletkach. Powodem tego jest to, że wszystkie wykresy zajmują ten sam obszar fizyczny, ale wykresy z etykietami osi wykorzystują część tego obszaru dla etykiet osi, co powoduje, że ich obszary wydruku są mniejsze w stosunku do wykresów bez etykiet osi.

Miałem nadzieję, że mogę rozwiązać ten przy użyciu plot_grid z pakietu cowplot (autorem przez @ClausWilke), ale plot_grid nie działa z nullGrob s. Następnie @baptiste dodał kolejną odpowiedź na to pytanie, które usunął, ale które nadal jest widoczne dla użytkowników SO z co najmniej 10 000 reputacji. Ta odpowiedź poinformowała mnie o jego pakiecie egg i funkcji set_panel_size, aby ustawić wspólny rozmiar panelu na różnych ggplotach.

Poniżej znajduje się moja próba użycia set_panel_size do rozwiązania problemu z obszarem wydruku. Nie było to całkiem udane, co omówię bardziej szczegółowo po wyświetleniu kodu i fabuły.

# devtools::install_github("baptiste/egg") 
library(egg) 

# Fake data for making a barplot. Once again we have 9 facet levels, 
# but with data for only 5 of the levels. 
set.seed(4193) 
d = data.frame(facet=rep(LETTERS[1:9],each=100), 
       group=sample(paste("Group",1:5),900,replace=TRUE)) 
d <- subset(d, facet %in% LETTERS[c(1,5,7:9)]) 

# Identify factor levels without any data 
blanks = which(table(d$facet)==0) 

# Initialize a list 
pl = list() 

for (i in 1:length(levels(d$facet))) { 

    if(i %in% blanks) { 

    pl[[i]] = nullGrob() 

    } else { 

    # Create the plot, including a common y-range across all plots 
    # (though this becomes the x-range due to coord_flip) 
    pl[[i]] = ggplot(d[d$facet %in% levels(d$facet)[i], ], aes(x=group)) + 
     geom_bar() + 
     facet_grid(. ~ facet) + 
     coord_flip() + 
     labs(x="", y="") + 
     scale_y_continuous(limits=c(0, max(table(d$group, d$facet)))) 

    # If the panel isn't on the left edge, remove y-axis labels 
    if(!(i %in% seq(1,9,3))) { 
     pl[[i]] = pl[[i]] + theme(axis.text.y=element_blank(), 
           axis.ticks.y=element_blank()) 
    } 

    # If the panel isn't on the bottom, remove x-axis labels 
    if(i %in% 1:6) { 
     pl[[i]] = pl[[i]] + theme(axis.text.x=element_blank(), 
           axis.ticks.x=element_blank()) 
    } 
    } 

    # If the panel is a plot (rather than a nullGrob), 
    # remove margins and set to common panel size 
    if(any(class(pl[[i]]) %in% c("ggplot","gtable"))) { 
    pl[[i]] = pl[[i]] + theme(plot.margin=unit(rep(-1,4), "lines")) 
    pl[[i]] = set_panel_size(pl[[i]], width=unit(4,"cm"), height=unit(3,"cm")) 
    } 

} 

teraz rozłożyć działek:

do.call(grid.arrange, c(pl, ncol=3)) 
grid.rect(.5, .5, gp=gpar(lwd=2, fill=NA, col="black")) 

Jak widać na poniższym wykresie, choć Działki wszystkie mają te same rozmiary panelu, marginesy pomiędzy nimi nie są stałe, przypuszczalnie z powodu do drogi grid.arrange obsługuje odstępy dla zerówek, w zależności od tego, które pozycje mają faktyczne wątki. Ponadto, ponieważ set_panel_size określa bezwzględne rozmiary, musiałem ręcznie wyrównać końcowy wykres, aby panele były jak najbliżej siebie, a jednocześnie unikały nakładania się. Mam nadzieję, że jeden z rezydentów z siedziby SO grid wpadnie i zaproponuje bardziej skuteczne podejście.

(Zauważ, że dzięki takiemu podejściu możesz zakończyć bez etykietowanego wykresu w danym wierszu lub kolumnie.W poniższym przykładzie fabuła "E" nie ma etykiet osi Y i brak jest wykresu "D", więc jeśli chcesz wyświetlać tylko etykiety "B", "C", "E" i "F", nie będzie to oznaczało, że nie ma w nim żadnych znaków. Nie wiem, jak OP chce poradzić sobie z tą sytuacją (jedną z opcji byłoby dodanie logiki, aby zachować etykiety na działkach "wewnętrznych", jeśli "zewnętrzny" wątek jest nieobecny dla danego wiersza lub kolumny), ale myślałem, że Warto było wskazać.)

enter image description here

+0

To jest niesamowite i działa jak czar! Jednak błąkałem się, jak przekształcić wyjście w jeden obraz, idealnie nadając się do użycia 'ggsave'. Dowolny pomysł ? –

+0

Nie jestem pewien jak to zrobić z 'ggsave' w tym przypadku, ale możesz zrobić, na przykład' pdf ("test.pdf", 6, 6) 'przed linią' do.call', a następnie ' dev.off() 'po linii' grid.rect'. – eipi10

+0

'ggsave' nie jest problemem z niedawnym ggplot2, możesz go używać z gtables (nie grid.rect, ale to prawdopodobnie nie jest konieczne). – baptiste

1

spróbować

d <- subset(mtcars, cyl==4) 
d$cyl <- factor(d$cyl, levels=unique(mtcars$cyl)) 
ggplot(d, aes(x=am, y=wt)) + 
    geom_point() + 
    facet_grid(.~cyl, drop = FALSE) 
+0

'drop = FALSE' nadal daje w wyniku" puste "wyryte aspekty, ale bez żadnych punktów w nich. Wygląda na to, że OP chce białej przestrzeni, w której byłyby fasety, gdyby istniały jakieś wartości do wykreślenia. Może układ 'grid.arrange' z pustymi grobami dla pustych poziomów aspektów? – eipi10

+0

Prawdopodobnie można usunąć groby z karty, ale nie jest jasne, co powinno się stać z osiami. – baptiste

+0

Po pracy nad obydwoma rozwiązaniami, mam wrażenie, że twoje rozwiązanie może być właściwym rozwiązaniem. Powód jest taki, że byłby przechowywany jako "obiekt fabuły" zamiast "tableGrob", który jest trudniejszy do manipulowania, na przykład do 'ggsave' całego elementu. Ponadto wymaga znacznie mniej kodowania. Jedynym powodem, dla którego nie mogę tego teraz zaakceptować, jest to, że nie mogę się dowiedzieć, jak ukryć puste działki z pustym poziomem. Jeśli się zorientuję, będę na bieżąco informować. –

0

Jednym z możliwych rozwiązań jest utworzenie listy ggplotów i zastąpienie niektórych z nich symbolami zastępczymi.

d <- data.frame(x=rnorm(90), y=rnorm(90), 
       f1 = gl(3, 30) , f2 = rep(gl(3, 10), 3)) 

p <- ggplot(d, aes(x, y)) + 
    geom_point() 

# if you want to keep the facet labels 
# p <- p + facet_grid(f1~f2) 

library(plyr) 
pl <- dlply(d, .(f1, f2), "%+%", e1=p, .drop = FALSE) 

.dummy_plot <- ggplot() + theme_void() 
pl[c(3,4,7)] <- rep(list(.dummy_plot), 3, simplify=FALSE) 


# devtools::install_github("baptiste/egg) 
library(grid) 
grid.newpage() 
grid.draw(egg::ggarrange(plots=pl)) 

# alternatively 
# library(cowplot) 
# plot_grid(plotlist = pl) 

enter image description here