在R工作流程中编写函数与逐行解释

时间:2011-03-20 00:43:19

标签: r workflow statistics

这里已经写了很多关于在R中为统计项目开发工作流程的文章。最受欢迎的工作流程似乎是Josh Reich's LCFD model。使用包含main.R代码的代码:

source('load.R')
source('clean.R')
source('func.R')
source('do.R')

以便单个source('main.R')运行整个项目。

问:是否有理由倾向于将此工作流程更改为load.Rclean.Rdo.R中完成的逐行解释性工作替换为由main.R调用?

我现在找不到这个链接了,但是我已经在某个地方看过,当在R中进行编程时,必须克服他们在函数调用方面编写所有东西的愿望--- R是要写的就是这个逐行解释形式。

问:真的吗?为什么?

我对LCFD方法感到沮丧,我可能会根据函数调用编写所有内容。但在此之前,我想听听SO的优秀人士是否这是一个好主意。

编辑:我现在正在进行的项目是(1)读取一组财务数据,(2)清理它(非常复杂),(3)使用我的估算器估算与数据相关的一些数量(4)使用传统估算器估算相同数量(5)报告结果。我的程序应该以这样的方式编写,即对于不同的经验数据集(1)进行模拟数据,或(3)使用不同的估计器,这是一项工作(1)。此外,它应该遵循有文化的编程和可重复的研究指南,以便新的代码运行程序,了解正在发生的事情,以及如何调整它。

6 个答案:

答案 0 :(得分:15)

我认为源文件中创建的任何临时内容都不会被清除。如果我这样做:

x=matrix(runif(big^2),big,big)
z=sum(x)

并将源代码作为文件,虽然我不需要它,但它仍然存在。但如果我这样做:

ff=function(big){
 x = matrix(runif(big^2),big,big)
 z=sum(x)
 return(z)
}

而不是源代码,在我的脚本中执行z = ff(big),x矩阵超出范围,因此得到清理。

功能可以实现整洁的小型可重复使用的封装,并且不会在外部污染。一般来说,它们没有副作用。您的逐行脚本可能正在使用与当前使用的数据集绑定的全局变量和名称,这使它们无法使用。

我有时会逐行工作,但是当我得到超过大约五行时,我发现我真正需要的是一个适当的可重用功能,而且我经常会最终重新使用它

答案 1 :(得分:13)

我认为没有一个答案。最好的办法是记住相对优点,然后选择适合这种情况的方法。

1)函数。不使用函数的优点是所有变量都保留在工作区中,您可以在最后检查它们。这可能会帮助您弄清楚如果您遇到问题会发生什么。

另一方面,精心设计的功能的优点是您可以对它们进行单元测试。也就是说,您可以将它们与其他代码区分开来,使它们更容易测试。此外,当您使用函数,模拟某些较低级别的结构时,您知道一个函数的结果不会影响其他函数,除非它们被传递出去,这可能会限制一个函数的错误处理可能对另一个函数造成的损害。您可以使用R中的debug工具来调试您的功能,并且能够单步执行它们是一个优势。

2) LCFD。关于是否应该使用load / clean / func / do的分解,无论是通过source还是函数完成,都是第二个问题。无论是通过source还是函数完成,这种分解的问题是你需要运行一个只是为了能够测试下一个,所以你不能真正独立地测试它们。从这个角度来看,它不是理想的结构。

另一方面,如果您想在不同的数据上尝试并且可以独立于负载和清洁步骤替换其他步骤,它的确具有以下优点:您可以独立于其他步骤替换加载步骤如果你想尝试不同的处理。

3)不。 of Files 在您询问是否所有内容都应在一个或多个源文件中时,可能存在第三个问题。将东西放在不同的源文件中的优点是您不必查看不相关的项目。特别是如果你有一些没有被使用或与当前函数无关的例程,你会看到它们不会中断流程,因为你可以安排它们在其他文件中。

另一方面,从(a)部署的角度来看,将所有内容放在一个文件中可能是有利的,即你可以向某人发送单个文件,以及(b)编辑方便,因为你可以放置整个程序在一个编辑器会话中,例如,便于搜索,因为您可以使用编辑器的功能搜索整个程序,因为您不必确定例程所在的文件。此外,连续的撤消命令将允许您向后移动程序的所有单元和单个保存将保存所有模块的当前状态,因为只有一个。 (c)速度,即如果你在慢速网络上工作,将一个文件保存在本地机器中然后偶尔写出来而不是必须来回慢速遥控器可能会更快。

注意:另一件需要考虑的事情是,首先使用套餐可能优于您的需求。

答案 2 :(得分:7)

在编写函数时,没有人提到一个重要的考虑因素:除非你一次又一次地重复某些动作,否则编写它们没有多大意义。在分析的某些部分,您将进行一次性操作,因此为它们编写函数没有多大意义。如果你不得不重复几次,那么值得投入时间和精力来编写一个可重复使用的功能。

答案 3 :(得分:6)

工作流:

