【问题标题】:Using for loops in row-wise evaluations - R version 4.0.0在逐行评估中使用 for 循环 - R 版本 4.0.0
【发布时间】:2020-07-31 21:56:51
【问题描述】:

我已经阅读了多个线程,解释应该不鼓励使用 for 循环,如果有更好的方法我想学习的话。我会说我已经尝试将summarize()group_by() 结合使用。

我想要完成的是,我想开发一个气候数据库。我已成功编程 R 以直接从源下载数据,并将列表转换为 data.frame。现在我想按月和年对多个列进行求和和/或平均。因此,我为什么尝试使用summarizegroup_by。我的问题是数据带有我想保留的代码“M”或“T”,所以我任意给它们整数 M = 9999 和 T = 9998。我想在需要操作代码时我可以使用 for循环以逐行评估并将这 2 个占位符转换为“0”并返回该子集中有多少“M”和“T”。

以下是数据的到达方式:

$data
# A tibble: 935 x 8
   date             datatype station        value fl_m  fl_q  fl_so fl_t 
   <chr>            <chr>    <chr>          <int> <chr> <chr> <chr> <chr>
 1 2020-01-01T00:0~ PRCP     GHCND:USW0002~    76 ""    ""    W     "240~
 2 2020-01-01T00:0~ SNOW     GHCND:USW0002~     0 "T"   ""    W     ""   
 3 2020-01-01T00:0~ SNWD     GHCND:USW0002~     0 "T"   ""    W     ""   
 4 2020-01-01T00:0~ TMAX     GHCND:USW0002~    39 ""    ""    W     "240~
 5 2020-01-01T00:0~ TMIN     GHCND:USW0002~    -5 ""    ""    W     "240~
 6 2020-01-02T00:0~ PRCP     GHCND:USW0002~     3 ""    ""    W     "240~
 7 2020-01-02T00:0~ SNOW     GHCND:USW0002~     5 ""    ""    W     ""   
 8 2020-01-02T00:0~ SNWD     GHCND:USW0002~     0 ""    ""    W     ""   
 9 2020-01-02T00:0~ TMAX     GHCND:USW0002~    11 ""    ""    W     "240~
10 2020-01-02T00:0~ TMIN     GHCND:USW0002~   -10 ""    ""    W     "240~
# ... with 925 more rows

这是我用来将其从列表转换为 data.frame 的代码:

## Convert a list from NCDC into a data frame
## mso_data is a placeholder file for the downloaded data from NCDC
## mso_light2 is a placeholder for the destination data frame
## NCDC downloads in a list, the data is stored in the $data portion

library(tidyverse)


## first convert from list to data.frame and remove 'station ID' column
mso_light2 <- mso_data$data[, -3]

## remove time from date group
mso_date <- mso_light2[1]
mso_date <- sub("T.*", "", mso_date$date)
mso_light2$date <- mso_date 

## remove flags for fl_so? and fl_t (time)
mso_light2 <- mso_light2[1:5]

## Change 'T' = 9998 & 'M' = 9999
mso_light2$value[mso_light2$fl_m == "T"] <- 9998
mso_light2$value[mso_light2$fl_q == "M"] <- 9999

## pivot data frame

## eventually use to change column names
## v_names <- c('PRCP', 'SNOW', 'SNWD', 'TMAX', 'TMIN')

mso_light2 <- mso_light2[1:3]

mso_light2 <- pivot_wider(mso_light2,
  names_from = datatype,  
  values_from = value)

这是转换后data.frame的样子,我添加了月份和年份的列以及日平均温度“TAVG”:

# A tibble: 187 x 9
# Rowwise: 
   date        PRCP  SNOW  SNWD  TMAX  TMIN  TAVG month  year
   <date>     <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 2020-01-01    76  9998  9998    39    -5  17       1  2020
 2 2020-01-02     3     5     0    11   -10   0.5     1  2020
 3 2020-01-03     5     8  9998    61   -38  11.5     1  2020
 4 2020-01-04     8  9998     0    33   -66 -16.5     1  2020
 5 2020-01-05     5    10     0    33   -21   6       1  2020
 6 2020-01-06  9998  9998  9998    33   -38  -2.5     1  2020
 7 2020-01-07  9998     0     0    78   -10  34       1  2020
 8 2020-01-08     5  9998  9998    44   -27   8.5     1  2020
 9 2020-01-09  9998  9998     0     0   -55 -27.5     1  2020
