--- loncom/interface/statistics/lonproblemanalysis.pm 2004/02/20 16:24:20 1.72
+++ loncom/interface/statistics/lonproblemanalysis.pm 2004/03/12 20:29:48 1.78
@@ -1,6 +1,6 @@
# The LearningOnline Network with CAPA
#
-# $Id: lonproblemanalysis.pm,v 1.72 2004/02/20 16:24:20 matthew Exp $
+# $Id: lonproblemanalysis.pm,v 1.78 2004/03/12 20:29:48 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -161,11 +161,11 @@ sub BuildProblemAnalysisPage {
\@Students);
} elsif ($current_problem->{'resptype'} eq 'numerical') {
# if (exists($ENV{'form.ExcelOutput'})) {
- &Apache::lonstudentsubmissions::prepare_excel_output
- ($r,$current_problem,$ProblemData,\@Students);
+# &Apache::lonstudentsubmissions::prepare_excel_output
+# ($r,$current_problem,$ProblemData,\@Students);
# } else {
-# &NumericalResponseAnalysis($r,$current_problem,
-# $ProblemData,\@Students);
+ &NumericalResponseAnalysis($r,$current_problem,
+ $ProblemData,\@Students);
# }
} else {
$r->print('
This analysis is not supported
');
@@ -192,7 +192,6 @@ sub BuildProblemAnalysisPage {
#########################################################
sub NumericalResponseAnalysis {
my ($r,$problem,$ProblemData,$Students) = @_;
- $r->print('This analysis is not yet supported
');
my ($resource,$respid) = ($problem->{'resource'},
$problem->{'respid'});
my $analysis_html;
@@ -207,40 +206,110 @@ sub NumericalResponseAnalysis {
$r->print($analysis_html);
return;
}
+ #
+ # This next call causes all the waiting around that people complain about
my ($max,$min) = &GetStudentAnswers($r,$problem,$Students);
- $r->print('Maximum = '.$max.' Minimum = '.$min);
- my $max_students = 0;
+ #
+ # Collate the data
my %Data;
foreach my $student (@$Students) {
my $answer = $student->{'answer'};
$Data{$answer}++;
- if ($max_students < $Data{$answer}) {
- $max_students = $Data{$answer};
- }
- }
- foreach (5,10,20,25,50,75,100,150,200,250,500,1000,1500,2000,2500,5000) {
- if ($max_students < $_) {
- $max_students = $_;
- last;
- }
}
my @Labels = sort {$a <=> $b } keys(%Data);
- $r->print('number of labels = '.scalar(@Labels));
my @PlotData = @Data{@Labels};
- $r->print('number of PlotData = '.scalar(@PlotData));
- my $graph =
- &Apache::loncommon::DrawXYGraph('Correct Answer Distribution',
- 'Correct Answer',
- 'Number of students',
- $max_students,
- undef,
- \@Labels,
- [\@PlotData],
- (xskip=>10));
- $r->print($graph);
+ #
+ my $width = 500;
+ my $height = 100;
+ my $plot = &one_dimensional_plot($r,500,100,scalar(@$Students),
+ \@Labels,\@PlotData);
+ $r->print($plot);
return;
}
+sub one_dimensional_plot {
+ my ($r,$width,$height,$N,$Labels,$Data)=@_;
+ #
+ # Compute data -> image scaling factors
+ my $min = $Labels->[0];
+ my $max = $Labels->[-1];
+ my $h_scale = ($width-10)/($max-$min);
+ #
+ my $max_y = 0;
+ foreach (@$Data) {
+ $max_y = $_ if ($max_y < $_);
+ }
+ my $ticscale = 5;
+ if ($max_y * $ticscale > $height/2) {
+ $ticscale = int($height/2/$max_y);
+ $ticscale = 1 if ($ticscale < 1);
+ }
+ #
+ # Create the plot
+ my $plot =
+ qq{};
+ for (my $idx=0;$idx[$idx] - $min);
+ my $top = $height/2-$Data->[$idx]*$ticscale;
+ my $bottom = $height/2+$Data->[$idx]*$ticscale;
+ $plot .=
+ &line($xloc,$top,$xloc,$bottom,'888888',1);
+ }
+ #
+ # Put the scale on last to ensure it is on top of the data.
+ if ($min < 0 && $max > 0) {
+ my $circle_x = 5+$h_scale*abs($min); # '0' in data coordinates
+ my $r = 4;
+ $plot .= &line(5,$height/2,$circle_x-$r,$height/2,'000000',1);
+ $plot .= &circle($circle_x,$height/2,$r+1,'000000');
+ $plot .= &line($circle_x+$r,$height/2,$width-5,$height/2,'000000',1);
+ } else {
+ $plot .= &line(5,$height/2,$width-5,$height/2,'000000',1);
+ }
+ $plot .= '';
+ my $plotresult = &Apache::lonxml::xmlparse($r,'web',$plot);
+
+ my $title = 'Distribution of correct answers';
+ my $result = ''.
+ ''.
+ ''.$title.' (N='.$N.')'.
+ ''.
+ ' |
'.
+ ''.
+ ''.$min.' | '.
+ ''.$plotresult.' | '.
+ ''.$max.' | '.
+ '
'.
+ ''.
+ 'Maximum Number of Coinciding Values: '.$max_y.
+ ' |
'.
+ '
';
+ return $result;
+}
+
+##
+## Helper subroutines for .
+## These should probably go somewhere more suitable soon.
+sub line {
+ my ($x1,$y1,$x2,$y2,$color,$thickness) = @_;
+ return qq{$/};
+}
+
+sub rectangle {
+ my ($x1,$y1,$x2,$y2,$color,$thickness,$filled) = @_;
+ return qq{};
+}
+
+sub arc {
+ my ($x,$y,$width,$height,$start,$end,$color,$thickness,$filled)=@_;
+ return qq{};
+}
+
+sub circle {
+ my ($x,$y,$radius,$color,$thickness,$filled)=@_;
+ return &arc($x,$y,$radius,$radius,0,360,$color,$thickness,$filled);
+}
+
sub GetStudentAnswers {
my ($r,$problem,$Students) = @_;
my %Answers;
@@ -541,7 +610,8 @@ sub OR_tries_analysis {
my $mintries = 1;
my $maxtries = $ENV{'form.NumPlots'};
my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
- if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) {
+ if (! defined($Concepts) ||
+ ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils'))) {
$table = ''.
&mt('Not enough data for concept analysis. '.
'Performing Foil Analysis').
@@ -552,7 +622,7 @@ sub OR_tries_analysis {
$mintries,$maxtries);
my $analysis = '';
if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
- $analysis = &OR_Tries_Foil_Analysis($mintries,$maxtries,$Foils,
+ $analysis = &OR_Tries_Foil_Analysis($mintries,$maxtries,$Concepts,
\%ResponseData,$ORdata);
} else {
$analysis = &OR_Tries_Concept_Analysis($mintries,$maxtries,
@@ -563,82 +633,100 @@ sub OR_tries_analysis {
}
sub OR_Tries_Foil_Analysis {
- my ($mintries,$maxtries,$Foils,$respdat,$ORdata) = @_;
+ my ($mintries,$maxtries,$Concepts,$respdat,$ORdata) = @_;
my %ResponseData = %$respdat;
#
# Compute the data neccessary to make the plots
my @PlotData;
- foreach my $foilid (@$Foils) {
- for (my $i=$mintries;$i<=$maxtries;$i++) {
- if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
- push(@{$PlotData[$i]->{'_correct'}},0);
- } else {
- push(@{$PlotData[$i]->{'_correct'}},
- 100*$ResponseData{$foilid}->[$i]->{'_correct'}/
- $ResponseData{$foilid}->[$i]->{'_total'});
- }
- foreach my $option (@{$ORdata->{'_Options'}}) {
- push(@{$PlotData[$i]->{'_total'}},
- $ResponseData{$foilid}->[$i]->{'_total'});
- if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
- push (@{$PlotData[$i]->{$option}},0);
+ foreach my $concept (@$Concepts) {
+ foreach my $foilid (@{$concept->{'foils'}}) {
+ for (my $try=$mintries;$try<=$maxtries;$try++) {
+ if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) {
+ push(@{$PlotData[$try]->{'_correct'}},0);
} else {
- if ($ResponseData{$foilid}->[$i]->{'_total'} ==
- $ResponseData{$foilid}->[$i]->{'_correct'}) {
- push(@{$PlotData[$i]->{$option}},0);
+ push(@{$PlotData[$try]->{'_correct'}},
+ 100*$ResponseData{$foilid}->[$try]->{'_correct'}/
+ $ResponseData{$foilid}->[$try]->{'_total'});
+ }
+ foreach my $option (@{$ORdata->{'_Options'}}) {
+ push(@{$PlotData[$try]->{'_total'}},
+ $ResponseData{$foilid}->[$try]->{'_total'});
+ if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) {
+ push (@{$PlotData[$try]->{$option}},0);
} else {
- push (@{$PlotData[$i]->{$option}},
- 100 * $ResponseData{$foilid}->[$i]->{$option} /
- ($ResponseData{$foilid}->[$i]->{'_total'} -
- $ResponseData{$foilid}->[$i]->{'_correct'}));
+ if ($ResponseData{$foilid}->[$try]->{'_total'} ==
+ $ResponseData{$foilid}->[$try]->{'_correct'}) {
+ push(@{$PlotData[$try]->{$option}},0);
+ } else {
+ push (@{$PlotData[$try]->{$option}},
+ 100 *
+ $ResponseData{$foilid}->[$try]->{$option} /
+ ($ResponseData{$foilid}->[$try]->{'_total'}
+ -
+ $ResponseData{$foilid}->[$try]->{'_correct'}
+ ));
+ }
}
- }
+ } # End of foreach my $option
}
- }
- }
+ } # End of foreach my $foilid
+ } # End of foreach my $concept
#
# Build a table for the plots
my $analysis_html = "
\n";
- my $foilkey = &build_option_index($ORdata);
- for (my $i=$mintries;$i<=$maxtries;$i++) {
- my $count = $ResponseData{'_total'}->[$i];
- if ($count == 0) {
- $count = 'no submissions';
- } elsif ($count == 1) {
- $count = '1 submission';
- } else {
- $count = $count.' submissions';
- }
- my $title = 'Attempt '.$i.', '.$count;
+ my $optionkey = &build_option_index($ORdata);
+ for (my $try=$mintries;$try<=$maxtries;$try++) {
+ my $count = $ResponseData{'_total'}->[$try];
+ my $title = 'Submission '.$try.' (N='.$count.')';
my @Datasets;
foreach my $option ('_correct',@{$ORdata->{'_Options'}}) {
- next if (! exists($PlotData[$i]->{$option}));
- push(@Datasets,$PlotData[$i]->{$option});
+ next if (! exists($PlotData[$try]->{$option}));
+ push(@Datasets,$PlotData[$try]->{$option});
}
+ #
+ # Put a blank in the data set between concepts
+ for (my $set =0;$set<=$#Datasets;$set++) {
+ my @Data = @{$Datasets[$set]};
+ my $idx = 0;
+ foreach my $concept (@{$Concepts}) {
+ foreach my $foilid (@{$concept->{'foils'}}) {
+ $Datasets[$set]->[$idx++]=shift(@Data);
+ }
+ if ($concept->{'name'} ne $Concepts->[-1]->{'name'}) {
+ $Datasets[$set]->[$idx++] = 0;
+ }
+ }
+ }
+ #
+ # Set up the labels needed for the bar graph
+ my @Labels;
+ my $idx = 1;
+ foreach my $concept (@{$Concepts}) {
+ foreach my $foilid (@{$concept->{'foils'}}) {
+ push(@Labels,$idx++);
+ }
+ push(@Labels,'');
+ }
+ #
my $correctgraph = &Apache::loncommon::DrawBarGraph
($title,'Foil Number','Percent Correct',
- 100,$plotcolors,undef,$Datasets[0]);
+ 100,$plotcolors,\@Labels,$Datasets[0]);
$analysis_html.= ''.$correctgraph.' | ';
- ##
- ##
+
+ #
+ #
next if (! defined($Datasets[0]));
for (my $i=0; $i< scalar(@{$Datasets[0]});$i++) {
$Datasets[0]->[$i]=0;
}
- $count = $ResponseData{'_total'}->[$i]-$ResponseData{'_correct'}->[$i];
- if ($count == 0) {
- $count = 'no submissions';
- } elsif ($count == 1) {
- $count = '1 submission';
- } else {
- $count = $count.' submissions';
- }
- $title = 'Attempt '.$i.', '.$count;
+ $count = $ResponseData{'_total'}->[$try] -
+ $ResponseData{'_correct'}->[$try];
+ $title = 'Submission '.$try.' (N='.$count.')';
my $incorrectgraph = &Apache::loncommon::DrawBarGraph
($title,'Foil Number','% Option Chosen Incorrectly',
- 100,$plotcolors,undef,@Datasets);
+ 100,$plotcolors,\@Labels,@Datasets);
$analysis_html.= ''.$incorrectgraph.' | ';
- $analysis_html.= ''.$foilkey." | |
\n";
+ $analysis_html.= ''.$optionkey." | \n";
}
$analysis_html .= " |
\n";
return $analysis_html;