2017-09-14 4 views
2

sagt, dass ich einen Datenrahmen wie diese:ggplot2, facet_wrap: Daten zweimal in verschiedenen Facetten Plotten

df <- data.frame(year_day = rep(1:365, 3), 
       year = rep(2001:2003, each = 365), 
       value = sin(2*pi*rep(1:365, 3)/365)) 

Es gewissen Wert (value) für jeden Tag des Jahres (year_day) zwischen 2001 und 2003 darstellt. Ich möchte jedes Jahr plotten und dazu ggplot2 verwenden.

ggplot(df) + geom_point(aes(year_day, value)) + facet_wrap(~year, ncol=1) 

Das gibt mir:

enter image description here

Großen. Nun, sagen wir, ich möchte meine Plot-Region etwas erweitern, so dass jedes Jahr 3 Monate des vorhergehenden Jahres und 3 Monate des nächsten Jahres umfasst (wenn diese Daten existieren). Dies bedeutet, dass einige Daten doppelt geplottet werden. Zum Beispiel werden die ersten drei Monate von 2003 in den Plots für 2002 und 2003 erscheinen. Also könnte ich diese Zeilen duplizieren und sie 2002 zuweisen, aber mit year-day s 366 bis 485. Das funktioniert, ist aber cluddy. Gibt es eine elegantere Lösung?

Antwort

0

Edited alte Version zu entfernen und

Das ist etwas zu ersetzen, das ich für eine Weile darüber nachzudenken, gewesen war, so war dies ein guter Grund, um zu versuchen, sie umzusetzen. Es geht immer noch darum, Zeilen zu duplizieren, was kludgy ist, aber es ist der beste Weg, an den ich denken könnte.

Dies ist eine ordentliche Pipe-fähige Funktion, die ein Dataframe (auch ein gruppiertes) als erstes Argument und eine Datumsspalte als zweites verwendet. Es gibt ein optionales drittes Argument, um die Ausdehnung jedes Fensters zu erweitern (standardmäßig auf 0,25 oder 3 Monate). Das vierte Argument wäre für Dinge wie fiskalische oder akademische Jahre, die nicht Jan-Jan sind, aber ich habe noch nicht tief durchdacht.

Der Ausgang ist der gleiche Datenrahmen, mit den duplizierten Zeilen für den Schwanz der Jahre, mit zusätzlichen Spalten für den Tag des Jahres doy_wrapped (von Negativ zu> 365 gehen) und nominal_yr, das ist die Jahr, auf das jedes Fenster zentriert ist.

Beispiel, unter Verwendung des Datensatzes ggplot2::economics:

library(dplyr) 
library(lubridate) 

economics %>% 
    filter(year(date) > 2007) 
# A tibble: 88 x 6 
     date  pce pop psavert uempmed unemploy 
     <date> <dbl> <int> <dbl> <dbl> <int> 
1 2008-01-01 9963.2 303506  3.4  9.0  7685 
2 2008-02-01 9955.7 303711  3.9  8.7  7497 
3 2008-03-01 10004.2 303907  4.0  8.7  7822 
4 2008-04-01 10044.6 304117  3.5  9.4  7637 
5 2008-05-01 10093.3 304323  7.9  7.9  8395 
6 2008-06-01 10149.4 304556  5.6  9.0  8575 
7 2008-07-01 10151.1 304798  4.4  9.7  8937 
8 2008-08-01 10140.3 305045  3.7  9.7  9438 
9 2008-09-01 10083.2 305309  4.4 10.2  9494 
10 2008-10-01 9983.3 305554  5.4 10.4 10074 
# ... with 78 more rows 

economics %>% 
    filter(year(date) > 2007) %>% 
    wrap_years(date, expand = 3/12) 
# A tibble: 136 x 8 
# Groups: nominal_yr [8] 
     date  pce pop psavert uempmed unemploy nominal_yr doy_wrapped 
     <date> <dbl> <int> <dbl> <dbl> <int>  <dbl>  <dbl> 