10 2020-01-10     8    10     0   -10   -99 -54.5     1  2020
# ... with 177 more rows

现在这是我尝试使用 summarise 和 group_by 的原始代码:

## first format mso_light2$date from <chr> to an actual 'date'
install.packages("chron")
install.packages("openair")
install.packages("lubridate")

library("openair")
library("chron")
library('lubridate')

options(stringAsFactors = FALSE)

mso_light2$date <- as.Date(mso_light2$date, "%Y-%m-%d")

## Turning all daily temperatures into an average

mso_light2 <- mso_light2 %>% rowwise() %>% 
              mutate(TAVG = mean(c(TMAX, TMIN), na.rm = T))

## Composing daily data into monthly packages

mso_light2 <- mso_light2 %>%
  mutate(month = month(date)) %>%
  mutate(year = year(date))

##  mso_PRCP <- mso_light2 %>%
##    group_by(month, year) %>%
##    summarise(PRCP = sum(PRCP)) 

##  mso_SNOW <- mso_light2 %>%
##    group_by(month, year) %>%
##    summarise(SNOW = sum(SNOW)) 

##  mso_TAVG <- mso_light2 %>%
##    group_by(month, year) %>%
##    summarise(TAVG = mean(TAVG)) 

##  summarise(SNOW = sum(SNOW)) %>%
##  summarise(TAVG = mean(TAVG))

问题是我不知道如何删除占位符“9999”和“9998”并将它们设为“0”。所以我一直在尝试开发一个 for 循环,这就是我所拥有的:

for(i in 1:length(mso_light2$year[[1]])){
     startDate <- as.character(mso_light2$date[1])

     startDate <- str_split(startDate, "-")
     start_year <- startDate[[1]][1]
     start_month <- startDate[[1]][2]
     start_day <- startDate[[1]][3]
     
     for(j in 1:length(mso_light2$month)){

         mso_monthly <- sapply(mso_light2, 
                               function(x) sum(x[["PRCP"]]), 
                               use.names = 
                                 paste(start_year, '-', 
                                       start_month, sep = ""))
       }
       
     }

请忽略sapply() 我已经尝试了该系列中所有可能的功能,它们都返回错误消息。

这是我不断收到的错误:

FUN(X[[i]], ...) 中的错误:未使用的参数 (use.names = "2020-01")

sapply 只是我在寻求帮助之前尝试的最后一个函数,谢谢。

【问题讨论】:

  • 能否请您发布一个数据的 sn-p 作为可重现的示例。使用dput()datapasta 粘贴数据(理想情况下,整个示例可以是一个reprex,但让我们从数据开始)

标签: r for-loop


【解决方案1】:

我了解到您正在尝试从 GHCN 下载 2020 年站 USW00024153 的数据。

library(tidyverse)
dt_path <- "ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/daily/by_year/2020.csv.gz"
download.file(dt_path, "2020.csv.gz", mode="wb")

#> ID = 11 character station identification code
#> YEAR/MONTH/DAY = 8 character date in YYYYMMDD format (e.g. 19860529 = May 29, 1986)
#> ELEMENT = 4 character indicator of element type 
#> DATA VALUE = 5 character data value for ELEMENT 
#> M-FLAG = 1 character Measurement Flag 
#> Q-FLAG = 1 character Quality Flag 
#> S-FLAG = 1 character Source Flag 
#> OBS-TIME = 4-character time of observation in hour-minute format (i.e. 0700 =7:00 am)
#>  this list ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/daily/by_year/
#>  data dictionary https://www1.ncdc.noaa.gov/pub/data/ghcn/daily/readme.txt

