2017-09-25 1 views
3

Ich habe eine Funktion erstellt, die eine Reihe von biologischen Statistiken berechnet, wie z. Hier ist eine vereinfachte Version der Funktion:Funktion auf gruppierte Zeilen im Datenframe anwenden

range_stats <- function(rangedf, lat, lon, weighting, na.rm=T){ 
    cent_lat <- weighted.mean(x=rangedf[,lat], w=rangedf[,weighting], na.rm=T) 
    cent_lon <- weighted.mean(x=rangedf[,lon], w=rangedf[,weighting], na.rm=T) 
out <- data.frame(cent_lat, cent_lon)  
return(out) 
} 

Ich mag würde dies zu einem großen Datenrahmen anzuwenden, in denen jede Zeile eine Beobachtung einer Spezies ist. Daher möchte ich, dass die Funktion Zeilen nach einer bestimmten Gruppe von Spalten gruppiert und diese Statistiken dann für jede Gruppe computert. Hier ist ein Test Datenrahmen:

LATITUDE <- c(27.91977, 21.29066, 26.06340, 28.38918, 25.97517, 27.96313) 
LONGITUDE <- c(-175.8617, -157.8645, -173.9593, -178.3571, -173.9679, -175.7837) 
BIOMASS <- c(4.3540488, 0.2406332, 0.2406332, 2.1419699, 0.3451426, 1.0946017) 
SPECIES <- c('Abudefduf abdominalis','Abudefduf abdominalis','Abudefduf abdominalis','Chaetodon lunulatus','Chaetodon lunulatus','Chaetodon lunulatus') 
YEAR <- c('2005', '2005', '2014', '2009', '2009', '2015') 
testdf <- data.table(LATITUDE, LONGITUDE, BIOMASS, SPECIES, YEAR) 

Ich mag diese Funktion zu jeder einzigartigen Kombination von Arten anzuwenden und Jahr zusammenfassende Statistiken zu berechnen, das heißt, die folgenden:

testresult <- testdf %>% 
    group_by(SPECIES, YEAR) %>% 
    range_stats(lat="LATITUDE",lon="LONGITUDE",weighting="BIOMASS",na.rm=T) 

jedoch der Code oben tut nicht funktionieren (Ich bekomme eine (list) object cannot be coerced to type 'double' Fehler) und ich bin mir nicht sicher, wie sonst das Problem anzugehen.

Antwort

1

Da Sie die Tags dplyr und purrr hinzufügen, nehme ich an, dass Sie an einer tidyverse Lösung interessiert sind. Im Folgenden werde ich eine Lösung auf Basis der tidyverse demonstrieren.

Zunächst ist Ihre range_stats problematisch. Deshalb haben Sie die Fehlermeldung erhalten. Die weighted.mean erwartet einen Vektor für die beiden x und w Argument. Wenn rangedf jedoch eine tibble ist, wird die Art und Weise, wie Sie die tibbletibble wie rangedf[,lat] noch eine einspaltige tibble zurückgeben. Ein besserer Weg ist, pull aus dem dplyr Paket zu verwenden.

library(tidyverse) 
range_stats <- function(rangedf, lat, lon, weighting, na.rm=T){ 
    cent_lat <- weighted.mean(x = rangedf %>% pull(lat), 
          w = rangedf %>% pull(weighting), na.rm=T) 
    cent_lon <- weighted.mean(x = rangedf %>% pull(lon), 
          w = rangedf %>% pull(weighting), na.rm=T) 
    out <- data.frame(cent_lat, cent_lon)  
    return(out) 
} 

Als nächstes wird die Art und Weise Sie den Datenrahmen erstellt ist OK, aber data.table ist vom data.table Paket und Sie werden ein data.table, kein tibble erstellen. Ich dachte, Sie wollen einen Ansatz von tidyverse verwenden, also änderte ich data.table zu data_frame wie folgt.

LATITUDE <- c(27.91977, 21.29066, 26.06340, 28.38918, 25.97517, 27.96313) 
LONGITUDE <- c(-175.8617, -157.8645, -173.9593, -178.3571, -173.9679, -175.7837) 
BIOMASS <- c(4.3540488, 0.2406332, 0.2406332, 2.1419699, 0.3451426, 1.0946017) 
SPECIES <- c('Abudefduf abdominalis','Abudefduf abdominalis','Abudefduf abdominalis','Chaetodon lunulatus','Chaetodon lunulatus','Chaetodon lunulatus') 
YEAR <- c('2005', '2005', '2014', '2009', '2009', '2015') 
testdf <- data_frame(LATITUDE, LONGITUDE, BIOMASS, SPECIES, YEAR) 

Nun, Sie sagten, Sie die range_stats Funktion zu jeder Kombination von SPECIES und YEAR anwenden möchten. Ein Ansatz besteht darin, den Datenrahmen in eine Liste von Datenrahmen aufzuteilen und die Familienfunktion lapply zu verwenden. Aber hier möchte ich Ihnen zeigen, wie man die map Familienfunktion verwendet, um diese Aufgabe zu erfüllen, da map aus dem purrr Paket ist, das Teil der tidyverse ist.

Wir können zuerst eine Gruppe Indizes basierend auf SPECIES und YEAR erstellen.

