Nombre de problèmes par essai

Le nombre de problèmes trouvés à chaque itération suit la même loi que le nombre de bogues trouvés à chaque itération de test d’un logiciel~: une proportion constante du nombre total de problèmes de problèmes est identifiée à chaque itération. Il s’agit du modèle avancé par Goel et Ukomoto (1979).

De manière plus générale, la courbe de problèmes restants suit donc une courbe dite de qui s’exprime, dans la forme de temps continu du temps~\(t\), selon la formule~: \(N_0 e^{\lambda t}\), où~\(N_0\) est la quantité initiale et~\(\lambda\) est la constante de décroissance. Comme le temps est ici un nombre d’itérations qui correspond au nombre de sujets participants au test, \(n\), la formule peut s’exprimer~: \(N(1-(1-L)^n)\), où~\(L\) est l’équivalent de la constante de décroissance et représente en fait cette proportion de problèmes identifiés à chaque sujet.

Il est ainsi possible à partir de déterminer combien de problèmes initiaux existaient au début des tests et combien de problèmes subsisteront après un nombre~\(n\) de tests.

Commençons par charger les données de Nielsen et Landauer

nl <- as.matrix(read.csv('donnees-nielse-landauer.csv',header=F))
nl[is.na(nl)] <- 0
colnames(nl) <- paste0('p',1:16)
rownames(nl) <- paste0('expert',1:19)
nl
##          p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16
## expert1   0  0  0  0  0  0  0  0  0   0   0   0   0   1   1   1
## expert2   0  0  0  0  1  0  0  0  0   0   0   0   0   1   1   1
## expert3   0  0  0  0  0  0  1  0  0   0   1   0   1   0   0   1
## expert4   0  0  0  0  0  0  0  0  1   1   0   1   1   0   0   1
## expert5   0  0  0  0  0  0  0  0  1   0   1   0   0   1   1   1
## expert6   0  0  0  0  0  0  0  0  0   0   1   1   0   0   1   1
## expert7   1  0  0  0  0  0  0  0  0   1   1   0   1   1   1   0
## expert8   0  0  0  1  1  1  0  0  0   0   0   0   1   1   0   1
## expert9   0  0  0  1  0  1  0  0  1   0   0   0   1   1   0   1
## expert10  0  0  0  0  1  1  1  0  0   0   0   1   0   1   1   1
## expert11  0  0  0  0  0  0  0  0  1   1   1   1   0   1   1   1
## expert12  0  0  0  0  0  0  0  1  1   0   0   1   1   1   1   1
## expert13  1  1  0  0  0  0  0  1  0   1   0   1   1   0   0   1
## expert14  0  0  0  0  0  0  1  1  1   0   0   1   1   1   1   0
## expert15  0  1  0  1  0  0  1  0  0   1   1   0   1   0   1   1
## expert16  0  0  1  0  1  1  0  1  1   0   1   0   1   1   1   0
## expert17  0  0  1  0  0  1  1  1  0   1   0   0   1   1   1   1
## expert18  0  0  1  1  1  0  0  0  0   1   1   1   1   0   1   1
## expert19  0  0  1  1  1  0  1  1  0   1   1   1   0   0   1   1
plot(ncol(nl) - rowSums(apply(nl, 2, cumsum) == 0), ylab='Problèmes trouvés', xlab='N expert', main="Problèmes trouvés par nombre d'experts")

ordreExperts <- c(9, 4, 8, 11, 19, 3, 7, 14, 13, 1, 2, 5, 17, 10, 6, 18, 15, 12, 16) # ordre aléatoire des experts
plot(ncol(nl) - rowSums(apply(nl[ordreExperts,], 2, cumsum) == 0), ylab='Problèmes trouvés', xlab='N expert', main="Problèmes trouvés par nombre d'experts")

Estimation des paramètres de la distribution

Les calculs ci-dessous démontrent que les paramètres estimés sont :

\(N0 = 16\)

$L = 0.28

