web-dev-qa-db-fra.com

Calculer l'aire sous une courbe

Je voudrais calculer l'aire sous une courbe pour faire l'intégration sans définir une fonction comme dans integrate().

Mes données ressemble à ceci:

Date          Strike     Volatility
2003-01-01    20         0.2
2003-01-01    30         0.3
2003-01-01    40         0.4
etc.

J'ai tracé plot(strike, volatility) pour regarder le sourire de volatilité. Y a-t-il un moyen d'intégrer cette "courbe" tracée?

52
Dani

On se rapproche assez facilement de l’ASC en regardant beaucoup de figures de trapèze, chaque fois liées entre x_i, x_{i+1}, y{i+1} et y_i. En utilisant le moyen du paquet Zoo, vous pouvez faire:

library(Zoo)

x <- 1:10
y <- 3*x+25
id <- order(x)

AUC <- sum(diff(x[id])*rollmean(y[id],2))

Assurez-vous de commander les valeurs x, sinon votre résultat n'aura aucun sens. Si vous avez des valeurs négatives quelque part sur l’axe des y, vous devez déterminer comment définir exactement l’aire sous la courbe et ajuster en conséquence (par exemple, en utilisant abs())

Concernant votre suivi: si vous n’avez pas de fonction formelle, comment la traceriez-vous? Donc, si vous n’avez que des valeurs, la seule chose que vous pouvez approximer est une intégrale définie. Même si vous avez la fonction dans R, vous ne pouvez calculer que les intégrales définies à l'aide de integrate(). Tracer la fonction formelle n'est possible que si vous pouvez également la définir.

36
Joris Meys

Ajoutez simplement ce qui suit à votre programme et vous obtiendrez l’aire sous la courbe:

require(pracma)
AUC = trapz(strike,volatility)

De ?trapz:

Cette approche correspond exactement à l’approximation pour l’intégration du fonction utilisant la règle trapézoïdale avec points de base x.

31
simon

Trois autres options, dont une utilisant une méthode spline et une utilisant la règle de Simpson ...

# get data
n <- 100
mean <- 50
sd <- 50

x <- seq(20, 80, length=n)
y <- dnorm(x, mean, sd) *100

# using sintegral in Bolstad2
require(Bolstad2)
sintegral(x,y)$int

# using auc in MESS
require(MESS)
auc(x,y, type = 'spline')

# using integrate.xy in sfsmisc
require(sfsmisc)
integrate.xy(x,y)

La méthode trapézoïdale est moins précise que la méthode spline, donc MESS::auc (utilise la méthode spline) ou Bolstad2::sintegral (utilise la règle de Simpson) devrait probablement être préféré. Des versions personnalisées de ceux-ci (et une approche supplémentaire utilisant la règle de quadrature) se trouvent ici: http://www.r-bloggers.com/one-dimensional-integrals/

20
Ben

OK, donc j'arrive un peu en retard à la soirée mais en parcourant les réponses, il manque une solution simple R au problème. Voilà, simple et propre:

sum(diff(x) * (head(y,-1)+tail(y,-1)))/2

La solution pour OP se lit alors comme suit:

sum(diff(strike) * (head(volatility,-1)+tail(volatility,-1)))/2

Ceci calcule efficacement la surface en utilisant la méthode trapézoïdale en prenant la moyenne des valeurs y "gauche" et "droite".

NB: comme @Joris l'a déjà indiqué, vous pouvez utiliser abs(y) si cela vous semble plus logique.

11
Victor Klos

Dans le monde de la pharmacocinétique (PK), le calcul de différents types d'AUC est une tâche commune et fondamentale. Il existe de nombreux calculs de l’ASC pour la pharmacokiétique, tels que

  • AUC0-t = AUC de zéro à l'instant t
  • AUC0-last = AUC de zéro au dernier point de temps (peut être identique à ci-dessus)
  • AUC0-inf = AUC de zéro à l'infini
  • AUCint = AUC sur un intervalle de temps
  • AUCall = AUC sur toute la période pour laquelle des données existent

L'un des meilleurs packages qui effectue ces calculs est le package relativement nouveau PKNCA, proposé par les employés de Pfizer. Vérifiez-le.

3
hackR

La réponse de Joris Meys était géniale mais j'ai eu du mal à retirer les NA de mes échantillons. Voici la petite fonction que j'ai écrite pour les traiter:

library(Zoo) #for the rollmean function

######
#' Calculate the Area Under Curve of y~x
#'
#'@param y Your y values (measures ?)
#'@param x Your x values (time ?)
#'@param start : The first x value 
#'@param stop : The last x value
#'@param na.stop : returns NA if one value is NA
#'@param ex.na.stop : returns NA if the first or the last value is NA
#'
#'@examples 
#'myX = 1:5
#'myY = c(17, 25, NA, 35, 56)
#'auc(myY, myX)
#'auc(myY, myX, na.stop=TRUE)
#'myY = c(17, 25, 28, 35, NA)
#'auc(myY, myX, ex.na.stop=FALSE)
auc = function(y, x, start=first(x), stop=last(x), na.stop=FALSE, ex.na.stop=TRUE){
  if(all(is.na(y))) return(NA)
  bounds = which(x==start):which(x==stop)
  x=x[bounds]
  y=y[bounds]
  r = which(is.na(y))
  if(length(r)>0){
    if(na.stop==TRUE) return(NA)
    if(ex.na.stop==TRUE & (is.na(first(y)) | is.na(last(y)))) return(NA)
    if(is.na(last(y))) warning("Last value is NA, so this AUC is bad and you should feel bad", call. = FALSE) 
    if(is.na(first(y))) warning("First value is NA, so this AUC is bad and you should feel bad", call. = FALSE) 
    x = x[-r]
    y = y[-r]
  }
  sum(diff(x[order(x)])*rollmean(y[order(x)],2))
}

Je l’utilise ensuite avec un apply sur mon dataframe: myDF$auc = apply(myDF, MARGIN=1, FUN=auc, x=c(0,5,10,15,20))

J'espère que ça peut aider des noobs comme moi :-)

EDIT: ajout de bornes

0
Dan Chaltiel