2010-12-30 21 views
21

Muszę narysować wykres piramidy, podobnie jak ten dołączony.rysowanie wykresu piramidy za pomocą R i ggplot2

alt text

znalazłem przykład przy użyciu R (ale nie ggplot) od here, może ktoś mi dać jakąś wskazówkę robi to przy użyciu ggplot? Dzięki!

+0

właśnie odkrył funkcję podobną koncepcją w 'Hmisc'. 'histbackback (rnorm (20), rnorm (30))'. –

Odpowiedz

19

Jest to zasadniczo barplot back-to-back, coś jak te generowane przy użyciu ggplot2 w doskonałej learnr blogu: http://learnr.wordpress.com/2009/09/24/ggplot2-back-to-back-bar-charts/

można użyć coord_flip z jednej z tych działek, ale nie jestem pewien, jak dostaniesz go, aby współużytkować etykiety osi Y między dwiema powierzchniami, jak to, co masz powyżej. Poniższy kod powinien dostać się wystarczająco blisko do oryginału:

Najpierw utworzyć przykładową ramkę danych danych, konwersja kolumnę Wiek do czynnika z wymaganych punktach kontrolnych:

require(ggplot2) 
df <- data.frame(Type = sample(c('Male', 'Female', 'Female'), 1000, replace=TRUE), 
       Age = sample(18:60, 1000, replace=TRUE)) 

AgesFactor <- ordered(cut(df$Age, breaks = c(18,seq(20,60,5)), 
          include.lowest = TRUE)) 

df$Age <- AgesFactor 

teraz rozpocząć budowę Opis: tworzenie męskich i żeńskich działek z odpowiednim zbiór danych, zatrzymywanie legendy, itp

gg <- ggplot(data = df, aes(x=Age)) 

gg.male <- gg + 
    geom_bar(subset = .(Type == 'Male'), 
      aes(y = ..count../sum(..count..), fill = Age)) + 
    scale_y_continuous('', formatter = 'percent') + 
    opts(legend.position = 'none') + 
    opts(axis.text.y = theme_blank(), axis.title.y = theme_blank()) + 
    opts(title = 'Male', plot.title = theme_text(size = 10)) + 
    coord_flip()  

u samic działce, odwrócenie „Procent” oś pomocą trans = "reverse" ...

gg.female <- gg + 
    geom_bar(subset = .(Type == 'Female'), 
      aes(y = ..count../sum(..count..), fill = Age)) + 
    scale_y_continuous('', formatter = 'percent', trans = 'reverse') + 
    opts(legend.position = 'none') + 
    opts(axis.text.y = theme_blank(), 
     axis.title.y = theme_blank(), 
     title = 'Female') + 
    opts(plot.title = theme_text(size = 10)) + 
    coord_flip() 

Teraz stworzyć fabułę tylko do wyświetlania W wieku nawiasach za pomocą geom_text, ale również użyć obojętne geom_bar aby upewnić się, że skalowanie „wiek” osi w tej działce jest identyczna jak w działkach męskich i żeńskich:

gg.ages <- gg + 
    geom_bar(subset = .(Type == 'Male'), aes(y = 0, fill = alpha('white',0))) + 
    geom_text(aes(y = 0, label = as.character(Age)), size = 3) + 
    coord_flip() + 
    opts(title = 'Ages', 
     legend.position = 'none' , 
     axis.text.y = theme_blank(), 
     axis.title.y = theme_blank(), 
     axis.text.x = theme_blank(), 
     axis.ticks = theme_blank(),   
     plot.title = theme_text(size = 10))  

Wreszcie zorganizować działek, na siatce, z zastosowaniem metody w książce Hadley Wickhama:

grid.newpage() 

pushViewport(viewport(layout = grid.layout(1,3, widths = c(.4,.2,.4)))) 

vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y) 

print(gg.female, vp = vplayout(1,1)) 
print(gg.ages, vp = vplayout(1,2)) 
print(gg.male, vp = vplayout(1,3)) 

alt text

+0

Jak mogę zwiększyć rozmiar etykiet nawiasów, bez naruszania wyrównania lewej i prawej strony? –

11

Nieznaczne Tweak:

library(ggplot2) 
library(plyr) 
library(gridExtra) 

## The Data 
df <- data.frame(Type = sample(c('Male', 'Female', 'Female'), 1000, replace=TRUE), 
    Age = sample(18:60, 1000, replace=TRUE)) 

AgesFactor <- ordered(cut(df$Age, breaks = c(18,seq(20,60,5)), 
    include.lowest = TRUE)) 

df$Age <- AgesFactor 

## Plotting 
gg <- ggplot(data = df, aes(x=Age)) 

gg.male <- gg + 
    geom_bar(data=subset(df,Type == 'Male'), 
     aes(y = ..count../sum(..count..), fill = Age)) + 
    scale_y_continuous('', labels = scales::percent) + 
    theme(legend.position = 'none', 
     axis.title.y = element_blank(), 
     plot.title = element_text(size = 11.5), 
     plot.margin=unit(c(0.1,0.2,0.1,-.1),"cm"), 
     axis.ticks.y = element_blank(), 
     axis.text.y = theme_bw()$axis.text.y) + 
    ggtitle("Male") + 
    coord_flip()  

