Lesen mehrerer Dateien und Berechnen des Mittelwerts basierend auf Benutzereingaben

Ich versuche eine function in R zu schreiben, die 3 Eingaben benötigt:

  1. Verzeichnis
  2. Schadstoff
  3. Ich würde

Ich habe ein Verzeichnis auf meinem Computer voller CSV-Dateien, dh über 300. Was diese function tun würde, wird im folgenden Prototyp gezeigt:

pollutantmean <- function(directory, pollutant, id = 1:332) { ## 'directory' is a character vector of length 1 indicating ## the location of the CSV files ## 'pollutant' is a character vector of length 1 indicating ## the name of the pollutant for which we will calculate the ## mean; either "sulfate" or "nitrate". ## 'id' is an integer vector indicating the monitor ID numbers ## to be used ## Return the mean of the pollutant across all monitors list ## in the 'id' vector (ignoring NA values) } 

Eine Beispielausgabe dieser function wird hier gezeigt:

 source("pollutantmean.R") pollutantmean("specdata", "sulfate", 1:10) ## [1] 4.064 pollutantmean("specdata", "nitrate", 70:72) ## [1] 1.706 pollutantmean("specdata", "nitrate", 23) ## [1] 1.281 

Ich kann die ganze Sache auf einmal lesen:

 path = "C:/Users/Sean/Documents/R Projects/Data/specdata" fileList = list.files(path=path,pattern="\\.csv$",full.names=T) all.files.data = lapply(fileList,read.csv,header=TRUE) DATA = do.call("rbind",all.files.data) 

Mein Problem sind:

  1. Der Benutzer gibt die ID entweder atomar oder in einem Bereich ein, z. B. wenn der Benutzer 1 eingibt, aber der Dateiname 001.csv ist oder wenn der Benutzer einen Bereich von 1:10 eingibt, dann lautet der Dateiname 001.csv … 010.csv
  2. Spalte wird vom Benutzer eingegeben, dh “Sulfat” oder “Nitrat”, an dem er / sie interessiert ist. In diesen Spalten gibt es viele fehlende Werte (die ich aus der Spalte weglassen muss, bevor der Mittelwert berechnet wird).

Die gesamten Daten aus allen Dateien sehen so aus:

 summary(DATA) Date sulfate nitrate ID 2004-01-01: 250 Min. : 0.0 Min. : 0.0 Min. : 1.0 2004-01-02: 250 1st Qu.: 1.3 1st Qu.: 0.4 1st Qu.: 79.0 2004-01-03: 250 Median : 2.4 Median : 0.8 Median :168.0 2004-01-04: 250 Mean : 3.2 Mean : 1.7 Mean :164.5 2004-01-05: 250 3rd Qu.: 4.0 3rd Qu.: 2.0 3rd Qu.:247.0 2004-01-06: 250 Max. :35.9 Max. :53.9 Max. :332.0 (Other) :770587 NA's :653304 NA's :657738 

Jede Idee, wie dies zu formulieren wäre sehr geschätzt …

Prost

So können Sie Ihre Situation so simulieren;

 # Simulate some data: # Create 332 data frames set.seed(1) df.list< -replicate(332,data.frame(sulfate=rnorm(100),nitrate=rnorm(100)),simplify=FALSE) # Generate names like 001.csv and 010.csv file.names<-paste0('specdata/',sprintf('%03d',1:332),'.csv') # Write them to disk invisible(mapply(write.csv,df.list,file.names)) 

Und hier ist eine function, die diese Dateien lesen würde:

 pollutantmean < - function(directory, pollutant, id = 1:332) { file.names <- list.files(directory) file.numbers <- as.numeric(sub('\\.csv$','', file.names)) selected.files <- na.omit(file.names[match(id, file.numbers)]) selected.dfs <- lapply(file.path(directory,selected.files), read.csv) mean(c(sapply(selected.dfs, function(x) x[ ,pollutant])), na.rm=TRUE) } pollutantmean('specdata','nitrate',c(1:100,141)) # [1] -0.005450574 
 User enters id either atomic or in a range eg 

Nehmen wir an, der Benutzer gibt 1 ein, aber der Dateiname lautet 001.csv oder was ist, wenn der Benutzer einen Bereich von 1:10 eingibt, dann sind die Dateinamen 001.csv … 010.csv

Sie könnten einen regulären Ausdruck und die gsub function verwenden, um führende Nullen aus den Dateinamen zu entfernen, und dann ein Wörterbuch (in r, einen benannten Vektor) erstellen, um die modifizierten / gsub-Dateinamen in die tatsächlichen Dateinamen umzuwandeln. Bsp .: Wenn Ihre Dateinamen in einem Zeichenvektor stehen, fnames

 fnames = c("001.csv","002.csv") names(fnames) < - gsub(pattern="^[0]*", replacement="", x=fnames) 

Damit wird der Vektor fnames in ein Dictionary konvertiert, so dass Sie die Datei mit dem Namen 001.csv mit den Namen fnames["1.csv"] . Sie können auch gsub() , um den .csv Teil des Dateinamens zu entfernen.

Spalte wird vom Benutzer eingegeben, dh "Sulfat" oder "Nitrat", an dem er / sie interessiert ist. In diesen Spalten gibt es viele fehlende Werte (die ich aus der Spalte weglassen muss, bevor der Mittelwert berechnet wird).

Viele R-functionen haben eine Option zum Ignorieren des Sonderzeichens, das einen fehlenden Wert angibt. Versuchen Sie, an der R-Eingabeaufforderung help(mean) einzugeben, um Informationen zu dieser function zu erhalten.

So habe ich es behoben:

 pollutantmean < - function(directory, pollutant, id = 1:332) { #set the path path = directory #get the file List in that directory fileList = list.files(path) #extract the file names and store as numeric for comparison file.names = as.numeric(sub("\\.csv$","",fileList)) #select files to be imported based on the user input or default selected.files = fileList[match(id,file.names)] #import data Data = lapply(file.path(path,selected.files),read.csv) #convert into data frame Data = do.call(rbind.data.frame,Data) #calculate mean mean(Data[,pollutant],na.rm=TRUE) } 

Die letzte Frage ist, dass meine function "specdata" (der Verzeichnisname, in dem sich alle csvs befinden) als Verzeichnis aufrufen soll, gibt es ein Verzeichnistyp-Objekt in r?

Angenommen, ich nenne die function als:

 pollutantmean(specdata, "niterate", 1:10) 

Es sollte den Pfad des specdata-Verzeichnisses bekommen, das sich in meinem Arbeitsverzeichnis befindet ... wie kann ich das tun?

Hier ist eine etwas allgemeine function zum Berechnen des Mittelwerts für eine bestimmte Spalte über eine Liste von Dateien. Nicht sicher, wie id eingerichtet werden sollte, aber im Moment fungiert es als Indexierungsvektor (dh id = 1:3 berechnet den Mittelwert für die ersten drei Dateien in der Dateiliste).

 multifile.means < - function(directory = getwd(), pollutant, id = NULL) { d <- match.arg(directory, list.files()) cn <- match.arg(pollutant, c('sulfate', 'nitrate')) ## get a vector of complete file paths in the given 'directory' p <- dir(d, full.names = TRUE) ## subset 'p' based on 'id' values if(!is.null(id)){ id <- id[!id > length(p)] p < - p[id] } ## read, store, and name the relevant columns cl <- sapply(p, function(x){ read.csv(x)[,cn] }, USE.NAMES = FALSE) colnames(cl) <- basename(p) ## return a named list of some results list(values = cl, mean = mean(cl, na.rm = TRUE), colMeans = colMeans(cl, na.rm = TRUE)) } 

Nimm es für eine Probefahrt:

 > multifile.means('testDir', 'sulfate') # $values # 001.csv 057.csv 146.csv 213.csv # [1,] 5 10 NA 9 # [2,] 1 1 10 3 # [3,] 10 4 10 2 # [4,] 3 10 9 NA # [5,] 4 1 5 5 # $mean # [1] 5.666667 # $colMeans # 001.csv 057.csv 146.csv 213.csv # 4.60 5.20 8.50 4.75 

Hier ist eine Lösung, die selbst deine Großmutter verstehen könnte:

 pollutantmean < - function(directory, pollutant, id = 1:332) { # Break this function up into a series of smaller functions # that do exactly what you expect them to. Your friends # will love you for it. csvFiles = getFilesById(id, directory) dataFrames = readMultipleCsvFiles(csvFiles) dataFrame = bindMultipleDataFrames(dataFrames) getColumnMean(dataFrame, column = pollutant) } getFilesById <- function(id, directory = getwd()) { allFiles = list.files(directory) file.path(directory, allFiles[id]) } readMultipleCsvFiles <- function(csvFiles) { lapply(csvFiles, read.csv) } bindMultipleDataFrames <- function(dataFrames) { Reduce(function(x, y) rbind(x, y), dataFrames) } getColumnMean <- function(dataFrame, column, ignoreNA = TRUE) { mean(dataFrame[ , column], na.rm = ignoreNA) } 

Die ausgewählte Antwort sieht gut aus, aber hier ist eine Alternative. Diese Antwort eignet sich gut für die Grundlagen des JHU-Kurses.

 pollutantmean < - function(directory, pollutant, id = 1:332) { csvfiles <- dir(directory, "*\\.csv$", full.names = TRUE) data <- lapply(csvfiles[id], read.csv) numDataPoints <- 0L total <- 0L for (filedata in data) { d <- filedata[[pollutant]] # relevant column data d <- d[complete.cases(d)] # remove NA values numDataPoints <- numDataPoints + length(d) total <- total + sum(d) } total / numDataPoints } 

Ich habe den Kurs ebenfalls gelesen und kam zu folgender Lösung:

 pollutantmean < - function(directory="d:/dev/r/documents/specdata", pollutant, id) { myfilename = paste(directory,"/",formatC(id, width=3, flag="0"),".csv", sep="") master = lapply(myfilename, read.table, header=TRUE, sep=",") masterfile = do.call("rbind", master) head(masterfile[[2]], 100) if (pollutant == "sulfate") { #result=lapply(masterfile[[2]], mean, na.rm=TRUE) result=mean(masterfile[[2]], na.rm=TRUE) } if (pollutant == "nitrate") { result=mean(masterfile[[3]], na.rm=TRUE) } result } 

Ich habe ein paar Stunden gebraucht, um das auszuarbeiten, aber hier ist meine (kürzere) Version

 pollutmean< - function(dir, pollutant, id=1:332) { dir<- list.files(dir, full.names = T) #list files dat<- data.frame() #make empty df for (i in id) { dat <- rbind(dat, read.csv(dir[i])) #rbind all files } mean(dat[,pollutant], na.rm = TRUE) #calculate mean of given column } pollutmean("assign/specdata", "sulfate", id=1:60)