Упростите ave () или aggregate () с несколькими входами - PullRequest
0 голосов
/ 29 сентября 2010

Как я могу написать все это в одной строке?

mydata - серия "zoo", предел - числовой вектор того же размера

tmp <- ave(coredata(mydata), as.Date(index(mydata)),
           FUN = function(x) cummax(x)-x)
tmp <- (tmp < limit)
final <- ave(tmp, as.Date(index(mydata)),
             FUN = function(x) cumprod(x))

Я пытался использовать два вектора в качестве аргумента для ave(...), но, похоже, он принимает только один, даже если я объединю их в матрицу.

Это всего лишь пример, но можно использовать любую другую функцию.

Здесь мне нужно сравнить значение cummax(mydata)-mydata с числовым вектором и как только он превзойдет его, я буду держать нули до конца дня. cummax рассчитывается с начала каждого дня.

Если бы предел был одним числом вместо вектора (с различными возможными числами), я мог бы написать это:

ave(coredata(mydata), as.Date(index(mydata)),
    FUN = function(x) cumprod((cummax(x) - x) < limit))

Но я не могу представить там вектор длиннее x (он должен иметь одинаковую длину, чем каждый день), и я не знаю, как ввести его в качестве другого аргумента в ave().

1 Ответ

1 голос
/ 02 ноября 2010

Похоже, эта процедура накладывает внутридневной стоп-лосс на основе максимального отката. Поэтому я предполагаю, что вы хотите иметь возможность передавать переменное ограничение в качестве второго аргумента вашей функции агрегирования, которая только в настоящее время принимает только 1 функцию из-за способа работы ave.

Если поместить все это в одну строку не является абсолютной необходимостью, я могу поделиться написанной мной функцией, которая обобщает агрегацию через «переменные среза». Вот код:

mtapplylist2 <- function(t, IDX, DEF, MoreArgs=NULL, ...)
{
  if(mode(DEF) != "list")
  {
    cat("Definition must be list type\n");
    return(NULL);
  }

  a        <- c();
  colnames <- names(DEF);
  for ( i in 1:length(DEF) )
  {
    def  <- DEF[[i]];
    func <- def[1];
    if(mode(func) == "character") { func <- get(func); }
    cols <- def[-1];

    # build the argument to be called
    arglist      <- list();
    arglist[[1]] <- func;
    for( j in 1:length(cols) )
    {
      col <- cols[j];
      grp <- split(t[,col], IDX);
      arglist[[1+j]] <- grp;
    }
    arglist[["MoreArgs"]] <- MoreArgs;
    v <- do.call("mapply", arglist);
    # print(class(v)); print(v);
    if(class(v) == "matrix")
    {
      a <- cbind(a, as.vector(v));
    } else {
      a <- cbind(a, v);
    }
  }
  colnames(a) <- colnames;
  return(a);
}

И вы можете использовать его так:

# assuming you have the data in the data.frame
df  <- data.frame(date=rep(1:10,10), ret=rnorm(100), limit=rep(c(0.25,0.50),50))

dfunc <- function(x, ...) { return(cummax(x)-x ) }
pfunc <- function(x,y, ...) { return((cummax(x)-x) < y) }

# assumes you have the function declared in the same namespace
def <- list(
 "drawdown"    = c("dfunc", "ret"),
 "hasdrawdown" = c("pfunc", "ret", "limit")
);

# from R console
> def <- list("drawdown" = c("dfunc", "ret"),"happened" = c("pfunc","ret","limit"))
> dim( mtapplylist2(df, df$date, def) )
[1] 100   2

Обратите внимание, что переменная "def" представляет собой список, содержащий следующие элементы:

  • имя вычисляемого столбца
  • имя функции векторного аргумента в виде строки
  • имя переменной во входных данных.frame, которые являются входными данными для функции

Если вы посмотрите на внутренности функции "mtapplylist2", ключевыми компонентами будут "split" и "mapply". Эти функции достаточно быстрые (я думаю, что разделение реализовано в C).

Это работает с функциями, требующими нескольких аргументов, а также с функциями, возвращающими вектор одинакового размера или агрегированного значения.

Попробуйте и дайте мне знать, если это решит вашу проблему.

...