gg.female <- gg + 
    geom_bar(data=subset(df,Type == 'Female'), 
     aes(y = ..count../sum(..count..), fill = Age)) + 
    scale_y_continuous('', labels = scales::percent, 
        trans = 'reverse') + 
    theme(legend.position = 'none', 
     axis.text.y = element_blank(), 
     axis.ticks.y = element_blank(), 
     plot.title = element_text(size = 11.5), 
     plot.margin=unit(c(0.1,0,0.1,0.05),"cm")) + 
    ggtitle("Female") + 
    coord_flip() + 
    ylab("Age") 

## Plutting it together 
grid.arrange(gg.female, 
    gg.male, 
    widths=c(0.4,0.6), 
    ncol=2 
) 

enter image description here

ja nadal chce grać z marginesami nieco więcej (może panel.margin pomogłyby w wywołaniu theme również).

+1

Znacznie lepiej, thnx. Wydaje się, że wywołanie 'opts()' jest przestarzałe i użycie 'theme()' jest ważne w tych dniach. –

+0

@Ben Dokonałem tutaj edycji, aby przenieść twoją odpowiedź na nowsze zmiany na 'ggplot2'. Włączyłem też bezpośrednio reakcję Prasad Chalasani, zamiast ją wykorzystywać. Jeśli Ci się nie podoba, możesz wrócić do niego. –

+1

Otrzymuję błąd; 'Błąd: nieznane parametry: podzbiór'. Podejrzewam, że jest z linii 18; 'geom_bar (podzbiór =. (Type == 'Male')'. Czy jest to przestarzała składnia? Używam R 3.3.0 i ggplot2 2.1.0 – user5359531

10

Zrobiłem to z małym obejściem - zamiast używać geom_bar, użyłem geom_linerange i geom_label.

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

population <- read.csv("https://raw.githubusercontent.com/andriy-gazin/datasets/master/ageSexDistribution.csv") 

population %<>% 
    tidyr::gather(sex, number, -year, - ageGroup) %>% 
    mutate(ageGroup = gsub("100 і старше", "≥100", ageGroup), 
    ageGroup = factor(ageGroup, 
         ordered = TRUE, 
         levels = c("0-4", "5-9", "10-14", "15-19", "20-24", 
            "25-29", "30-34", "35-39", "40-44", 
            "45-49", "50-54", "55-59", "60-64", 
            "65-69", "70-74", "75-79", "80-84", 
            "85-89", "90-94", "95-99", "≥100")), 
    number = ifelse(sex == "male", number*-1/10^6, number/10^6)) %>% 
    filter(year %in% c(1990, 1995, 2000, 2005, 2010, 2015)) 

png(filename = "~/R/pyramid.png", width = 900, height = 1000, type = "cairo") 

ggplot(population, aes(x = ageGroup, color = sex))+ 
    geom_linerange(data = population[population$sex=="male",], 
       aes(ymin = -0.3, ymax = -0.3+number), size = 3.5, alpha = 0.8)+ 
    geom_linerange(data = population[population$sex=="female",], 
       aes(ymin = 0.3, ymax = 0.3+number), size = 3.5, alpha = 0.8)+ 
    geom_label(aes(x = ageGroup, y = 0, label = ageGroup, family = "Ubuntu Condensed"), 
     inherit.aes = F, 
     size = 3.5, label.padding = unit(0.0, "lines"), label.size = 0, 
     label.r = unit(0.0, "lines"), fill = "#EFF2F4", alpha = 0.9, color = "#5D646F")+ 
    scale_y_continuous(breaks = c(c(-2, -1.5, -1, -0.5, 0) + -0.3, c(0, 0.5, 1, 1.5, 2)+0.3), 
       labels = c("2", "1.5", "1", "0.5", "0", "0", "0.5", "1", "1.5", "2"))+ 
    facet_wrap(~year, ncol = 2)+ 
    coord_flip()+ 
labs(title = "Піраміда населення України", 
    subtitle = "Статево-вікові групи у 1990-2015 роках, млн осіб", 
    caption = "Дані: Держкомстат України")+ 
    scale_color_manual(name = "", values = c(male = "#3E606F", female = "#8C3F4D"), 
       labels = c("жінки", "чоловіки"))+ 
    theme_minimal(base_family = "Ubuntu Condensed")+ 
theme(text = element_text(color = "#3A3F4A"), 
    panel.grid.major.y = element_blank(), 
    panel.grid.minor = element_blank(), 
    panel.grid.major.x = element_line(linetype = "dotted", size = 0.3, color = "#3A3F4A"), 
    axis.title = element_blank(), 
    plot.title = element_text(face = "bold", size = 36, margin = margin(b = 10), hjust = 0.030), 
    plot.subtitle = element_text(size = 16, margin = margin(b = 20), hjust = 0.030), 
    plot.caption = element_text(size = 14, margin = margin(b = 10, t = 50), color = "#5D646F"), 
    axis.text.x = element_text(size = 12, color = "#5D646F"), 
    axis.text.y = element_blank(), 
    strip.text = element_text(color = "#5D646F", size = 18, face = "bold", hjust = 0.030), 
    plot.background = element_rect(fill = "#EFF2F4"), 
    plot.margin = unit(c(2, 2, 2, 2), "cm"), 
    legend.position = "top", 
    legend.margin = unit(0.1, "lines"), 
    legend.text = element_text(family = "Ubuntu Condensed", size = 14), 
    legend.text.align = 0) 