来自这个 FTP 服务器的数据更干净一些,至少日期不包含时间戳。我重用您的列名,因为数据没有标题。另请注意,readr::read_csv()(和data.table::fread())可以很好地处理压缩文件,因此无需解压缩。

dt_colnms <- c("station", "date", "datatype", "value", "fl_m", "fl_q", "fl_so", "fl_t")

dt <- readr::read_csv("2020.csv.gz", col_names = dt_colnms, col_types = 'cccdcccc')

数据处理步骤包括:

  1. 过滤您需要的站点并忽略数据集中也存在的风柱。
  2. 转置多个值列(您感兴趣的值和标志)
  3. 平均温度。由于您只有 2 列,因此我没有理由去 rowwise()
  4. 从字符日期中提取月份和年份并转换日期。
dt %>% 
  filter(station=="USW00024153", !str_detect(datatype, "^W")) %>% 
  pivot_wider(id_cols = "date",
              names_from = "datatype",
              values_from = c("fl_m", "fl_q","value")) %>% 
  mutate(value_TAVG=(value_TAVG+value_TAVG)/2,
         month=parse_number(substr(date, 5,6)),
         year=parse_number(substr(date, 1,4)),
         date=as.Date(date, "%Y%m%d"))

现在您的最后一步是检查 fl_m == "T" 或 fl_q == "M" 的行是否用零替换值。

您可以在旋转之前完成它。那么透视和总结都会变得更容易:

dt %>% 
  filter(station=="USW00024153", !str_detect(datatype, "^W")) %>% 
  mutate(value=ifelse(fl_m=="T"&!is.na(fl_m), 0, value),
         value=ifelse(fl_q=="M"&!is.na(fl_q), 0, value)) %>% 
  pivot_wider(id_cols = "date",
              names_from = "datatype",
              values_from = "value") %>% 
  mutate(TAVG=(TMIN+TMAX)/2,
         month=parse_number(substr(date, 5,6)),
         year=parse_number(substr(date, 1,4)),
         date=as.Date(date, "%Y%m%d")) %>% 
  group_by(month, year) %>% 
  summarize(AVG_TAVG=mean(TAVG, na.rm = TRUE),
            AVG_PRCP=mean(PRCP, na.rm=TRUE),
            AVG_SNOW=mean(SNOW, na.rm=TRUE)) %>% 
  ungroup()
#> # A tibble: 7 x 5
#>   month  year AVG_TAVG AVG_PRCP AVG_SNOW
#>   <dbl> <dbl>    <dbl>    <dbl>    <dbl>
#> 1     1  2020    -1.82     6.61   2.58  
#> 2     2  2020    -7.60     9.31  11.3   
#> 3     3  2020    31.6      1.77   0.0968
#> 4     4  2020    69.9     15.1    3.97  
#> 5     5  2020   119.      21.3    0     
#> 6     6  2020   155.      21.5    0     
#> 7     7  2020   191.       2.55   0  

【讨论】:

  • 感谢您的回答。所以你猜的一切都是正确的,除了我想要每个站的完整数据集。例如,“USW00024153”从 1948 年 1 月 1 日到现在有效。我在使用 RNOAA 软件包时遇到了问题,因为它一次最多下载 1 年。我为下载整个数据集而开发的代码存在一些我接下来要处理的问题,因此为什么只有 2020 年。我将在我的帖子中发布一个新答案,显示我现在遇到的问题。
  • 好吧,在这种情况下,您可以从 filter() 中删除 station=="USW00024153" 并继续处理整个数据集,尽管我不明白您将如何旋转数据。那你需要车站吗?
【解决方案2】:

由于我需要的不仅仅是 2020 年的 GHCN 数据,因此我下载了“ghcnd-all.tar.gz”文件,并将单独提取每个站。现在的问题是这些文件采用 '.dly' 格式,并且解压后的数据看起来像这样:

mso <- read.fwf('USW00024153.dly',widths = c(11, 4, 2, 4, rep(c(5, 1, 1, 1),31)))
> mso
           V1   V2 V3   V4    V5 V6 V7 V8    V9 V10 V11 V12   V13 V14
