Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/quiz/report/statistics/qstats.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 
00036 class quiz_statistics_question_stats {
00037     public $questions;
00038     public $subquestions = array();
00039 
00040     protected $s;
00041     protected $summarksavg;
00042     protected $allattempts;
00043 
00045     protected $lateststeps;
00046 
00047     protected $sumofmarkvariance = 0;
00048     protected $randomselectors = array();
00049 
00056     public function __construct($questions, $s, $summarksavg) {
00057         $this->s = $s;
00058         $this->summarksavg = $summarksavg;
00059 
00060         foreach ($questions as $slot => $question) {
00061             $question->_stats = $this->make_blank_question_stats();
00062             $question->_stats->questionid = $question->id;
00063             $question->_stats->slot = $slot;
00064         }
00065 
00066         $this->questions = $questions;
00067     }
00068 
00072     protected function make_blank_question_stats() {
00073         $stats = new stdClass();
00074         $stats->slot = null;
00075         $stats->s = 0;
00076         $stats->totalmarks = 0;
00077         $stats->totalothermarks = 0;
00078         $stats->markvariancesum = 0;
00079         $stats->othermarkvariancesum = 0;
00080         $stats->covariancesum = 0;
00081         $stats->covariancemaxsum = 0;
00082         $stats->subquestion = false;
00083         $stats->subquestions = '';
00084         $stats->covariancewithoverallmarksum = 0;
00085         $stats->randomguessscore = null;
00086         $stats->markarray = array();
00087         $stats->othermarksarray = array();
00088         return $stats;
00089     }
00090 
00099     public function load_step_data($quizid, $currentgroup, $groupstudents, $allattempts) {
00100         global $DB;
00101 
00102         $this->allattempts = $allattempts;
00103 
00104         list($qsql, $qparams) = $DB->get_in_or_equal(array_keys($this->questions),
00105                 SQL_PARAMS_NAMED, 'q');
00106         list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql(
00107                 $quizid, $currentgroup, $groupstudents, $allattempts, false);
00108 
00109         $this->lateststeps = $DB->get_records_sql("
00110                 SELECT
00111                     qas.id,
00112                     quiza.sumgrades,
00113                     qa.questionid,
00114                     qa.slot,
00115                     qa.maxmark,
00116                     qas.fraction * qa.maxmark as mark
00117 
00118                 FROM $fromqa
00119                 JOIN {question_attempts} qa ON qa.questionusageid = quiza.uniqueid
00120                 JOIN (
00121                     SELECT questionattemptid, MAX(id) AS latestid
00122                       FROM {question_attempt_steps}
00123                   GROUP BY questionattemptid
00124                 ) lateststepid ON lateststepid.questionattemptid = qa.id
00125                 JOIN {question_attempt_steps} qas ON qas.id = lateststepid.latestid
00126 
00127                 WHERE
00128                     qa.slot $qsql AND
00129                     $whereqa", $qparams + $qaparams);
00130     }
00131 
00132     public function compute_statistics() {
00133         set_time_limit(0);
00134 
00135         $subquestionstats = array();
00136 
00137         // Compute the statistics of position, and for random questions, work
00138         // out which questions appear in which positions.
00139         foreach ($this->lateststeps as $step) {
00140             $this->initial_steps_walker($step, $this->questions[$step->slot]->_stats);
00141 
00142             // If this is a random question what is the real item being used?
00143             if ($step->questionid != $this->questions[$step->slot]->id) {
00144                 if (!isset($subquestionstats[$step->questionid])) {
00145                     $subquestionstats[$step->questionid] = $this->make_blank_question_stats();
00146                     $subquestionstats[$step->questionid]->questionid = $step->questionid;
00147                     $subquestionstats[$step->questionid]->allattempts = $this->allattempts;
00148                     $subquestionstats[$step->questionid]->usedin = array();
00149                     $subquestionstats[$step->questionid]->subquestion = true;
00150                     $subquestionstats[$step->questionid]->differentweights = false;
00151                     $subquestionstats[$step->questionid]->maxmark = $step->maxmark;
00152                 } else if ($subquestionstats[$step->questionid]->maxmark != $step->maxmark) {
00153                     $subquestionstats[$step->questionid]->differentweights = true;
00154                 }
00155 
00156                 $this->initial_steps_walker($step,
00157                         $subquestionstats[$step->questionid], false);
00158 
00159                 $number = $this->questions[$step->slot]->number;
00160                 $subquestionstats[$step->questionid]->usedin[$number] = $number;
00161 
00162                 $randomselectorstring = $this->questions[$step->slot]->category .
00163                         '/' . $this->questions[$step->slot]->questiontext;
00164                 if (!isset($this->randomselectors[$randomselectorstring])) {
00165                     $this->randomselectors[$randomselectorstring] = array();
00166                 }
00167                 $this->randomselectors[$randomselectorstring][$step->questionid] =
00168                         $step->questionid;
00169             }
00170         }
00171 
00172         foreach ($this->randomselectors as $key => $notused) {
00173             ksort($this->randomselectors[$key]);
00174         }
00175 
00176         // Compute the statistics of question id, if we need any.
00177         $this->subquestions = question_load_questions(array_keys($subquestionstats));
00178         foreach ($this->subquestions as $qid => $subquestion) {
00179             $subquestion->_stats = $subquestionstats[$qid];
00180             $subquestion->maxmark = $subquestion->_stats->maxmark;
00181             $subquestion->_stats->randomguessscore = $this->get_random_guess_score($subquestion);
00182 
00183             $this->initial_question_walker($subquestion->_stats);
00184 
00185             if ($subquestionstats[$qid]->differentweights) {
00186                 // TODO output here really sucks, but throwing is too severe.
00187                 global $OUTPUT;
00188                 echo $OUTPUT->notification(
00189                         get_string('erroritemappearsmorethanoncewithdifferentweight',
00190                         'quiz_statistics', $this->subquestions[$qid]->name));
00191             }
00192 
00193             if ($subquestion->_stats->usedin) {
00194                 sort($subquestion->_stats->usedin, SORT_NUMERIC);
00195                 $subquestion->_stats->positions = implode(',', $subquestion->_stats->usedin);
00196             } else {
00197                 $subquestion->_stats->positions = '';
00198             }
00199         }
00200 
00201         // Finish computing the averages, and put the subquestion data into the
00202         // corresponding questions.
00203 
00204         // This cannot be a foreach loop because we need to have both
00205         // $question and $nextquestion available, but apart from that it is
00206         // foreach ($this->questions as $qid => $question) {
00207         reset($this->questions);
00208         while (list($slot, $question) = each($this->questions)) {
00209             $nextquestion = current($this->questions);
00210             $question->_stats->allattempts = $this->allattempts;
00211             $question->_stats->positions = $question->number;
00212             $question->_stats->maxmark = $question->maxmark;
00213             $question->_stats->randomguessscore = $this->get_random_guess_score($question);
00214 
00215             $this->initial_question_walker($question->_stats);
00216 
00217             if ($question->qtype == 'random') {
00218                 $randomselectorstring = $question->category.'/'.$question->questiontext;
00219                 if ($nextquestion && $nextquestion->qtype == 'random') {
00220                     $nextrandomselectorstring = $nextquestion->category . '/' .
00221                             $nextquestion->questiontext;
00222                     if ($randomselectorstring == $nextrandomselectorstring) {
00223                         continue; // Next loop iteration
00224                     }
00225                 }
00226                 if (isset($this->randomselectors[$randomselectorstring])) {
00227                     $question->_stats->subquestions = implode(',',
00228                             $this->randomselectors[$randomselectorstring]);
00229                 }
00230             }
00231         }
00232 
00233         // Go through the records one more time
00234         foreach ($this->lateststeps as $step) {
00235             $this->secondary_steps_walker($step,
00236                     $this->questions[$step->slot]->_stats);
00237 
00238             if ($this->questions[$step->slot]->qtype == 'random') {
00239                 $this->secondary_steps_walker($step,
00240                         $this->subquestions[$step->questionid]->_stats);
00241             }
00242         }
00243 
00244         $sumofcovariancewithoverallmark = 0;
00245         foreach ($this->questions as $slot => $question) {
00246             $this->secondary_question_walker($question->_stats);
00247 
00248             $this->sumofmarkvariance += $question->_stats->markvariance;
00249 
00250             if ($question->_stats->covariancewithoverallmark >= 0) {
00251                 $sumofcovariancewithoverallmark +=
00252                         sqrt($question->_stats->covariancewithoverallmark);
00253                 $question->_stats->negcovar = 0;
00254             } else {
00255                 $question->_stats->negcovar = 1;
00256             }
00257         }
00258 
00259         foreach ($this->subquestions as $subquestion) {
00260             $this->secondary_question_walker($subquestion->_stats);
00261         }
00262 
00263         foreach ($this->questions as $question) {
00264             if ($sumofcovariancewithoverallmark) {
00265                 if ($question->_stats->negcovar) {
00266                     $question->_stats->effectiveweight = null;
00267                 } else {
00268                     $question->_stats->effectiveweight = 100 *
00269                             sqrt($question->_stats->covariancewithoverallmark) /
00270                             $sumofcovariancewithoverallmark;
00271                 }
00272             } else {
00273                 $question->_stats->effectiveweight = null;
00274             }
00275         }
00276     }
00277 
00286     protected function initial_steps_walker($step, $stats, $positionstat = true) {
00287         $stats->s++;
00288         $stats->totalmarks += $step->mark;
00289         $stats->markarray[] = $step->mark;
00290 
00291         if ($positionstat) {
00292             $stats->totalothermarks += $step->sumgrades - $step->mark;
00293             $stats->othermarksarray[] = $step->sumgrades - $step->mark;
00294 
00295         } else {
00296             $stats->totalothermarks += $step->sumgrades;
00297             $stats->othermarksarray[] = $step->sumgrades;
00298         }
00299     }
00300 
00307     protected function initial_question_walker($stats) {
00308         $stats->markaverage = $stats->totalmarks / $stats->s;
00309 
00310         if ($stats->maxmark != 0) {
00311             $stats->facility = $stats->markaverage / $stats->maxmark;
00312         } else {
00313             $stats->facility = null;
00314         }
00315 
00316         $stats->othermarkaverage = $stats->totalothermarks / $stats->s;
00317 
00318         sort($stats->markarray, SORT_NUMERIC);
00319         sort($stats->othermarksarray, SORT_NUMERIC);
00320     }
00321 
00330     protected function secondary_steps_walker($step, $stats) {
00331         $markdifference = $step->mark - $stats->markaverage;
00332         if ($stats->subquestion) {
00333             $othermarkdifference = $step->sumgrades - $stats->othermarkaverage;
00334         } else {
00335             $othermarkdifference = $step->sumgrades - $step->mark -
00336                     $stats->othermarkaverage;
00337         }
00338         $overallmarkdifference = $step->sumgrades - $this->summarksavg;
00339 
00340         $sortedmarkdifference = array_shift($stats->markarray) - $stats->markaverage;
00341         $sortedothermarkdifference = array_shift($stats->othermarksarray) -
00342                 $stats->othermarkaverage;
00343 
00344         $stats->markvariancesum += pow($markdifference, 2);
00345         $stats->othermarkvariancesum += pow($othermarkdifference, 2);
00346         $stats->covariancesum += $markdifference * $othermarkdifference;
00347         $stats->covariancemaxsum += $sortedmarkdifference * $sortedothermarkdifference;
00348         $stats->covariancewithoverallmarksum += $markdifference * $overallmarkdifference;
00349     }
00350 
00356     protected function secondary_question_walker($stats) {
00357         if ($stats->s > 1) {
00358             $stats->markvariance = $stats->markvariancesum / ($stats->s - 1);
00359             $stats->othermarkvariance = $stats->othermarkvariancesum / ($stats->s - 1);
00360             $stats->covariance = $stats->covariancesum / ($stats->s - 1);
00361             $stats->covariancemax = $stats->covariancemaxsum / ($stats->s - 1);
00362             $stats->covariancewithoverallmark = $stats->covariancewithoverallmarksum /
00363                     ($stats->s - 1);
00364             $stats->sd = sqrt($stats->markvariancesum / ($stats->s - 1));
00365 
00366         } else {
00367             $stats->markvariance = null;
00368             $stats->othermarkvariance = null;
00369             $stats->covariance = null;
00370             $stats->covariancemax = null;
00371             $stats->covariancewithoverallmark = null;
00372             $stats->sd = null;
00373         }
00374 
00375         if ($stats->markvariance * $stats->othermarkvariance) {
00376             $stats->discriminationindex = 100 * $stats->covariance /
00377                     sqrt($stats->markvariance * $stats->othermarkvariance);
00378         } else {
00379             $stats->discriminationindex = null;
00380         }
00381 
00382         if ($stats->covariancemax) {
00383             $stats->discriminativeefficiency = 100 * $stats->covariance /
00384                     $stats->covariancemax;
00385         } else {
00386             $stats->discriminativeefficiency = null;
00387         }
00388     }
00389 
00394     protected function get_random_guess_score($questiondata) {
00395         return question_bank::get_qtype(
00396                 $questiondata->qtype, false)->get_random_guess_score($questiondata);
00397     }
00398 
00403     public function get_sum_of_mark_variance() {
00404         return $this->sumofmarkvariance;
00405     }
00406 }
 All Data Structures Namespaces Files Functions Variables Enumerations