dev.off() 

i oto powstały działki:

and here's the resulting plot

+0

To jest doskonała demonstracja jak dostroić tabelę 'ggplot2' Dziękuję za udostępnienie – Uwe

+0

Używając aktualnej wydanej wersji ggplot2, 'legend.margin'should musi być" legend.spacing ". – Tavrock

0

lubiłem działek @ Andrij wystarczy, aby uproszczony niestandardową funkcję z niego:

danych powinien wyglądać tak, a ageGroup być czynnikiem uporządkowanym.

head(population) 
# ageGroup sex number 
# 1  0-4 male 1.896459 
# 2  5-9 male 1.914255 
# 3 10-14 male 1.832594 
# 4 15-19 male 1.849453 
# 5 20-24 male 1.658733 
# 6 25-29 male 1.918060 

Następnie podać dane i przerwy:

pyramid(population,c(0, 0.5, 1, 1.5, 2)) 

W razie potrzeby, tworzenie grup wiekowych można zrobić za pomocą funkcji age_cat, że wziąłem z this blog. Zobacz kod poniżej. Lekko edytowałem oryginalną nazwę i domyślne parametry.

Na przykład:

age_column <- sample(0:110,10000,TRUE) 
table(age_cat(age_column)) 
# 0-9 10-19 20-29 30-39 40-49 50-59 60-69 70-79 80-89 90-99 100+ 
# 885 836 885 927 942 953 886 882 935 872 997 

funkcje

pyramid <- function(data,.breaks){ 
ggplot(data, aes(x = ageGroup, color = sex))+ 
    geom_linerange(data = data[data$sex=="male",], 
       aes(ymin = -tail(.breaks,1)/7, ymax = -tail(.breaks,1)/7-number), size = 3.5, alpha = 0.8)+ 
    geom_linerange(data = data[data$sex=="female",], 
       aes(ymin = tail(.breaks,1)/7, ymax = tail(.breaks,1)/7+number), size = 3.5, alpha = 0.8)+ 
    geom_label(aes(x = ageGroup, y = 0, label = ageGroup), 
      inherit.aes = F, 
      size = 3.5, label.padding = unit(0.0, "lines"), label.size = NA, 
      label.r = unit(0.0, "lines"), fill = "white", alpha = 0.9, color = "#5D646F")+ 
    scale_y_continuous(breaks = c(-rev(.breaks) -tail(.breaks,1)/7, .breaks+tail(.breaks,1)/7), 
        labels = c(rev(.breaks),.breaks))+ 
    coord_flip()+ 
    scale_color_manual(name = "", values = c(male = "#3E606F", female = "#8C3F4D"))+ 
    theme_minimal()+ 
    theme(text = element_text(color = "#3A3F4A"), 
     panel.grid.major.y = element_blank(), 
     panel.grid.minor = element_blank(), 
     panel.grid.major.x = element_line(linetype = "dotted", size = 0.3, color = "#3A3F4A"), 
     axis.title = element_blank(), 
     axis.text.x = element_text(size = 12, color = "#5D646F"), 
     axis.text.y = element_blank(), 
     strip.text = element_text(color = "#5D646F", size = 18, face = "bold", hjust = 0.030), 
     legend.position = "none") 
} 

age_cat <- function(x, lower = 0, upper = 100, by = 5, 
        sep = "-", above.char = "+") { 

    labs <- c(paste(seq(lower, upper - by, by = by), 
        seq(lower + by - 1, upper - 1, by = by), 
        sep = sep), 
      paste(upper, above.char, sep = "")) 

    cut(floor(x), breaks = c(seq(lower, upper, by = by), Inf), 
     right = FALSE, labels = labs) 
} 

danych

library(dplyr) 
library(ggplot2) 
population <- read.csv("https://raw.githubusercontent.com/andriy-gazin/datasets/master/ageSexDistribution.csv") 
population <- population %>% 
    tidyr::gather(sex, number, -year, - ageGroup) %>% 
    mutate(ageGroup = factor(ageGroup, 
          ordered = TRUE, 
          levels = c("0-4", "5-9", "10-14", "15-19", "20-24", 
             "25-29", "30-34", "35-39", "40-44", 
             "45-49", "50-54", "55-59", "60-64", 
             "65-69", "70-74", "75-79", "80-84", 
             "85-89", "90-94", "95-99", "100+")), 
     ageGroup = `[<-`(ageGroup,is.na(ageGroup),value="100+"), 
     number = number/10^6) %>% 
    dplyr::filter(year == 1990) %>% 
    select(-year) 
Powiązane problemy