R版SAS宏变量?

时间:2015-12-25 02:08:26

标签: r for-loop macros

我对SAS很熟悉。我是R的初学者,我试图找出R等价的宏变量是什么。具体来说,我有6个数据集,其公共变量名称为Price。我想创建一个循环,将每个数据集中的Price更改为DatasetNamePrice。在使用宏变量进行文本替换的SAS中,这很简单。到目前为止,我已经使用每个数据集的名称创建了向量。

v=c("SP","SPF","SPP","NQ","RTY","NYA")

我通常使用此代码重命名变量:

names(SP)[names(SP)=="Price"]="SPPrice"

到目前为止,我的尝试是:

for(i in 1:6) 
{ 
  names(v[[i]])[names(v[[i]])=="Price"]="v[[i]]Price"
}

R在运行时没有给我任何错误,但似乎没有做任何事情。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

以下是一些替代方案。

1)Base R e设置为数据帧的环境。在这里,我们假设它们处于当前环境中。有了这个,e[[nm]]引用了数据框,其名称是变量nm中保存的字符串,因此以下内容可以修改名称:

e <- environment()
for(nm in v) {
   is.price <- names(e[[nm]]) == "Price"
   names(e[[nm]])[ is.price ] <- paste0(nm, "Price")
}

1a)基本R函数传递名称和环境这里我们定义一个函数,它接受数据框和环境的名称,并修改数据框的名称。我们使用match代替==,以便fromto可以选择是名称的​​向量。此解决方案中的原位修改并非真正符合R的功能性,但我们将其作为替代方案展示:

rename1a <- function(DFname, from, to, envir = parent.frame()) {
    ix <- match(from, names(envir[[DFname]]))
    names(envir[[DFname]])[ ix ] <- to
}

for(nm in v) rename1a(nm, "Price", paste0(nm, "Price"))

1b)基本R函数返回副本这里我们定义一个函数,该函数获取数据框本身并返回名称已更改的副本。函数本身不需要处理环境,本质上更具功能性(即它不会修改其输入) - 调用者负责将结果分配回去。

rename1b <- function(DF, from, to) {
    names(DF)[match(from, names(DF))] <- to
    DF
}

e <- environment()
for(nm in v) e[[nm]] <- rename1b(e[[nm]], "Price", paste0(nm, "Price"))
doBy包中的

2)doBy :: renameCol renameCol与(1b)中的rename1b插件兼容,因此:

library(doBy)
e <- environment()
for(nm in v) e[[nm]] <- renameCol(e[[nm]], "Price", paste0(nm, "Price"))

3)plyr :: rename plyr包具有rename功能。请注意,与(1b)类似,它会使用重命名的列生成数据框的副本,因此我们将其分配回来:

e <- environment()
for(nm in v) e[[nm]] <- plyr::rename(e[[nm]], list(Price = paste0(nm, "Price")))

reshape包具有类似的功能,也称为rename,如果我们将plyr::rename替换为reshape::rename,则上述工作正常。

4)gtools :: defmacro 还可以在gtools中使用defmacro来创建一个改变名称的宏。虽然不是典型的R处理,但这确实允许人们传递数据帧本身而不是像(1a)中那样单独的名称和环境。

library(gtools)
rename4 <- defmacro(DF, from, to, expr = { names(DF)[ match(from, names(DF)) ] <- to })

e <- environment()
for(nm in v) rename4(e[[nm]], "Price", paste0(nm, "Price"))

另请参阅R News 2001/3中Thomas Lumley的程序员的Niche文章。

注1:您可能希望首先检查您想要更改这些名称的原因。还有一个问题是数据框架是应该在全球环境中自由定义还是组合成一个列表,因为我们希望大体上处理它们。第一个Map创建一个命名列表L,例如,L$SPL[["SP"]]引用SP中的L组件。第二个Map输出一个新的命名列表,其组件具有新的列名:

L <- Map(get, v) # create named list of input data frames
Map(rename1b, L, "Price", paste0(names(L), "Price"))

注2:在这里,我们使用内置数据框BOD创建一些要测试的输入。这会创建与数据框SP相同的对象SPFBOD等,但第二列的名称为"Price"

# create SP, SPF, ... to test, each with a Price column
v <- c("SP","SPF","SPP","NQ","RTY","NYA")
for(nm in v) assign(nm, setNames(BOD, c("Time", "Price")))

答案 1 :(得分:1)

根据您的需要,您将需要get()assign()函数,因为您试图传递names()属性中需要数据框对象的字符串文字。此外,要使用字符串连接变量,您需要使用paste()

考虑以下使用lapply()(将函数应用于列表或向量并返回列表的递归方法);它重命名字段并将每个数据帧返回到数据帧列表中。然后,for循环使用assign()从此创建的列表中重写原始数据框:

v=c("SP","SPF","SPP","NQ","RTY","NYA")

dfList <- lapply(v, function(x) {
                      df <- get(x)
                      names(df)[grep("Price", names(df))] <- paste0(x, "Price")              
                      return(df)    
                })

for (i in 1:length(v)) {  
     assign(v[[i]], as.data.frame(dfList[[i]]))  
}

rm(dfList)