循环子集,获取文件并将结果保存在数据框中
Posted
技术标签:
【中文标题】循环子集,获取文件并将结果保存在数据框中【英文标题】:Loop over a subset, source a file and save results in a dataframe 【发布时间】:2016-08-03 18:09:58 【问题描述】:已经提出了类似的问题,但没有一个能够解决我的具体问题。我有一个.R
文件(“Mycalculus.R”),其中包含许多我需要应用于数据框子集的基本微积分:每年的一个子集,其中“年”的模态是因子(yearA、yearB、yearC)不是数值。该文件生成一个新的数据框,我需要将其保存在 Rda 文件中。这是我希望代码看起来像 for
循环(这个显然不起作用):
id <- identif(unlist(df$year))
for (i in 1:length(id))
data <- subset(df, year == id[i])
source ("Mycalculus.R", echo=TRUE)
save(content_df1,file="myresults.Rda")
这是主要 data.frame df 的确切内容:
obs year income gender ageclass weight
1 yearA 1000 F 1 10
2 yearA 1200 M 2 25
3 yearB 1400 M 2 5
4 yearB 1350 M 1 11
源文件“Mycalculus.R”的作用如下:它将大量基本微积分应用于数据帧的“数据”列,并基于 df1 创建两个新数据帧 df1 和 df2。以下是摘录:
data <- data %>%
group_by(gender) %>%
mutate(Income_gender = weighted.mean(income, weight))
data <- data %>%
group_by(ageclass) %>%
mutate(Income_ageclass = weighted.mean(income, weight))
library(GiniWegNeg)
gini=c(Gini_RSV(data$Income_gender, weight), Gini_RSV(data$Income_ageclass,weight))
df1=data.frame(gini)
colnames(df1) <- c("Income_gender","Income_ageclass")
rownames(df1) <- c("content_df1")
df2=(1/5)*df1$Income_gender+df2$Income_ageclass
colnames(df2) <- c("myresult")
rownames(df2) <- c("content_df2")
所以最后,我得到两个这样的数据框:
Income_Gender Income_Ageclass
content_df1 .... ....
对于 df2:
myresult
content_df2 ....
但我需要将 df1 和 Rf2 保存为 Rda 文件,其中 content_df1 和 content_df2 的行名按子集给出,如下所示:
Income_Gender Income_Ageclass
content_df1_yearA .... ....
content_df1_yearB .... ....
content_df1_yearC .... ....
和
myresult
content_df2_yearA ....
content_df2_yearB ....
content_df2_yearC ....
目前,我的程序没有使用任何循环并且正在完成这项工作,但很混乱。基本上代码是2500多行代码。 (请不要向我扔西红柿)。
任何人都可以帮助我解决这个特定的请求吗? 提前谢谢你。
【问题讨论】:
用一个可重复的例子简单地说明你的问题。提供两个带有 yearA 和 yearB 的简单 data.frame,并在您的示例 Mycalculus.R 文件中执行一个简单的功能。这将使其他人更容易理解您的问题的性质。 【参考方案1】:考虑将所有内容与所需参数的定义函数合并到一个脚本中,由lapply()
调用。然后,Lapply 返回一个数据帧列表,您可以将这些数据帧绑定到一个最终的 df 中。
library(dplyr)
library(GiniWegNeg)
runIncomeCalc <- function(data, y)
data <- data %>%
group_by(gender) %>%
mutate(Income_gender = weighted.mean(income, weight))
data <- data %>%
group_by(ageclass) %>%
mutate(Income_ageclass = weighted.mean(income, weight))
gini <- c(Gini_RSV(data$Income_gender, weight), Gini_RSV(data$Income_ageclass,weight))
df1 <- data.frame(gini)
colnames(df1) <- c("Income_gender","Income_ageclass")
rownames(df1) <- c(paste0("content_df1_", y))
return(df1)
runResultsCalc <- function(df, y)
df2 <- (1/5) * df$Income_gender + df$Income_ageclass
colnames(df2) <- c("myresult")
rownames(df2) <- c(paste0("content_df2_", y)
return(df2)
dfIncList <- lapply(unique(df$year), function(i)
yeardata <- subset(df, year == i)
runIncomeCalc(yeardata, i)
)
dfResList <- lapply(unique(df$year), function(i)
yeardata <- subset(df, year == i)
df <- runIncomeCalc(yeardata, i)
runResultsCalc(df, i)
)
df1 <- do.call(rbind, dfIncList)
df2 <- do.call(rbind, dfResList)
现在,如果您需要跨脚本获取资源。在 Mycalculus.R 中创建相同的两个函数,runIncomeCalc 和 runResultsCalc,然后在其他脚本中调用:
library(dplyr)
library(GiniWegNeg)
if(!exists("runIncomeCalc", mode="function")) source("Mycalculus.R")
dfIncList <- lapply(unique(df$year), function(i)
yeardata <- subset(df, year == i)
runIncomeCalc(yeardata, i)
)
dfResList <- lapply(unique(df$year), function(i)
yeardata <- subset(df, year == i)
df <- runIncomeCalc(yeardata, i)
runResultsCalc(df, i)
)
df1 <- do.call(rbind, dfIncList)
df2 <- do.call(rbind, dfResList)
【讨论】:
您的解决方案完美运行,但前提是我的微积分仅生成一个数据帧 (df1)。如果我的微积分生成第二个数据帧(基于 df1 的 df2),它就不再起作用了。这里的代码甚至不再为 df1 生成结果。我编辑了我的问题,以便在这一点上更清楚。 使用相同的 fct 和 args 进程查看更新。只需添加另一个接收 df1 作为输入的函数,然后在结尾处使用lapply
迭代到rbind
。并从其他脚本中获取这两个函数。
在您的更新中,您在 runResultsCalc 部分中写了 df 而不是 df1。它应该是df2 <- (1/5) * df1$Income_gender + df1$Income_ageclass
,因为 df2 演算是基于 df1 的结果。这是故意的吗?如果不是,这对您提供的其余代码有什么影响?
不正确,因为df
是作为参数传递的函数的本地参数。在dfResList
lapply() 中,其本地df
是runIncomeCalc()
的返回值,然后将其结果集传递给runResultsCalc()
。使用df1
会出错或返回空数据,因为它在全局环境中除了最后之外没有创建。当然要测试确认。更改重复的df
的名称以避免混淆。【参考方案2】:
如果您将步骤功能化,您可以创建如下工作流:
calcFunc <- function(df)
## Do something to the df, then return it
df
processFunc <- function(fname)
## Read in your table
x <- read.table(fname)
## Do the calculation
x <- calcFunc(x)
## Make a new file name (remember to change the file extension)
new_fname <- sub("something", "else", fname)
## Write the .RData file
save(x, file = new_fname)
### Your workflow
## Generate a vector of files
my_files <- list.files()
## Do the work
res <- lapply(my_files, processFunc)
或者,不要保存文件。省略processFunc
中的save
调用,并返回data.frame 对象列表。然后使用data.table::rbindlist(res)
或do.call(rbind, list)
制作一个大的data.frame 对象。
【讨论】:
鉴于我当前的 .R 演算文件(我必须首先对其进行功能化)是指演算中的主要 data.frame,恐怕子设置会被覆盖。我在尝试使用by
命令时遇到了这个问题。我基本上是要求程序按组“做某事”,但“某事”指的是主数据框。因此,它与 by group
指令相矛盾,导致程序运行的次数与子集的数量一样多,但在主数据帧上。我害怕在这里遇到同样的问题。你怎么看?以上是关于循环子集,获取文件并将结果保存在数据框中的主要内容,如果未能解决你的问题,请参考以下文章