2010-10-14 8 views
11

Question: Je teste des fonctions dans un paquet que je suis en train de développer et je voudrais savoir si vous pouvez suggérer quelques directives générales pour ce faire. Les fonctions comprennent un large éventail de modélisation statistique, de transformations, de sous-ensembles et de traçage. Existe-t-il un test «standard» ou suffisant?directives pour tester une fonction statistique dans R?

Exemple: le test qui m'a poussé pose cette question,

La fonction dtheta:

dtheta <- function(x) { 
    ## find the quantile of the mean 
    q.mean <- mean(mean(x) >= x) 
    ## find the quantiles of ucl and lcl (q.mean +/- 0.15) 
    q.ucl <- q.mean + 0.15 
    q.lcl <- q.mean - 0.15 
    qs <- c(q.lcl, q.mean, q.ucl) 
    ## find the lcl, mean, and ucl of the vector 
    c(quantile(x,qs), var(x), sqrt(var(x))/mean(x)) 
} 

Étape 1: données de test:

set.seed(100) # per Dirk's recommendation 
test <- rnorm(100000,10,1) 

Étape 2: comparer la sortie attendue de la fonction avec la sortie réelle de la fonction:

expected <- quantile(test, c(0.35, 0.65, 0.5)) 
actual <- dtheta(test)[1:3] 
signif(expected,2) %in% signif(actual,2) 

Étape 3: peut-être faire un autre test

test2 <- runif(100000, 0, 100) 
expected <- c(35, 50, 65) 
actual <- dtheta(test2) 
expected %in% signif(actual,2) 

Étape 4: si cela est vrai, pensez à la fonction 'fonctionnelle'

Répondre

6

Cela dépend de ce que vous voulez tester exactement. A côté de recommandations Dirks, svUnit ou le paquet RUnit Vitoshka mentionné, je voudrais ajouter quelques petites choses:

  • En effet, définissez la graine, mais assurez-vous d'essayer la fonction de graines différentes aussi bien. Certaines fonctions échouent seulement une fois toutes les dix fois que vous essayez. Surtout quand l'optimisation est impliquée, cela devient crucial. replicate() est une bonne fonction à utiliser dans ce contexte. Pensez très bien à l'entrée que vous voulez tester. Vous devriez tester un certain nombre de cas "impairs" qui ne ressemblent pas vraiment au jeu de données "parfait". Je teste toujours au moins 10 jeux de données (simulés) de différentes tailles.
  • Fool-proof la fonction: Je jette également dans certains types de données qui ne sont pas ceux pour lesquels la fonction est destinée. Une entrée de type incorrect est susceptible de se produire à un moment donné, et la dernière chose que vous voulez est une fonction retournant un faux résultat sans avertissement. Si vous utilisez cette fonction plus tard dans un autre code, le débogage de ce code peut et va! soit l'enfer. Été là, fait cela, a acheté le t-shirt ...

Un exemple sur les tests étendus des ensembles de données: que voulez-vous voir comme sortie dans ces cas? Est-ce le résultat que vous attendez? Pas selon le test que tu as fait.

> test3 <- rep(12,100000) # data with only 1 value 
> expected <- c(12, 12, 12) 
> actual <- dtheta(test3) 
Error in quantile.default(x, qs) : 'probs' outside [0,1] 

> test4 <- rbinom(100000,30,0.5) # large dataset with a limited amount of values 
> expected <- quantile(test4,c(0.35, 0.50, 0.65)) 
> actual <- dtheta(test4) 
> expected %in% signif(actual,2) 
[1] FALSE TRUE TRUE 

> test5 <- runif(100,0,100) # small dataset. 
> expected <- c(35, 50, 65) 
> actual <- dtheta(test5) 
> expected %in% signif(actual,2) 
[1] FALSE FALSE FALSE 

éditer: code corrigé donc les tests sont un peu plus sensés.

+1

+1 Joris pour tester l'entrée. Je voudrais ajouter pour tester la * sortie * aussi bien. La sortie de vos fonctions doit être * prévisible * et * définie avec précision *. Malheureusement sur ce point, la fonctionnalité de base de R est parfois tout simplement nulle. Ne sait jamais exactement ce que la fonction retourne, sauf si vous allez à nouveau à la documentation, et encore, et encore ... – VitoshKa

5

question Nice. En dehors des généralités telles que la définition d'une graine, je vous recommande de regarder certains des tests dans les sources R. Le répertoire tests/ dans la source a une richesse de ceux-ci; certains des packages de R Base (tels que les outils) ont également le sous-répertoire tests/.

+0

Excellente ressource. Merci pour le pointeur. J'ai ajouté set.seed() à mon code et je suis sûr de commencer à l'utiliser. L'utilisez-vous pour l'analyse ou juste pour des tests et des exemples? –

+1

Sans une graine fixe vous n'avez aucune reproductibilité en faisant des comparaisons, débogage, ... un peu plus difficile ;-) –

+3

Vous connaissez probablement déjà [runit] (http://cran.r-project.org/web/packages/RUnit /index.html) – VitoshKa

6

Vous devez écrire

  1. des tests qui montrent que vous obtenez la bonne réponse lorsque vous entrez les valeurs sensibles

  2. tests qui montrent votre fonction échoue correctement lorsque vous un non-sens d'entrée.

  3. test pour tous les cas limites

Il y a une énorme quantité de littérature sur les différentes stratégies pour les logiciels de test; Le software testing page de Wikipedia est le meilleur endroit pour commencer.

En regardant votre exemple:

Qu'est-ce qui se passe lorsque vous entrez une chaîne/dataframe/liste?
Qu'en est-il négatif x ou imaginaire x?
Que diriez-vous de vecteur/tableau x?
Si seulement x est autorisé, alors que se passe-t-il à x = 0?

Notez que les sous-fonctions (qui ne sont appelées que par vos fonctions et jamais par l'utilisateur) nécessitent moins de vérification des entrées car vous avez plus de contrôle sur ce qui entre dans la fonction.

3

Il est déjà apparu comme un commentaire, mais je vais l'ajouter comme une réponse de bonne foi. R a quelques paquets de tests automatisés pour aider avec ce genre de chose, les deux principaux étant Runit et testthat. J'ai brièvement utilisé runit, et récemment commencé à utiliser testthat plus en profondeur (je ne peux pas vraiment donner de bons avantages/inconvénients de l'un sur l'autre si!).

Les tests automatisés vous permettent de configurer ces cas de test, ainsi que d'autres comme suggéré ci-dessus comme;

  • Boundary Tests
  • stress tests (moins besoin de tester la précision, il suffit de jeter des données à et voir si elle tombe)
  • Faire face à l'entrée différente
  • Faire face à différentes plates-formes/locales sous-jacentes
+1

n'oubliez pas 'svUnit', une partie de sciviews. – mariotomo

Questions connexes