1 USW00024153 1948  1 TMAX    44        X    44           X    44    
2 USW00024153 1948  1 TMIN  -122        X     6           X   -39    
3 USW00024153 1948  1 PRCP     0  T     X     3           X     0   T
4 USW00024153 1948  1 SNOW     0  T     X     0   T       X     0   T
5 USW00024153 1948  1 SNWD   102        X    51           X    25    
6 USW00024153 1948  1 WT01 -9999          -9999             -9999    
7 USW00024153 1948  1 WT06 -9999          -9999             -9999 

这将持续到 128 列,具有重复序列,IE V5-V8 = 月 1 日,V9-V12 = 月 2 日,ECT。我即将修改 pivot_longer 和 pivot_wider 以将其转换为类似于此的格式:

  date        PRCP  SNOW  SNWD  TMAX  TMIN  TAVG month  year
   <date>     <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1 2020-01-01    76  9998  9998    39    -5  17       1  2020
 2 2020-01-02     3     5     0    11   -10   0.5     1  2020
 3 2020-01-03     5     8  9998    61   -38  11.5     1  2020
 4 2020-01-04     8  9998     0    33   -66 -16.5     1  2020
 5 2020-01-05     5    10     0    33   -21   6       1  2020
 6 2020-01-06  9998  9998  9998    33   -38  -2.5     1  2020
 7 2020-01-07  9998     0     0    78   -10  34       1  2020
 8 2020-01-08     5  9998  9998    44   -27   8.5     1  2020
 9 2020-01-09  9998  9998     0     0   -55 -27.5     1  2020
10 2020-01-10     8    10     0   -10   -99 -54.5     1  2020

但是,也许有人可以帮助我提供下载代码,以便我可以下载更易于使用的 CSV 格式的数据。这是一旦完成的代码,文件中只有 2020 年。每次迭代后,我都会检查并确保每年,例如 1948、1949 年,ECT 都在那里,但最终只有 2020 年在最终的 data.frame 中:

此代码来自 RNOAA 包,我知道一切正常,但我使用 save()resave() 存在问题。我想要完成的是在 for 循环的每一步中,下载 1 年的数据并将其添加到我的数据集中。我随意选择一次下载 1 个十年,因为每次迭代都是时间密集型的,而且我害怕超时。对于这个特定的气候站,我将从 1948-01-01 开始,因为那是第一个数据周期。所以我只是从 (1948:1959) 运行 for 循环,然后是 (1960:1969) 等,直到我到达 2019 年,然后我进行了 2020 年的最终下载。不幸的是,当一切都说完了,只有 2020 年在文件。

library('rnoaa')
library('dplyr')
library('utils')
library('cgwtools')


data_type <- c('tmax','tmin','PRCP', 'SNOW', 'SNWD')

## Station ID for MSO is GHCND:USW00024153
## Station ID for GPI is GHCND:USC00244558
## Station ID for BTM is GHCND:USW00024135

for (i in 2009:2019){
  start_date <- paste(i, '-01-01', sep = "")
  end_date <- paste(i, '-12-31', sep = "")
  assign(paste('mso_data', i, sep = ""), ncdc(datasetid = 'GHCND', stationid = 'GHCND:USW00024153',
             datatypeid = data_type, startdate = start_date, 
             enddate = end_date, limit = 1000))
  a <- paste('mso_data', i, sep = "")

  
  if (i == 1948){
    save(a, file = 'mso_data.RData')
  }
  else {
    resave(a, file = 'mso_data.RData')
  }
}

mso_data <- ncdc(datasetid = 'GHCND', stationid = 'GHCND:USW00024153',
                 datatypeid = data_type, startdate = '2020-01-01', 
                 enddate = '2020-07-07', limit = 1000)
resave(mso_data, file = 'mso_data.RData')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 2020-04-09
    • 2017-05-05
    • 1970-01-01
    相关资源
    最近更新 更多