1 2008-01-01 9963.2 303506  3.4  9.0  7685  2008   1 
2 2008-02-01 9955.7 303711  3.9  8.7  7497  2008   32 
3 2008-03-01 10004.2 303907  4.0  8.7  7822  2008   61 
4 2008-04-01 10044.6 304117  3.5  9.4  7637  2008   92 
5 2008-05-01 10093.3 304323  7.9  7.9  8395  2008   122 
6 2008-06-01 10149.4 304556  5.6  9.0  8575  2008   153 
7 2008-07-01 10151.1 304798  4.4  9.7  8937  2008   183 
8 2008-08-01 10140.3 305045  3.7  9.7  9438  2008   214 
9 2008-09-01 10083.2 305309  4.4 10.2  9494  2008   245 
10 2008-10-01 9983.3 305554  5.4 10.4 10074  2009   -90 
# ... with 126 more rows 

Das macht es etwas kaputt; es verdreifacht Reihen in ihrer Reihenfolge und weist sie dann den benachbarten Jahren zu. Es behält die ursprüngliche Gruppierung bei und fügt eine für die neue nominal_yr hinzu (um möglicherweise verwaiste Schwänze zu entfernen, wenn die Daten zum zentralen Jahr fehlen).

economics %>% 
    filter(year(date) > 2007) %>% 
    wrap_years(date, expand = 3/12) %>% 
    ggplot(aes(doy_wrapped, unemploy)) + 
    geom_line() + facet_wrap(~nominal_yr, ncol = 3) 

enter image description here

Und dann ein paar Tricks es sich zu verkleiden und korrigieren, die die Achse:

economics %>% 
    filter(year(date) > 2007) %>% 
    wrap_years(date, expand = 3/12) %>% 
    ggplot(aes(doy_wrapped + ymd("1900-01-01") - 1, unemploy)) + 
    geom_line() + facet_wrap(~nominal_yr, ncol = 2) + 
    geom_vline(xintercept = as.numeric(c(ymd("1900-01-01"), ymd("1901-01-01")))) + 
    scale_x_date(date_breaks = "2 months",date_labels = "%b", 
       name = NULL, expand = c(0,0) + 
    theme_minimal() + 
    theme(panel.spacing.x = unit(1, "cm")) 

Die + ymd("1900-01-01") - 1 im aes(...) willkürlich ist, Sie wollen, dass es nur mit einem Line-Up 1. Januar, so dass jedes Jahr die richtigen Monate hat. Dann passen Sie es an die xintercept = in den vertikalen Linien an.

Im Idealfall würde dies schließlich Teil einer Familie von wrap_* Funktionen sein, für die Quartale, Monate, Stunden, Jahrzehnte usw.

enter image description here

-Code für die Funktion:

wrap_years <- function(df, datecol, expand = 0.25, offset = "2001-01-01") { 

    if(!is.data.frame(df)) {return(df)} 

    datecol <- enquo(datecol) 

    if(expand > 1) { 
    warning(paste0("Window expansions of > 1 are not supported.")) 
    return(df) 
    } 


    if(!(quo_name(datecol) %in% names(df))) { 
    warning(paste0("Column '", quo_name(datecol), "' not found in data.")) 
    return(df) 
    } 

    # offset <- as_date(offset) 
    # warning(paste0("Using ", stamp("August 26", orders = "md")(offset), 
    #    " as start of year. Not yet implemented.")) 

    if(!is.Date(df %>% pull(!!datecol))) { 
    warning(paste0("Use lubridate functions to parse '", 
        quo_name(datecol), 
        "' before proceeding.")) 
    return(df) 
    } 

    df %>% 
    mutate(adj_wrap = list(-1:1)) %>% 
    tidyr::unnest() %>% 
    mutate(nominal_yr = year(!!datecol) -  adj_wrap, 
      doy_wrapped = yday(!!datecol) + 365*adj_wrap) %>% 
    filter(between(doy_wrapped, -expand * 365, (1 + expand) * 365)) %>% 
    select(-adj_wrap) %>% 
    group_by(nominal_yr, add = T) %>% 
    filter(sum(year(!!datecol) != nominal_yr) != length(nominal_yr)) 

} 

I Ich hatte angenommen, dass das Kopieren der geringsten Zeilen die schnellste Methode sein würde. Das war das Paradigma, das hinter meinem ersten Versuch steckte. Als ich später darüber nachdachte, wurde mir klar, dass ein naiverer Ansatz wäre, einfach alle Zeilen zu kopieren, was sich als viel schneller herausstellte. Dann wird der Filterschritt mit between gemacht, was auch schnell ist. Diese Version der Funktion ist etwa doppelt so schnell wie die vorherige Version (aber etwa das 0,01-fache der Geschwindigkeit beim Zeichnen der Rohdaten).

Verwandte Themen