试着编写一个在数据帧的一列上运行的简单函数

时间:2013-04-20 22:21:09

标签: r function dataframe user-defined-functions plyr

我正在尝试编写一个“变量化”ddply调用的函数:

december <- ddply(adk47, .(PeakName, Elevation), summarize, 
  needThese=if(sum(dec) == 0) "needThis" 
  else character(0), .progress='text')

df中每个月有3个字母的列名。我正在尝试将函数编写为:

need.fr.month <- function(df, monthCol) {
    needThese <- ddply(df, .(PeakName, Elevation), 
                       summarize, 
                       needThese=if(sum(monthCol) == 0)
                           "needThis" else character(0)
    )
    return(needThese)
}

但是当我用

打电话时
need.fr.month(adk47, oct)

need.fr.month(adk47, "oct")

我收到以下错误消息:

  

eval(expr,envir,enclos)中的错误:找不到对象'monthCol'

  

sum(“monthCol”)中的错误:参数

的'type'(字符)无效

我知道我没有得到一些非常基本的东西,但我不知道是什么。

我正在使用这个DF来练习编写R函数。我的其他功能相当不错;但是,这是我尝试变换df列的第一个函数。

非常感谢帮助。

以下是数据子集的可重现示例

PeakName    Elevation   jul aug sep oct nov dec
Algonquin   5114    0   0   1   0   0   0
Algonquin   5114    0   0   0   0   0   0
Algonquin   5114    0   0   0   1   0   0
Algonquin   5114    1   0   0   0   0   0
Allen   4340    0   0   0   0   0   0
Allen   4340    0   0   0   0   0   0
Allen   4340    0   0   1   0   0   0
Allen   4340    1   0   0   0   0   0
Allen   4340    0   0   0   0   1   0
Armstrong   4400    0   0   0   0   0   0
Armstrong   4400    0   0   0   0   0   0
Armstrong   4400    0   0   0   0   0   0
Armstrong   4400    0   0   0   0   0   0
Armstrong   4400    0   0   0   0   1   0
Armstrong   4400    0   0   0   0   0   0
Armstrong   4400    0   0   0   1   0   0
Basin   4827    1   0   0   0   0   0
Basin   4827    0   0   0   0   0   0
Basin   4827    0   0   0   0   0   0
Basin   4827    0   0   0   0   0   0
Basin   4827    0   0   0   0   0   0
Basin   4827    0   0   0   0   0   0
Basin   4827    0   0   0   0   1   0
Big.Slide   4240    0   0   0   0   0   0
Big.Slide   4240    0   0   0   1   0   0
Big.Slide   4240    0   0   0   0   0   0
Big.Slide   4240    0   0   1   0   0   0
Big.Slide   4240    0   0   0   0   0   0
Big.Slide   4240    0   0   0   0   0   0
Big.Slide   4240    0   0   0   0   0   0
Big.Slide   4240    1   0   0   0   0   0

我希望这就足够了。显然,这是数据的一个子集。表格是每个“加息”都有一行与月份列(这里截断到7月到12月),表示一个月为“1”,另一个月为零。

由于

韦恩

4 个答案:

答案 0 :(得分:3)

致电时

need.fr.month(adk47, oct)

R在您的常规环境中查找名为oct的变量,但什么都没找到。因此它报告说找不到它。

如果你打电话:

need.fr.month(adk47, "oct")

R尝试使用字符串"oct"代替monthCol。但是,取一个字符串的sum没有意义,所以它会抛出一个错误。

将参数传递给内部函数可能很困难。臭名昭着的eval-parse结构是一个快速的kludge。虽然它完成了工作,但它是generally not recommended,因为通常有更简单的方法来完成同样的工作。

need.fr.month <- function(df, monthCol) {
    needThese <- eval(parse(text=paste0("ddply(df, .(PeakName, Elevation), 
                       summarize, 
                       needThese=if(sum(", monthCol, ") == 0)
                           "needThis" else character(0)
                 ")))
    )
    return(needThese)
}

在这里,您无需进行eval-parse即可获得所需内容。只是不要使用summarize并依赖基本R提取函数:

need.fr.month <- function(df, monthCol) {
    needThese <- ddply(df, .(PeakName, Elevation), 
                       function(x) sum(x[[monthCol]]))
    return(needThese)
    #return(needThese[needThese[["V1"]] != 0,])
}

我认为这种方法可以做得更好,但如果不了解您想要对信息做什么,我就无法进一步改进。如果你想找到你想要分组的行,我认为做这样的事情会更好:

need.fr.month <- function(df, monthCol) {
ave(df[[monthCol]],df[["PeakName"]],df[["Elevation"]],FUN=sum)
}
adk47$need <- need.fr.month(adk47,"dec") == 0

然后,您会在数据框中为您提供一列,通过adk47$need == TRUE为您提供所需数据的子集。

答案 1 :(得分:2)

看起来,summarize无法从调用ddply的环境中找到对象。但是,您可以手动将此环境附加到搜索路径。 ddply调用后,您可以分离环境。

这里有一个简单的例子 - 类似的方法也适用于你。

test_fun=function(team_vec)
{
    attach(environment())
    tmp=ddply(baseball,
              "team",
              summarise,
              duration=(if (unique(team)%in%team_vec) max(year)-min(year) else 0)
             )
    detach(environment())
    tmp
}

test_fun(c("PIT","PHI"))

答案 2 :(得分:2)

谢谢大家,这两个都非常有用。

我选择了Blue Magister第二个例子的修改版本:

need.fr.month <- function(df, monthCol) {
needThese <- ddply(df, .(PeakName, Elevation),
                   function(x) sum(x[[monthCol]]))
subsetNeedThese <- subset(needThese, V1 == 0, select=c(PeakName, Elevation))

}

因为它完全返回我需要的东西,我理解它在做什么。我之前没有处理过附加和分离环境,所以我感谢croy111的例子。我需要阅读这个!同样,Blue Magister的eval-parse对我来说似乎是一种简单的方法,我可以做一些我真的不理解的事情。

我赞赏Blue Magister的评论:“将参数传递给内部函数可能很困难”。我现在会接受,如果你避免调用内部函数(例如“汇总”)并在下次遇到像这样的问题时再考虑一下这个问题就会消失!!

答案 3 :(得分:2)

我认为创建一个您的指标变量为指标变量(如describeie Optimization: splitting dataframe into a list of dataframes, transforming data per row)然后从中进行子集的列会容易得多。

我主张使用data.table而不是ddply + summarize来提高效率(但这可能是一个长期目标)

使用data.table访问set(适用于data.frames)

library(data.table)
adk47$monthCol <- character(nrow(adk47))
# data.table specific
# adk47 <- data.table(adk47)
# adk47[, monthCol := character(nrow(adk47))]

# find which columns are == 1
whiches <- lapply(adk47[c("jul", "aug", "sep", "oct", "nov", "dec")],
                  function(x) which(x==1))
# data.table approach would require 
#  adk47[c("jul", "aug", "sep", "oct", "nov", "dec"),with = TRUE]

for(val in names(whiches)){ 
  set(adk47, i = whiches[[val]], j = 'monthCol', value = val)
  }

head(adk47)


       PeakName Elevation jul aug sep oct nov dec monthCol
1 Algonquin      5114   0   0   1   0   0   0      sep
2 Algonquin      5114   0   0   0   0   0   0         
3 Algonquin      5114   0   0   0   1   0   0      oct
4 Algonquin      5114   1   0   0   0   0   0      jul
5     Allen      4340   0   0   0   0   0   0         
6     Allen      4340   0   0   0   0   0   0         

然后,您可以使用monthCol

进行子集化