2016-07-28 30 views
0

Ich versuche den jährlichen mittleren Niederschlag für jedes Jahr zwischen 1950-2015 für mehrere Messstationen zu extrahieren. Der Niederschlagsdatensatz besteht aus täglichen Messungen.Bedingtes Entfernen von Zeilen für gruppierte Daten in R

Bevor ich das jährliche Mittel für jede einzelne Station berechne, muss ich die Daten filtern, so dass jeder Monat Daten von mehr als 15 Tagen haben muss.

Wie könnte dies in R getan werden?

Lassen Sie diese ein funktionierendes Beispiel sein:

id<-rep(c("Station_1","Station_2","Station_3"),length(1),each=1080) 
year<-rep(c(1950:1952),length(1:3),each=360) 
month <- rep(c(1:12),length(1:9),each=30) 
day <- rep(c(1:30),length(1:108)) 
value<-runif(3240, min=0, max=10) 

df<-data.frame(cbind(id,year,month,day,value)) 

Ich habe versucht, so etwas wie:

result<-df %>% 
    group_by(id,year,month) %>% 
    summarise(No._of_days=n(),mean_month=mean(value)) 
result<-result[!(result$No._of_days<15),] 
result<-result %>% 
    group_by(id,year) %>% 
    summarise(No._of_months=n(),mean_year=mean(mean_month)) 

jedoch diese auf die falsche Lösung führt, wie ich „von einem Mittelwert einer mittleren“ nehme.

Vielen Dank für jeden Hinweis.

+0

Bitte beenden Sie die Verwendung von 'data.frame (cbind (...))' '. 'data.frame()' ist ausreichend. Schauen Sie sich "data.frame" an. – Arun

+0

Danke Sathish, Aichao, Sebolus und NJBurgo für Ihre schnelle und korrekte Antworten - Ihre Hilfe wird sehr geschätzt! Alle Lösungen funktionieren, aber ich bleibe bei aichaos einfach so, wie ich am liebsten mit dplyr arbeite. Tut mir leid, dass ich den Datenframe falsch formuliere - ich werde "so wie es ist" jetzt verlassen, weil jeder hier einen guten Kommentar hinzugefügt hat, wie man ihn richtig strukturiert. –

Antwort

1

Rohdaten: Entfernen Sie die Standardfaktorierung von Variablen beim Erstellen von Datenrahmen.

df<-data.frame(id = id,year = year, month = month, day = day, value = value, stringsAsFactors = FALSE) 

Gruppe die Daten von id, year, month und die Anzahl der Tage für die Teilmenge erhalten mit .N interne Variable. Als nächstes ketten Sie die Ergebnisse (ähnlich wie%>% in dplyr). Jetzt wird die Gruppe id, year, gefolgt von der Bedingung N > 15 und schließlich der mean Niederschlag für diese Teilmenge berechnet und in avg_rainfall gespeichert.

setDT wandelt Datenrahmen zu Datentabelle durch Verweis

library("data.table") 
setDT(df)[, .(value, .N), by = .(id, year, month)][N > 15, .(avg_rainfall = mean(value)), by = .(id, year)] 

#   id year avg_rainfall 
# 1: Station_1 1950  4.852840 
# 2: Station_1 1951  5.138069 
# 3: Station_1 1952  4.934006 
# 4: Station_2 1950  4.870335 
# 5: Station_2 1951  5.179425 
# 6: Station_2 1952  5.055026 
# 7: Station_3 1950  4.959524 
# 8: Station_3 1951  5.049996 
# 9: Station_3 1952  4.927548 
+0

vielen dank für ihre schnelle und klare lösung - ich schätze es sehr! Entschuldigungen über die falsche Formulierung des Datenrahmens - wird für das nächste Mal im Auge behalten. –

+0

eine weitere Frage, anstatt Zeilen zu filtern, die> 15 Tage haben, kann ich stattdessen filtern, wenn 15 aufeinander folgende Zellen NAs sind? ZB .... df [,! Anwenden (is.na (df), 2, Funktion (x) {v <- cumsum (x); beliebig (diff (v, 2) == 2)})]. .... Ich werde meine Frage bei Bedarf aktualisieren. Angepasst von diesem Pfosten: http://stackoverflow.com/questions/15186697/conditional-row-removal-based-on-number-of-nas-within-the-row –

+0

Wenn Ihre Absicht ist, NA vor dem Berechnen des Mittels zu entfernen , könnten Sie 'na.rm = TRUE' innerhalb der Mittelwertfunktion übergeben. Zum Beispiel: 'avg_rainfall = mean (Wert, na.rm = TRUE)' – Sathish

1

Ein paar Dinge, das Beispiel data.frame, weil, wie Sie es erstellt, Codes alles als ein Faktor. Verwenden Sie stattdessen:

df<-data.frame(id = id, 
       year = year, 
       month = month, 
       day = day, 
       value = value) 

Dann (und ich bedaure, ich bin kein magrittr Fan) folgendes funktioniert:

# Filter into a new data.frame 
df2 <- semi_join(df, 
      filter(summarise(group_by(df, year, month), N = n()), N > 15), 
      by = c(year = "year", 
       month = "month")) 

# Summarise 
summarise(group_by(df2, id), 
      value = mean(value, na.rm = TRUE)) 
+0

Danke @NJBurgo, für die großartige Lösung und schnelle Antwort auf die Frage! Prost, C –

0

Fokussierung nur auf den Code, es funktioniert für mich, mit einigen Änderungen. Z.B. Sie schreiben, dass Sie wollen> 15 Tage Daten, aber wählen Sie> 14, stellen Sie auch sicher, Werte sind numerisch, nicht Faktor in Ihrem df.

`df<-data.frame(cbind(id,year,month,day,value)) 
df$value<- as.numeric(as.character(df$value)) 
    result<-df %>% 
    group_by(id,year,month) %>% 
    summarise(No._of_days=n(),mean_month=mean(value)) 
result<-result[!(result$No._of_days<=15),] 
result<-result %>% 
    group_by(id,year) %>% 
    summarise(No._of_months=n(),mean_year=mean(mean_month))` 

Code beiseite: Aus mathematischer Sicht ist mir dieser Ansatz nicht klar. Warum möchten Sie alles ausschließen, das weniger als 16 Messungen für die jährliche Aggregation hat? Wenn das Vorhandensein Ihrer Messungen wirklich zufällig ist, sollten Sie die Werte für jeden Monat verwenden und alle fehlenden Tage in Ihrem Dataset angeben (z. B. Regression für jede Station, Jahr (kontinuierlich), Kalenderwoche, Niederschlag auf Vorheriges und Nachfolgendes) Tag).

+0

Dank @sebolus für Ihre Lösung - Ich schätze die schnelle Abwicklung sehr. Re. In der Mathematik ist meine Methode der Untergliederung getan, weil jahreszeitliche Unterschiede im Niederschlag über das Jahr hinweg verloren gehen können, wenn einige Monate bei der Formulierung des Jahresmittels schlecht dargestellt werden. Obwohl Ihre Imputationslösung durchführbar ist, besteht eine übliche Methode, die in der vorhandenen Literatur zu Niederschlags-/Wind-/Temp-Daten usw. verwendet wird, darin, diese Monate (und dann gegebenenfalls Jahre) einfach auszuschließen, um dieses Problem zu vermeiden. Für den Moment bleibe ich beim Subsetting-Ansatz. Danke, Cai –

0

Ein etwas anderer Ansatz dplyr und magrittr:

library(dplyr) 
set.seed(42) # this is only so we get consistent results to compare 

# then generate id, year month, day, and value using your code 
# but use what @NJBurgo used to generate df 

df<-data.frame(id = id, 
     year = year, 
     month = month, 
     day = day, 
     value = value) 

result <- df %>% 
    group_by(id,year,month) %>% 
    mutate(No._of_days=n()) %>% 
    filter(No._of_days > 15) %>% ## keep only rows with number of days greater than 15 
    ungroup() %>% 
    group_by(id,year) %>% 
    summarize(mean_year=mean(value)) 

# using set.seed(42), you should get 
print(result) 
##   id year mean_year 
##  <fctr> <int>  <dbl> 
##1 Station_1 1950 4.954538 
##2 Station_1 1951 4.878709 
##3 Station_1 1952 4.737996 
##4 Station_2 1950 4.942614 
##5 Station_2 1951 4.876992 
##6 Station_2 1952 5.193242 
##7 Station_3 1950 5.235278 
##8 Station_3 1951 4.955401 
##9 Station_3 1952 4.905078 

Der Schlüssel ist, zu filter die group_by Zeilen (gruppierten nach id, year und month).Ich habe auch mutate anstelle von summarise verwendet, um No._of_days zu erstellen. Dann ungroup() und dann durch id und gruppieren, um den Mittelwert zu berechnen.

BTW, Ihr Testfall ist nicht ausreichend, um dies zu testen, da alle Monate 30 Tage haben.

Hoffe, das hilft.

+0

Danke @aichao - Ich mag diese Lösung, vor allem, da ich am bequemsten mit dplyr arbeite. Punkt über den Datenrahmen genommen - ich dachte, es ist wichtig, ein Beispiel zu erwähnen, um vorbei zu gehen, auch wenn es ein schlechter war! Ich hatte vorher noch keinen Dummy-Datensatz erstellt und wollte nicht zu lange herausfinden, wie ich eine realistischere Replikation der realen Daten erstellen konnte, da ich wusste, dass das Konzept des Filterns nach Tagen immer noch wahr ist. Vielen Dank –