高效地创建 data.frames 用于不断变化的具有相同 'tidy' 格式和大小的输入 csv 文件

Efficiently create data.frames for a changing number of input csv files with identical 'tidy' format and size

我不知道如何:

  1. 使用 rbind 或其他方式高效创建 data.frame 编译 csv 派生的 data.frames,其数量因项目而异。或者类似的:
  2. 有效地创建 data.frame csv 派生的 "baseline scenario" 的值与其他基于 csv 的替代方案的值之间的差异。

csvs 是水文模型输出的时间序列,已经很长了,'tidy' 格式,它们在格式、大小和顺序上都是相同的——只是不同项目的数量不同。总是至少有两个,一个基线和一个备选方案,但通常有很多。例如,项目 A 可能有四个 csvs/scenarios,项目 B 可能有三十个 csvs/scenarios.

我希望有一个代码模板可以有效地适应具有任意数量场景的项目。如果没有有效的方法,我需要添加或删除相当多的行以匹配我在次日基础上的场景数量,因此我想避免这是一个耗时的步骤。 dfdf_diff创建后,都用于后面的总结和剧情。

我会手动输入场景的名称,因为它们总是不同的,例如:

library(dplyr)
scenarios <- c("baseline", "alt1", "alt1b", "no dam")

length(scenarios) 将始终与我为给定项目拥有的 CSV 数量相匹配。

读入 csv(每个场景一个 csv)并保持不变以供以后单独处理:

#In my case these csv#s are from a separate file's list of csvs,
#eg csv1 <- read.csv("baseline.csv")
#   csv2 <- read.csv("alt1.csv"), etc - all tidy monthly timeseries of many variables

#For reproducibility, simplyfying:

csv1 <- data.frame("variable" = "x", "value" = 13)  #baseline scenario                                                    
csv2 <- data.frame("variable" = "x", "value" = 5)   #"alternative 1"
csv3 <- data.frame("variable" = "x", "value" = 109) #"alternative 1b"
csv4 <- data.frame("variable" = "x", "value" = 11)  #"dam removal"
#csv5 <- data.frame("variable" = "x", "value" = 2.5) #"100 extra flow for salmon sep-dec" 
#...
#csv30 <- data.frame("variable" = "x", "value" = 41) #"alternative H3" 

复制 csvs 并将数据连接到场景:

baseline  <- csv1 %>% mutate(scenario = as.factor(paste0(scenarios[1])))
scen2     <- csv2 %>% mutate(scenario = as.factor(paste0(scenarios[2])))
scen3     <- csv3 %>% mutate(scenario = as.factor(paste0(scenarios[3])))
scen4     <- csv4 %>% mutate(scenario = as.factor(paste0(scenarios[4])))  

df <- rbind(baseline, scen2, scen3, scen4)   #data.frame #1 I'm looking for.
#eg, if csv1-csv30 were included, how to compile in df efficiently, w/o needing the "scen" lines?

本例中有 4 个场景,因此 df$scenario 有 4 个级别。到达这里。

现在是第二个"difference" data.frame:

bslnevals <-  baseline  %>% select(value)
scen2vals <-  scen2     %>% select(value)
scen3vals <-  scen3     %>% select(value)
scen4vals <-  scen4     %>% select(value)

scen2diff  <- (scen2vals -  bslnevals)  %>% transmute(value_diff = value, 
              scenario_diff = as.factor(paste0(scenarios[2], " - baseline"))) %>% 
              data.frame(scen2)  %>% select(-value, -scenario)
scen3diff  <- (scen3vals -  bslnevals)  %>% transmute(value_diff = value, 
              scenario_diff = as.factor(paste0(scenarios[3], " - baseline"))) %>% 
              data.frame(scen3) %>% select(-value, -scenario)
scen4diff  <- (scen4vals -  bslnevals)  %>% transmute(value_diff = value, 
              scenario_diff = as.factor(paste0(scenarios[4], " - baseline"))) %>% 
              data.frame(scen4) %>% select(-value, -scenario)

df_diff <- rbind(scen2diff, scen3diff, scen4diff) #data.frame #2 I'm looking for.
#same as above, if csv1 - csv30 were included, how to compile in df_diff efficiently, w/o
#needing the  "scen#vals" and "scen#diff" lines?

rm(baseline, scen2, scen3, scen4) #declutter - now unneeded (but csv1, csv2, etc orig csv#s needed later)
rm(bslnevals, scen2vals, scen3vals, scen4vals) #unneeded
rm(scen2diff, scen3diff, scen4diff) #unneeded

有 4 个场景,与基线有 3 个差异,因此 df_diff$scenario 有 3 个级别。

所以,如果我有 4 个 csvs(1 个基线,3 个备选方案)或 30 个 CSV(1 个基线,29 个备选方案),我尝试编写函数和 for 循环来分配 scen2scen3 ...scen28scen2diff, scen3diff...scen28diff 等动态变量,但我失败了。因此,我正在寻找一种可行的方法,并且在应用于具有任意数量场景的项目时不需要太多修改。我只是想以一种干净的方式为用户创建 dfdf_diff,因为对于给定的项目,我或他们碰巧遇到了许多场景(即 csvs)。

非常感谢任何帮助。

我无法用你的案例进行测试,但这可能是重构代码的一个很好的起点。我使用 case_when 生成规则以将 CSV 文件的名称映射到场景。我从每个场景的值中减去基线值。

library(dplyr)
library(readr)
library(purrr)
library(tidyr)

baseline_df <- read_csv("baseline.csv") %>% 
  mutate(id = row_number())

# list all csv files (in current directory), then read them all, and row-bind them.
# use case_when to apply rules to change filenames to "scenarios" (grepl to check presence of string)
# join with baseline df (by scenario row number) for easy subtracting.
# calculate differences values.
# remove baseline-baseline rows (diff is 0)

diff_df <- list.files(path = getwd(), pattern = "*.csv", full.names = TRUE) %>% 
  tibble(filename = .) %>%
  mutate(data = map(filename, read_csv)) %>%
  unnest() %>% 
  mutate(scenario = case_when(
    grepl("baseline", filename) ~ "baseline",
    grepl("alternative1", filename) ~ "alt1",
    grepl("alternative2", filename) ~ "alt2",
    grepl("dam_removal", filename) ~ "no dam",
    TRUE ~ "other"
  )) %>% 
  group_by(scenario) %>% 
  mutate(id = row_number()) %>% 
  left_join(baseline_df, by = "id", suffix = c("_new", "_baseline")) %>% 
  mutate(Value_diff = Value_new - Value_baseline) %>% 
  filter(scenario != "baseline")