testdf2 <- testdf %>% 
    mutate(Group = group_indices(., SPECIES, YEAR)) 
testdf2 
# A tibble: 6 x 6 
    LATITUDE LONGITUDE BIOMASS    SPECIES YEAR Group 
    <dbl>  <dbl>  <dbl>     <chr> <chr> <int> 
1 27.91977 -175.8617 4.3540488 Abudefduf abdominalis 2005  1 
2 21.29066 -157.8645 0.2406332 Abudefduf abdominalis 2005  1 
3 26.06340 -173.9593 0.2406332 Abudefduf abdominalis 2014  2 
4 28.38918 -178.3571 2.1419699 Chaetodon lunulatus 2009  3 
5 25.97517 -173.9679 0.3451426 Chaetodon lunulatus 2009  3 
6 27.96313 -175.7837 1.0946017 Chaetodon lunulatus 2015  4 

Wie Sie sehen können, ist Group eine neue Spalte die Indexnummer zeigt. Jetzt können wir den Datenrahmen basierend auf Group aufteilen und dann map_dfr verwenden, um die range_stats Funktion anzuwenden.

testresult <- testdf2 %>% 
    split(.$Group) %>% 
    map_dfr(range_stats, lat = "LATITUDE",lon = "LONGITUDE", 
      weighting = "BIOMASS", na.rm = TRUE, .id = "Group") 
testresult 
    Group cent_lat cent_lon 
1  1 27.57259 -174.9191 
2  2 26.06340 -173.9593 
3  3 28.05418 -177.7480 
4  4 27.96313 -175.7837 

Hinweis, dass map_dfr kann die automatische bind der Ausgabeliste von Datenrahmen zu einem einzelnen Datenrahmen. .id = "Group" bedeutet, dass wir basierend auf dem Namen des Listenelements eine Spalte namens Group erstellen möchten.

Ich habe den Prozess in zwei Schritte getrennt, aber natürlich können sie alle in einer Pipeline wie folgt sein.

testresult <- testdf %>% 
    mutate(Group = group_indices(., SPECIES, YEAR)) %>% 
    split(.$Group) %>% 
    map_dfr(range_stats, lat = "LATITUDE",lon = "LONGITUDE", 
      weighting = "BIOMASS", na.rm = TRUE, .id = "Group") 

Wenn Sie möchten, testresult mit testdf mit left_join zusammengeführt werden, aber ich werde hier aufhören als testresult wahrscheinlich ist bereits die gewünschte Ausgabe, die Sie wollen. Ich hoffe das hilft.

+1

Perfekte Lösung, danke! Ich habe es genau implementiert, außer "mutate" (Group = group_indices (., SPECIES, YEAR)) ', habe ich einen aussagekräftigen Spaltennamen verwendet, so dass der Bezeichner in der Ausgabe df sinnvoll ist:' muate (GROUPID = paste (YEAR, Spezies)) '. – AFH

+0

@AFH Schöne Möglichkeit, aussagekräftige Gruppenname oder ID zu erstellen. Danke für das Teilen. – www

1

Grundlegend bezieht sich das Hauptproblem auf weighted.mean(), wo Sie ein Dataframe-Objekt übergeben und nicht einen Vektor, der zum Verdoppeln gezwungen werden kann.

x=rangedf[,lat] 

Um doppelte eckige Klammern: Um dies zu beheben innerhalb Methode, einfach ändern

x=rangedf[[lat]] 

Adjusted Methode:

range_stats <- function(rangedf, lat, lon, weighting, na.rm=T){ 
    cent_lat <- weighted.mean(x=rangedf[[lat]], w=rangedf[[weighting]], na.rm=T) 
    cent_lon <- weighted.mean(x=rangedf[[lon]], w=rangedf[[weighting]], na.rm=T) 
    out <- data.frame(cent_lat, cent_lon)  
    return(out) 
} 

Wie für die gesamte Gruppe durch Berechnung in Scheiben schneiden, vergib mir in der Umgehung, dplyr und data.table die Sie verwenden und die Basis R's nicht ausgelastet betrachten, aber u seful Methode, by().

Die Herausforderung mit Ihrem aktuellen Setup ist die Ausgabe von range_stats Methode Rückkehr group_by() erwartet Aggregation Vektoroperation eine eine data.frame von zwei Säulen und dplyr der ist. Allerdings geht by Datenrahmen Objekte (von Faktoren in Scheiben geschnitten) in eine definierte Funktion eine Liste von data.frames zurückzukehren, die Sie dann rbind für einen letzten Datenrahmen:

df_List <- by(testdf, testdf[, c("SPECIES", "YEAR")], FUN=function(df) 
       data.frame(species=df$SPECIES[1], 
          year=df$YEAR[1], 
          range_stats(df,"LATITUDE","LONGITUDE","BIOMASS")) 
      ) 

finaldf <- do.call(rbind, df_List) 
finaldf 
#     species year cent_lat cent_lon 
# 1 Abudefduf abdominalis 2005 27.57259 -174.9191 
# 2 Chaetodon lunulatus 2009 28.05418 -177.7480 
# 3 Abudefduf abdominalis 2014 26.06340 -173.9593 
# 4 Chaetodon lunulatus 2015 27.96313 -175.7837 
Verwandte Themen