sequenceExperts <- data.frame(y=(ncol(nl) - rowSums(apply(nl[ordreExperts,], 2, cumsum) == 0)), x=1:nrow(nl))
sequenceExperts
##           y  x
## expert9   6  1
## expert4   8  2
## expert8   9  3
## expert11 11  4
## expert19 14  5
## expert3  14  6
## expert7  15  7
## expert14 15  8
## expert13 16  9
## expert1  16 10
## expert2  16 11
## expert5  16 12
## expert17 16 13
## expert10 16 14
## expert6  16 15
## expert18 16 16
## expert15 16 17
## expert12 16 18
## expert16 16 19
mod <- nls(y ~ N0 * (1-(1-L)^x), data = sequenceExperts, start = list(N0 = 1, L = .5))
summary(mod)
## 
## Formula: y ~ N0 * (1 - (1 - L)^x)
## 
## Parameters:
##    Estimate Std. Error t value Pr(>|t|)    
## N0  16.2275     0.2030   79.95  < 2e-16 ***
## L    0.2838     0.0134   21.18 1.17e-13 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5908 on 17 degrees of freedom
## 
## Number of iterations to convergence: 6 
## Achieved convergence tolerance: 4.537e-06

Une fois ces paramètres estimés, on peut les utiliser pour fin d’estimation du nombre de problèmes restants en fonction du nombre d’essais (experts dans des tests heuristiques ou sujets dans des tests d’utilisabilité).

plot(sequenceExperts$x, sequenceExperts$y, xlim=c(0,nrow(nl)), ylab='Problèmes trouvés', xlab='N expert', main='Problèmes trouvés par nombre d\'experts')
lines(sequenceExperts$x, predict(mod, list(x = sequenceExperts$x)))

cbind(expert=1:19, N.Problm=predict(mod, c(x=c(0,1,4))))
##       expert  N.Problm
##  [1,]      1  4.606060
##  [2,]      2  7.904719
##  [3,]      3 10.267074
##  [4,]      4 11.958890
##  [5,]      5 13.170495
##  [6,]      6 14.038193
##  [7,]      7 14.659601
##  [8,]      8 15.104626
##  [9,]      9 15.423333
## [10,]     10 15.651578
## [11,]     11 15.815036
## [12,]     12 15.932098
## [13,]     13 16.015933
## [14,]     14 16.075972
## [15,]     15 16.118969
## [16,]     16 16.149761
## [17,]     17 16.171814
## [18,]     18 16.187607
## [19,]     19 16.198917

Estimation du nombre de problèmes restants avec seulement 5 experts

Pour explorer l’erreur à laquelle on peut s’attendre avec des données semblables à l’exemple si on limitait le nombre d’experts à 5, nous pouvons simuler un ordre différent et répéter quelques fois l’expérience afin d’obtenir un estimé plus fiable.

## On fixe maintenant à 5 experts
nombreExperts <- 5
nombrePrblms <- 16
nombrePrblmsMax <- 20
expertsMax <- 20
plot(sequenceExperts$x, sequenceExperts$y, ylim=c(0, nombrePrblmsMax), xlim=c(0,nrow(nl)), ylab='Problèmes trouvés', xlab='N expert', main='Problèmes trouvés par nombre d\'experts')
for(i in 1:5) {
    ordreExperts <- sample(nrow(nl), nombreExperts)
    sequenceExperts <- data.frame(y=(nombrePrblms - rowSums(apply(nl[ordreExperts,], 2, cumsum) == 0)), x=1:nombreExperts)
    mod <- nls(y ~ N0 * (1-(1-L)^x), data = sequenceExperts, start = list(N0 = 1, L = .5))
    lines(sequenceExperts$x, predict(mod, list(x = sequenceExperts$x)))
    cbind(expert=1:nombreExperts, N.Problm=predict(mod, c(x=c(0,1,4))))
    predict(mod, list(x = 1:20))
    lines(1:expertsMax, predict(mod, list(x = 1:expertsMax)), col='blue', lty='dotted')
}