高效地创建 data.frames 用于不断变化的具有相同 'tidy' 格式和大小的输入 csv 文件
Efficiently create data.frames for a changing number of input csv files with identical 'tidy' format and size
我不知道如何:
- 使用
rbind
或其他方式高效创建 data.frame
编译 csv 派生的 data.frame
s,其数量因项目而异。或者类似的:
- 有效地创建
data.frame
csv 派生的 "baseline scenario" 的值与其他基于 csv 的替代方案的值之间的差异。
csvs 是水文模型输出的时间序列,已经很长了,'tidy' 格式,它们在格式、大小和顺序上都是相同的——只是不同项目的数量不同。总是至少有两个,一个基线和一个备选方案,但通常有很多。例如,项目 A 可能有四个 csvs/scenarios,项目 B 可能有三十个 csvs/scenarios.
我希望有一个代码模板可以有效地适应具有任意数量场景的项目。如果没有有效的方法,我需要添加或删除相当多的行以匹配我在次日基础上的场景数量,因此我想避免这是一个耗时的步骤。 df
和df_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 循环来分配 scen2
和 scen3
...scen28
和 scen2diff
, scen3diff
...scen28diff
等动态变量,但我失败了。因此,我正在寻找一种可行的方法,并且在应用于具有任意数量场景的项目时不需要太多修改。我只是想以一种干净的方式为用户创建 df
和 df_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")
我不知道如何:
- 使用
rbind
或其他方式高效创建data.frame
编译 csv 派生的data.frame
s,其数量因项目而异。或者类似的: - 有效地创建
data.frame
csv 派生的 "baseline scenario" 的值与其他基于 csv 的替代方案的值之间的差异。
csvs 是水文模型输出的时间序列,已经很长了,'tidy' 格式,它们在格式、大小和顺序上都是相同的——只是不同项目的数量不同。总是至少有两个,一个基线和一个备选方案,但通常有很多。例如,项目 A 可能有四个 csvs/scenarios,项目 B 可能有三十个 csvs/scenarios.
我希望有一个代码模板可以有效地适应具有任意数量场景的项目。如果没有有效的方法,我需要添加或删除相当多的行以匹配我在次日基础上的场景数量,因此我想避免这是一个耗时的步骤。 df
和df_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 循环来分配 scen2
和 scen3
...scen28
和 scen2diff
, scen3diff
...scen28diff
等动态变量,但我失败了。因此,我正在寻找一种可行的方法,并且在应用于具有任意数量场景的项目时不需要太多修改。我只是想以一种干净的方式为用户创建 df
和 df_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")