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")
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
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')
}