Completed
Push — master ( 8d6b38...b6a560 )
by Pierce
01:50
created

Quartile::belongsIn()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 6
nc 4
nop 3
1
<?php
2
3
namespace PierceMcGeough\phpquartiles;
4
5
class Quartile
6
{
7
    private $scores;
8
9
    private $quartiles;
10
11
    /**
12
     * Create a new Quartiles Instance
13
     * @param array $scores
14
     */
15
    public function __construct(array $scores)
16
    {
17
        $this->scores = $scores;
18
19
        if (!$this->arrayOnlyContainsNumbers()) {
20
            throw new \Exception('Scores can only contain numbers');
21
        }
22
23
        $this->calculateQuartiles();
24
    }
25
26
    /**
27
     * Calculate the quartiles
28
     */
29
    private function calculateQuartiles()
30
    {
31
        if (count($this->scores)+1 <= 3) {
32
            return;
33
        }
34
35
        sort($this->scores, SORT_NUMERIC);
36
37
        $this->quartiles = [
38
            'q1' => $this->getQuartile(0.25),
39
            'q2' => $this->getQuartile(0.50),
40
            'q3' => $this->getQuartile(0.75)
41
        ];
42
    }
43
44
    /**
45
     * Get the quartiles
46
     *
47
     * @return array|void
48
     */
49
    public function getAllQuartiles()
50
    {
51
        return $this->quartiles;
52
    }
53
54
    /**
55
     * @return numeric
56
     */
57
    public function getFirstQuartile()
58
    {
59
        return $this->getQuartile(0.25);
60
    }
61
62
    /**
63
     * @return numeric
64
     */
65
    public function getMedianQuartile()
66
    {
67
        return $this->getQuartile(0.50);
68
    }
69
70
    /**
71
     * @return numeric
72
     */
73
    public function getSecondQuartile()
74
    {
75
        return $this->getMedianQuartile();
76
    }
77
78
    /**
79
     * @return numeric
80
     */
81
    public function getThirdQuartile()
82
    {
83
        return $this->getQuartile(0.75);
84
    }
85
86
    /**
87
     * Use the params to work out the quartile specific to this $array
88
     *
89
     * @param double $quartilePlace
90
     * @return float
91
     */
92
    public function getQuartile($quartilePlace)
93
    {
94
        $pos = (count($this->scores) + 1) * $quartilePlace;
95
96
        if (fmod($pos, 1) == 0) {
97
            return $this->scores[$pos-1];
98
        }
99
100
        $fraction = $pos - floor($pos);
101
102
        $lower_num = $this->scores[floor($pos) - 1];
103
        $upper_num = $this->scores[ceil($pos) - 1];
104
105
        $difference = $upper_num - $lower_num;
106
107
        return round($lower_num + ($difference * $fraction), 2);
108
    }
109
110
    public function getPlacement($value)
111
    {
112
        $belongsIn = [
113
            'LOWEST_QUARTILE' => $value <= $this->quartiles['q1'],
114
            'SECOND_QUARTILE' => $this->belongsIn('q1', 'q2', $value),
115
            'THIRD_QUARTILE' => $this->belongsIn('q2', 'q3', $value),
116
            'HIGHEST_QUARTILE' => $value > $this->quartiles['q3'],
117
        ];
118
119
        if ($belongsIn['SECOND_QUARTILE'] && $belongsIn['THIRD_QUARTILE'] && $value >= $this->quartiles['q3']) {
120
            $belongsIn['HIGHEST_QUARTILE'] = true;
121
        }
122
123
        return $this->extractBelongsIn($belongsIn);
124
    }
125
126
    public function getPlacementInverse()
127
    {
128
129
    }
130
131
    /**
132
     * Check if $value belongs in a quartile
133
     *
134
     * @param array $quartiles
0 ignored issues
show
Bug introduced by
There is no parameter named $quartiles. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
135
     * @param string $q1
136
     * @param string $q2
137
     * @param float $value
138
     *
139
     * @return boolean
140
     */
141
    function belongsIn($q1, $q2, $value)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
142
    {
143
        if ($this->quartiles[$q1] == $this->quartiles[$q2]) { // Spans multiples
144
            return ($value >= $this->quartiles[$q1] &&  $value <= $this->quartiles[$q2]);
145
        } else {
146
            return ($value > $this->quartiles[$q1] &&  $value <= $this->quartiles[$q2]);
147
        }
148
149
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
150
    }
151
152
    /**
153
     * Find the first TRUE value
154
     *
155
     * @param array $belongsIn
156
     *
157
     * @return string
158
     */
159
    function extractBelongsIn($belongsIn)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
160
    {
161
        // Find the first TRUE value from bottom to top (hence array_reverse)
162
        foreach(array_reverse($belongsIn) as $placement => $active) {
163
            if ($active) {
164
                return $placement;
165
            }
166
        }
167
168
        return 'NONE';
169
    }
170
171
    /**
172
     * Validate that the scores array only contains numbers
173
     *
174
     * @return boolean
175
     */
176
    private function arrayOnlyContainsNumbers()
177
    {
178
        foreach ($this->scores as $score) {
179
            if (!is_numeric($score)) {
180
                return false;
181
            }
182
        }
183
184
        return true;
185
    }
186
}
187