MDL-64320 analytics: Validate discrete indicators separately
[moodle.git] / analytics / classes / local / indicator / discrete.php
CommitLineData
369389c9
DM
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Abstract discrete indicator.
19 *
20 * @package core_analytics
21 * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace core_analytics\local\indicator;
26
27defined('MOODLE_INTERNAL') || die();
28
29/**
30 * Abstract discrete indicator.
31 *
32 * @package core_analytics
33 * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36abstract class discrete extends base {
37
38 /**
39 * Classes need to be defined so they can be converted internally to individual dataset features.
40 *
41 * @return string[]
42 */
43 protected static function get_classes() {
44 throw new \coding_exception('Please overwrite get_classes() specifying your discrete-values\' indicator classes');
45 }
46
413f19bc
DM
47 /**
48 * Returns 1 feature header for each of the classes.
49 *
50 * @return string[]
51 */
369389c9 52 public static function get_feature_headers() {
3a396286 53 $fullclassname = '\\' . get_called_class();
369389c9 54
e0c88997 55 foreach (static::get_classes() as $class) {
369389c9
DM
56 $headers[] = $fullclassname . '/' . $class;
57 }
58
59 return $headers;
60 }
61
413f19bc
DM
62 /**
63 * Whether the value should be displayed or not.
64 *
65 * @param float $value
66 * @param string $subtype
67 * @return bool
68 */
369389c9
DM
69 public function should_be_displayed($value, $subtype) {
70 if ($value != static::get_max_value()) {
71 // Discrete values indicators are converted internally to 1 feature per indicator, we are only interested
72 // in showing the feature flagged with the max value.
73 return false;
74 }
75 return true;
76 }
77
78 /**
79 * Returns the value to display when the prediction is $value.
80 *
27ae9af4 81 * @param float $value
369389c9 82 * @param string $subtype
27ae9af4 83 * @return string
369389c9 84 */
27ae9af4 85 public function get_display_value($value, $subtype = false) {
369389c9 86
5c5cb3ee 87 $displayvalue = array_search($subtype, static::get_classes(), false);
369389c9
DM
88
89 debugging('Please overwrite \core_analytics\local\indicator\discrete::get_display_value to show something ' .
90 'different than the default "' . $displayvalue . '"', DEBUG_DEVELOPER);
91
92 return $displayvalue;
93 }
94
27ae9af4
DM
95 /**
96 * get_display_style
97 *
98 * @param float $ignoredvalue
99 * @param string $ignoredsubtype
100 * @return int
101 */
102 public function get_display_style($ignoredvalue, $ignoredsubtype) {
369389c9
DM
103 // No style attached to indicators classes, they are what they are, a cat,
104 // a horse or a sandwich, they are not good or bad.
27ae9af4 105 return \core_analytics\calculable::OUTCOME_NEUTRAL;
369389c9
DM
106 }
107
413f19bc
DM
108 /**
109 * From calculated values to dataset features.
110 *
111 * One column for each class.
112 *
113 * @param float[] $calculatedvalues
114 * @return float[]
115 */
369389c9
DM
116 protected function to_features($calculatedvalues) {
117
e0c88997 118 $classes = static::get_classes();
369389c9
DM
119
120 foreach ($calculatedvalues as $sampleid => $calculatedvalue) {
121
e0c88997
DM
122 // Using intval as it may come as a float from the db.
123 $classindex = array_search(intval($calculatedvalue), $classes, true);
369389c9 124
e0c88997
DM
125 if ($classindex === false && !is_null($calculatedvalue)) {
126 throw new \coding_exception(get_class($this) . ' calculated value "' . $calculatedvalue .
127 '" is not one of its defined classes (' . json_encode($classes) . ')');
369389c9
DM
128 }
129
130 // We transform the calculated value into multiple features, one for each of the possible classes.
131 $features = array_fill(0, count($classes), 0);
132
133 // 1 to the selected value.
e0c88997
DM
134 if (!is_null($calculatedvalue)) {
135 $features[$classindex] = 1;
136 }
369389c9
DM
137
138 $calculatedvalues[$sampleid] = $features;
139 }
140
141 return $calculatedvalues;
142 }
e0c88997
DM
143
144 /**
145 * Validates the calculated value.
146 *
147 * @param float $calculatedvalue
148 * @return true
149 */
150 protected function validate_calculated_value($calculatedvalue) {
151
152 // Using intval as it may come as a float from the db.
153 if (!in_array(intval($calculatedvalue), static::get_classes())) {
154 throw new \coding_exception(get_class($this) . ' calculated value "' . $calculatedvalue .
155 '" is not one of its defined classes (' . json_encode(static::get_classes()) . ')');
156 }
157 return true;
158 }
369389c9 159}