【问题标题】:Reading multiple files and calculating mean based on user input读取多个文件并根据用户输入计算平均值
【发布时间】:2014-07-01 16:17:17
【问题描述】:

我正在尝试在 R 中编写一个需要 3 个输入的函数:

  1. 目录
  2. 污染物
  3. 身份证

我的计算机上有一个包含 CSV 文件的目录,即超过 300 个。这个函数的作用如下面的原型所示:

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)
        }

此函数的示例输出如下所示:

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

我可以一口气读完全文:

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)

我的问题是:

  1. 用户输入的 id 可以是原子的,也可以是在一个范围内,例如假设用户输入 1 但文件名是 001.csv 或者如果用户输入范围 1:10 那么文件名是 001.csv ... 010.csv
  2. 列由用户输入,即他/她有兴趣获取平均值的“硫酸盐”或“硝酸盐”...这些列中有很多缺失值(我需要在计算之前从列中省略这些值)平均值。

所有文件的全部数据如下所示:

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

任何想法如何制定这一点将不胜感激......

干杯

【问题讨论】:

  • 我现在正在努力完成这项任务。 JHU家伙的教学设计非常糟糕。第一个作业应该是使用一个 CSV 文件的作业……让学生逐步建立技能。合并多个CSV文件应该是赋值2,或者3。不得不发泄。

标签: r function subset mean missing-data


【解决方案1】:

所以,你可以这样模拟你的情况;

# 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))

这是一个读取这些文件的函数:

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

【讨论】:

  • 这适用于单个 args,但如果我这样做会给出错误:contributemean(path,"nitrate",70:72) [1] NA 警告消息:在 mean.default(c(sapply(selected .dfs, function(x) x[, 污染物])), : 参数不是数字或逻辑:返回 NA
  • 该代码在示例中有效。您确定70.csv-72.csv 没有什么特别之处吗?
  • @nograpes :解决方案需要稍作修改。我们需要取消列出 sapply 的输出。所以它看起来像这样:''' selected.dfs
【解决方案2】:

这是一个连你祖母都能理解的解决方案:

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)
}

【讨论】:

    【解决方案3】:
    User enters id either atomic or in a range e.g. 
    

    假设用户输入 1 但文件名是 001.csv 或者如果用户输入范围怎么办 1:10 然后文件名是 001.csv ... 010.csv

    您可以使用正则表达式和gsub 函数从文件名中删除前导零,然后制作一个字典(在 r 中,一个命名向量)将修改/gsub 的文件名转换为实际文件名字。 例如:如果您的文件名在字符向量中,fnames

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

    这样,向量 fnames 将转换为字典,让您可以调用名为 001.csv 的文件,其中包含 fnames["1.csv"] 的内容。您也可以使用gsub() 删除文件名的.csv 部分。

    列由用户输入,即“硫酸盐”或“硝酸盐” 他/她有兴趣了解...有很多 这些列中的缺失值(我需要从列中省略 在计算平均值之前。

    许多 R 函数都有忽略表示缺失值的特殊字符的选项。尝试在 R 命令提示符处输入 help(mean) 以查找有关此功能的信息。

    【讨论】:

      【解决方案4】:

      这就是我修复它的方式:

      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)
      
          }
      

      最后一个问题是我的函数应该调用“specdata”(所有csv所在的目录名)作为目录,r中是否有目录类型对象?

      假设我将函数称为:

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

      它应该得到我工作目录上的 specdata 目录的路径...我该怎么做?

      【讨论】:

      • 您可以使用 setwd 将路径更改为首先指向包含 specdata 文件夹的工作目录。
      【解决方案5】:

      这是一个用于计算文件列表中特定列的平均值的通用函数。不确定应该如何设置id,但现在它充当索引向量(即id = 1:3 计算文件列表中前三个文件的平均值)。

      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))
      }
      

      试驾一下:

      > 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 
      

      【讨论】:

        【解决方案6】:

        选择的答案看起来不错,但这里有一个替代方案。此答案适用于 JHU 课程涵盖的基础知识。

        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
        }
        

        【讨论】:

        • csvfiles
        【解决方案7】:

        我花了几个小时才解决这个问题,但这是我的(较短的)版本

        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)
        

        【讨论】:

          【解决方案8】:

          我也在阅读课程,并提出了以下解决方案:

          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
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-04-27
            • 1970-01-01
            • 1970-01-01
            • 2014-01-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多