我使用的东西非常相似:

  1. Base.r:提取主要数据,调用其他文件(第2项至第5项)
  2. Functions.r:加载函数
  3. Plot Options.r:加载我经常使用的一些常规绘图选项
  4. Lists.r:加载列表,我有很多,因为公司名称,陈述等随着时间的推移而变化
  5. Recodes.r:大部分工作都在这个文件中完成,基本上是数据清理和排序
  6. 到目前为止尚未进行任何分析。这仅用于数据清理和排序。

    在Recodes.r结束时,我将环境保存到我的实际分析中。

    save(list=ls(), file="Cleaned.Rdata")
    

    随着清洁工作,功能和绘图选项准备就绪,我开始进入分析。再次,我继续将其分解为专注于主题或主题的较小文件,例如:人口统计,客户请求,相关性,对应分析,情节等。我几乎总是自动运行前5个来设置我的环境,然后我逐行运行其他的以确保准确性和探索。

    在每个文件的开头,我加载了清理过的数据环境并繁荣了。

    load("Cleaned.Rdata")
    

    对象命名法:

    我不使用列表,但我确实使用了对象的命名法。

    df.YYYY # Data for a certain year
    demo.describe.YYYY ## Demographic data for a certain year
    po.describe ## Plot option
    list.describe.YYYY ## lists
    f.describe ## Functions
    

    使用友好的助记符替换上面的“描述”。

    注释

    我一直在努力让自己养成使用评论(x)的习惯,我发现它非常有用。代码中的注释很有帮助,但通常还不够。

    清理

    同样,在这里,我总是尝试使用相同的对象进行简单的清理。例如tmp,tmp1,tmp2,tmp3,并确保在结束时删除它们。

    功能

    在其他帖子中有一些评论,如果你不止一次地使用它,只会写一些函数。我想调整一下来说,如果你认为你有可能再次使用它,你应该把它扔进一个函数中。我甚至无法计算我希望为逐行创建的进程编写函数的次数。

    另外,在我更改一个函数之前,我将它抛入一个名为Deprecated Functions.r的文件中,再次防止“我该怎么做”这个效果。

答案 4 :(得分:3)

我经常将代码与此类似地分开(尽管我通常将Load和Clean放在一个文件中),但我从不只是将所有文件都源于运行整个项目;对我来说,它打败了分裂他们的目的。

与Sharpie的评论一样,我认为您的工作流程应该在很大程度上取决于您正在做的工作。我主要进行探索性工作,在这种情况下,保持数据输入(加载和清理)与分析(功能和做法)分开,意味着当我第二天回来时,我不需要重新加载和重新开始;我可以在清理后保存数据集,然后再次导入。

我几乎没有重复日常数据集的经验,但我想我会发现一个不同的工作流程有帮助;正如哈德利回答的那样,如果你只做一次事(就像我加载/清理我的数据时一样),写一个函数可能没什么帮助。但如果你一遍又一遍地做这件事(看起来你会这么做的话),那可能会更有帮助。

简而言之,我发现将代码分开有助于探索性分析,但可能会为重复分析做一些不同的事情,就像你在想的那样。

答案 5 :(得分:1)

我一直在思考工作流权衡问题。

以下是我为涉及数据分析的任何项目所做的工作:

  1. 加载和清理:为项目创建原始数据集的干净版本,就像我正在构建本地关系数据库一样。因此,我尽可能以3n正常形式构造表格。我执行基本的munging,但是我在这一步 not 合并或过滤表;再一次,我只是为给定的项目创建一个规范化的数据库。我将此步骤放在自己的文件中,然后使用save将对象保存到磁盘。

  2. 函数:我创建了一个函数脚本,其中包含用于数据过滤,合并和聚合任务的函数。这是工作流程中最具智力挑战的部分,因为我不得不考虑如何创建适当的抽象以使函数可重用。这些函数需要进行泛化,以便我可以灵活地合并和汇总来自加载和清理步骤的数据。与LCFD模型一样,此脚本没有副作用,因为它只加载函数定义。

  3. 功能测试:我创建了一个单独的脚本来测试和优化步骤2中定义的函数的性能。我清楚地定义了函数的输出应该是什么,所以这一步作为一种文件(思考单元测试)。

  4. Main :我加载了步骤1中保存的对象。如果表太大而无法容纳在RAM中,我可以使用SQL查询过滤表,并保持数据库思维。然后我通过调用在步骤2中定义的函数来过滤,合并和聚合表。这些表作为参数传递给我定义的函数。函数的输出是适合于绘图,建模和分析的形式的数据结构。显然,我可能会有一些额外的逐行步骤,创建一个新函数是没有意义的。

  5. 此工作流程允许我在Main.R步骤中进行快速探索。这是因为我已经构建了清晰,可推广和优化的功能。与LCFD模型的主要区别在于我不进行逐行过滤,合并或聚合;我假设我可能希望以不同的方式过滤,合并或聚合数据,作为探索的一部分。此外,我不想用冗长的逐行脚本污染我的全球环境;正如Spacedman所指出的,功能有助于此。