首页 > 解决方案 > 在 R 中模拟动态范围以按任意函数进行过滤

问题描述

我在一个目录中有许多文件,我想根据它们的内容是否满足某个条件,将它们分布在两个新创建的子目录之间。而且,我需要对多个目录执行这个操作,而且每次的条件可能都不一样。

我想我会这样做的方法是创建一个高阶函数,它读取文件,从每个文件中提取感兴趣的属性,然后应用条件(作为函数形式的参数提供)元素-明智的值的结果向量:

filter.by.property <- function(where, funct) {
  setwd(where)
  paths <- list.files(".", pattern = ".file")
  Robjects <- Map(read.file, paths)
  values <- sapply(Robjects, function(x) property_of_interest(x))
  passes <- Filter(funct, paths)
  dir.create("dir1")
  dir.create("dir2")
  file.copy(passes, paste("./dir1/", 1:length(passes), ".file", sep = ""))
  file.copy(paths[!paths %in% passes], paste("./dir2/", (length(passes) + 1):length(paths), ".file", sep = ""))
}

问题是我希望条件指定函数(在funct参数中提供)能够访问values向量。例如:

ten.biggest <- function(path) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(values), 10)
}

filter.by.property(<place>, ten.biggest)

由于 R 是词法范围的,它会valuesten.biggest定义的环境(全局环境)中查找,而不是在调用它的环境(即 inside filter.by.property)中查找。values我可以通过对inside使用全局赋值来解决这个问题filter.by.property,但除非绝对必要,否则我不希望这样做。我也可以values作为另一个参数提供给ten.biggest,但是该函数将不再是一元的,而且我不确定它如何(甚至是否)仍然可以在Filter.

我尝试的一种解决方案是重写ten.biggest如下

ten.biggest <- function(path) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(eval.parent(parse(text = values))), 10)
}

但这没有用。

有什么方法可以模拟 R 中的动态范围来完成这项工作?通过阅读相关的 StackExchange 问题,似乎该body功能可能会有所帮助,但我不知道如何将其应用于我的问题。

标签: rhigher-order-functionsscoping

解决方案


我找到了一个不依赖于切换环境的解决方案。我允许使用两个参数,并在内部ten.biggest定义了一个辅助函数 ,通过设置其中一个参数将二进制函数转换为一元函数。然后可以通过后者的参数将二进制函数传递给,将 的参数设置为by ,然后可以在内部使用生成的一元函数:outer_fxnfilter.by.propertyten.biggestfilter.by.propertyfunctvalsten.biggestvaluesouter_fxnFilter

ten.biggest <- function(path, vals) {
  Robject <- read.file(path)
  property_of_interest(x) %in% tail(sort(vals), 10)
}

filter.by.property <- function(where, funct) {
  setwd(where)
  paths <- list.files(".", pattern = ".file")
  Robjects <- Map(read.file, paths)
  values <- sapply(Robjects, function(x) property_of_interest(x))
  outer_fxn <- function(x) { funct(x, values) }
  passes <- Filter(outer_fxn, paths)
  dir.create("dir1")
  dir.create("dir2")
  file.copy(passes, paste("./dir1/", 1:length(passes), ".file", sep = ""))
  file.copy(paths[!paths %in% passes], paste("./dir2/", (length(passes) + 1):length(paths), ".file", sep = ""))
}

推荐阅读