MDL-14204 "Content for Quiz Statistics report table - Random_guess_score" added method random_guess_score to question type objects and a new function in questionlib that calls the question type method.

This commit is contained in:
jamiesensei 2008-06-16 13:29:00 +00:00
parent ed48af75b4
commit 6f51ed72be
12 changed files with 201 additions and 96 deletions

View file

@ -32,4 +32,5 @@ $string['questionnumber'] = 'Q#';
$string['quizstructureanalysis'] = 'Quiz structure analysis'; $string['quizstructureanalysis'] = 'Quiz structure analysis';
$string['questiontype'] = 'Q type'; $string['questiontype'] = 'Q type';
$string['intended_weight'] = 'Intended question weight'; $string['intended_weight'] = 'Intended question weight';
$string['random_guess_score'] = 'Random guess score';
?> ?>

View file

@ -1794,8 +1794,16 @@ function get_question_fraction_grade($question, $state) {
$r = $QTYPES[$question->qtype]->get_fractional_grade($question, $state); $r = $QTYPES[$question->qtype]->get_fractional_grade($question, $state);
return $r; return $r;
} }
/**
* @return integer grade out of 1 that a random guess by a student might score.
*/
// ULPGc ecastro
function get_random_guess_score($question) {
global $QTYPES;
$r = $QTYPES[$question->qtype]->get_random_guess_score($question);
return $r;
}
/// CATEGORY FUNCTIONS ///////////////////////////////////////////////////////////////// /// CATEGORY FUNCTIONS /////////////////////////////////////////////////////////////////
/** /**

View file

@ -8,7 +8,6 @@
* @package quiz * @package quiz
*//** */ *//** */
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php'); require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php');
require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php'); require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php');
@ -55,7 +54,11 @@ class quiz_report extends quiz_default_report {
$allowedlist = $groupstudentslist; $allowedlist = $groupstudentslist;
} }
$questions = question_load_questions(quiz_questions_in_quiz($quiz->questions)); $questions = quiz_report_load_questions($quiz);
// Load the question type specific information
if (!get_question_options($questions)) {
print_error('cannotloadquestion', 'question');
}
$table = new quiz_report_statistics_table(); $table = new quiz_report_statistics_table();
$table->is_downloading($download, get_string('reportstatistics','quiz_statistics'), $table->is_downloading($download, get_string('reportstatistics','quiz_statistics'),
@ -170,97 +173,105 @@ class quiz_report extends quiz_default_report {
$median += array_shift($mediangrades); $median += array_shift($mediangrades);
$median = $median /2; $median = $median /2;
} }
//fetch sum of squared, cubed and power 4d
//differences between grades and mean grade
$mean = $usingattempts->total / $usingattempts->countrecs;
$sql = "SELECT " .
"SUM(POWER((qa.sumgrades - ?),2)) AS power2, " .
"SUM(POWER((qa.sumgrades - ?),3)) AS power3, ".
"SUM(POWER((qa.sumgrades - ?),4)) AS power4 ".
'FROM ' .$fromqa.
'WHERE ' .$whereqa.
$usingattempts->sql;
$params = array($mean, $mean, $mean, $quiz->id);
if (!$powers = $DB->get_record_sql($sql, $params)){
print_error('errorpowers', 'quiz_statistics');
}
$s = $usingattempts->countrecs; $s = $usingattempts->countrecs;
if ($s>1){
//Standard_Deviation $quizattsstatistics = new object();
//see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation $quizattsstatistics->align = array('center', 'center');
$quizattsstatistics->width = '60%';
$sd = sqrt($powers->power2 / ($s -1)); $quizattsstatistics->class = 'generaltable titlesleft';
$quizattsstatistics->data = array();
//Skewness_and_Kurtosis $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz));
//see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis //fetch sum of squared, cubed and power 4d
$m2= $powers->power2 / $s; //differences between grades and mean grade
$m3= $powers->power3 / $s; $mean = $usingattempts->total / $usingattempts->countrecs;
$m4= $powers->power4 / $s; $sql = "SELECT " .
"SUM(POWER((qa.sumgrades - ?),2)) AS power2, " .
$k2= $s*$m2/($s-1); "SUM(POWER((qa.sumgrades - ?),3)) AS power3, ".
$k3= $s*$s*$m3/(($s-1)*($s-2)); "SUM(POWER((qa.sumgrades - ?),4)) AS power4 ".
$k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2)); 'FROM ' .$fromqa.
$skewness = $k3 / (pow($k2, 2/3));
$kurtosis = $k4 / ($k2*$k2);
$quizattsstatistics = new object();
$quizattsstatistics->align = array('center', 'center');
$quizattsstatistics->width = '60%';
$quizattsstatistics->class = 'generaltable titlesleft';
$quizattsstatistics->data = array();
$quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz));
$quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz));
$quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness);
$quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis);
//CIC, ER and SE.
//http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE
$qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " .
"{question_sessions} qns, " .
"{question_states} qs, " .
"{question} q, " .
$fromqa.' '.
'WHERE ' .$whereqa. 'WHERE ' .$whereqa.
'AND qns.attemptid = qa.uniqueid '. $usingattempts->sql;
'AND qs.question = q.id ' . $params = array($mean, $mean, $mean, $quiz->id);
'AND q.length > 0 '. if (!$powers = $DB->get_record_sql($sql, $params)){
$usingattempts->sql. print_error('errorpowers', 'quiz_statistics');
'AND qns.newgraded = qs.id GROUP BY qs.question';
$qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, array($quiz->id));
$sum = 0;
$sql = 'SELECT ' .
'SUM(POWER((qs.grade - ?),2)) AS power2 ' .
'FROM ' .
'{question_sessions} qns, ' .
'{question_states} qs, ' .
'{question} q, ' .
$fromqa.' '.
'WHERE ' .$whereqa.
'AND qns.attemptid = qa.uniqueid '.
'AND qs.question = ? ' .
$usingattempts->sql.
'AND qns.newgraded = qs.id';
foreach ($qgradeavgs as $qid => $qgradeavg){
$params = array($qgradeavg, $quiz->id, $qid);
$power = $DB->get_field_sql($sql, $params);
if ($power === false){
print_error('errorpowerquestions', 'quiz_statistics');
} }
$sum += $power;
//Standard_Deviation
//see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation
$sd = sqrt($powers->power2 / ($s -1));
$quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz));
//Skewness_and_Kurtosis
if ($s>2){
//see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis
$m2= $powers->power2 / $s;
$m3= $powers->power3 / $s;
$m4= $powers->power4 / $s;
$k2= $s*$m2/($s-1);
$k3= $s*$s*$m3/(($s-1)*($s-2));
$skewness = $k3 / (pow($k2, 2/3));
$quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness);
}
if ($s>3){
$k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2));
$kurtosis = $k4 / ($k2*$k2);
$quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis);
}
//CIC, ER and SE.
//http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE
$qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " .
"{question_sessions} qns, " .
"{question_states} qs, " .
"{question} q, " .
$fromqa.' '.
'WHERE ' .$whereqa.
'AND qns.attemptid = qa.uniqueid '.
'AND qs.question = q.id ' .
'AND q.length > 0 '.
$usingattempts->sql.
'AND qns.newgraded = qs.id GROUP BY qs.question';
$qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, array($quiz->id));
$sum = 0;
$sql = 'SELECT ' .
'SUM(POWER((qs.grade - ?),2)) AS power2 ' .
'FROM ' .
'{question_sessions} qns, ' .
'{question_states} qs, ' .
'{question} q, ' .
$fromqa.' '.
'WHERE ' .$whereqa.
'AND qns.attemptid = qa.uniqueid '.
'AND qs.question = ? ' .
$usingattempts->sql.
'AND qns.newgraded = qs.id';
foreach ($qgradeavgs as $qid => $qgradeavg){
$params = array($qgradeavg, $quiz->id, $qid);
$power = $DB->get_field_sql($sql, $params);
if ($power === false){
print_error('errorpowerquestions', 'quiz_statistics');
}
$sum += $power;
}
$sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1);
$p = count($qgradeavgs);//no of positions
$cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2));
$quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %');
$errorratio = 100 * sqrt(1-($cic/100));
$quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %');
$standarderror = ($errorratio * $sd / 100);
$quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'),
quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz));
print_table($quizattsstatistics);
} }
$sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1);
$p = count($qgradeavgs);//no of positions
$cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2));
$quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %');
$errorratio = 100 * sqrt(1-($cic/100));
$quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %');
$standarderror = ($errorratio * $sd / 100);
$quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'),
quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz));
print_table($quizattsstatistics);
} }
if (!$table->is_downloading()){ if (!$table->is_downloading()){

View file

@ -1,4 +1,5 @@
<?php // $Id$ <?php // $Id$
require_once($CFG->libdir.'/tablelib.php');
class quiz_report_statistics_table extends flexible_table { class quiz_report_statistics_table extends flexible_table {
@ -37,6 +38,9 @@ class quiz_report_statistics_table extends flexible_table {
$columns[]= 'intended_weight'; $columns[]= 'intended_weight';
$headers[]= get_string('intended_weight', 'quiz_statistics'); $headers[]= get_string('intended_weight', 'quiz_statistics');
$columns[]= 'random_guess_score';
$headers[]= get_string('random_guess_score', 'quiz_statistics');
$this->define_columns($columns); $this->define_columns($columns);
$this->define_headers($headers); $this->define_headers($headers);
$this->sortable(false); $this->sortable(false);
@ -59,6 +63,7 @@ class quiz_report_statistics_table extends flexible_table {
$this->column_class('sumgrades', 'bold');*/ $this->column_class('sumgrades', 'bold');*/
$this->column_class('intended_weight', 'numcol'); $this->column_class('intended_weight', 'numcol');
$this->column_class('random_guess_score', 'numcol');
$this->set_attribute('id', 'questionstatistics'); $this->set_attribute('id', 'questionstatistics');
$this->set_attribute('class', 'generaltable generalbox boxaligncenter'); $this->set_attribute('class', 'generaltable generalbox boxaligncenter');
@ -89,6 +94,10 @@ class quiz_report_statistics_table extends flexible_table {
return quiz_report_scale_sumgrades_as_percentage($question->grade, $this->quiz); return quiz_report_scale_sumgrades_as_percentage($question->grade, $this->quiz);
} }
function col_random_guess_score($question){
return number_format(get_random_guess_score($question) * 100, 2).' %';
}
} }
?> ?>

View file

@ -426,6 +426,16 @@ class question_match_qtype extends default_questiontype {
return substr(implode(', ', $this->get_actual_response($question, $state)), 0, $length); return substr(implode(', ', $this->get_actual_response($question, $state)), 0, $length);
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
return 1 / count($question->options->subquestions);
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*

View file

@ -515,6 +515,19 @@ class embedded_cloze_qtype extends default_questiontype {
return $responses; return $responses;
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
$totalfraction = 0;
foreach (array_keys($question->options->questions) as $key){
$totalfraction += get_random_guess_score($question->options->questions[$key]);
}
return $totalfraction / count($question->options->questions);
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*

View file

@ -396,7 +396,18 @@ class question_multichoice_qtype extends default_questiontype {
function response_summary($question, $state, $length = 80) { function response_summary($question, $state, $length = 80) {
return implode(',', $this->get_actual_response($question, $state)); return implode(',', $this->get_actual_response($question, $state));
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
$totalfraction = 0;
foreach ($question->options->answers as $answer){
$totalfraction += $answer->fraction;
}
return $totalfraction / count($question->options->answers);
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*

View file

@ -675,6 +675,14 @@ class default_questiontype {
} }
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
return 0;
}
/** /**
* Return the actual response to the question in a given state * Return the actual response to the question in a given state
* for the question * for the question

View file

@ -101,7 +101,7 @@ class random_qtype extends default_questiontype {
AND parent = '0' AND parent = '0'
AND hidden = '0' AND hidden = '0'
AND id NOT IN ($cmoptions->questionsinuse) AND id NOT IN ($cmoptions->questionsinuse)
AND qtype NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)", '', 'id')) { AND qtype NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)", array(), '', 'id')) {
$this->catrandoms[$question->category][$question->questiontext] = $this->catrandoms[$question->category][$question->questiontext] =
draw_rand_array($catrandoms, count($catrandoms)); draw_rand_array($catrandoms, count($catrandoms));
} else { } else {

View file

@ -272,7 +272,20 @@ class question_randomsamatch_qtype extends question_match_qtype {
$result->responses = $answers; $result->responses = $answers;
return $result; return $result;
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
//Effectively $subquestions multi choice questions with equal weighting
//assuming a student has the intelligence to not select the same answer twice
//there is in each subquestion factorial($subquestions-1) chance of getting
//the answer right. There are factorial($subquestions) possible combinations of
//answers and it works out to an average grade of 1/$subquestions.
$subquestions = count($question->options->subquestions);
return 1/$subquestions;
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*

View file

@ -239,7 +239,20 @@ class question_shortanswer_qtype extends default_questiontype {
} }
return $response; return $response;
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
$answers = &$question->options->answers;
foreach($answers as $aid => $answer) {
if ('*' == trim($answer->answer)){
return $answer->fraction;
}
}
return 0;
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*

View file

@ -245,6 +245,14 @@ class question_truefalse_qtype extends default_questiontype {
} }
return $responses; return $responses;
} }
/**
* @param object $question
* @return integer a score out of 1 that the average random guess by a
* student might give.
*/
function get_random_guess_score($question) {
return 0.5;
}
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////