Class: JLDrill::Statistics

Inherits:
Object
  • Object
show all
Defined in:
lib/jldrill/model/Quiz/Statistics.rb

Overview

Statistics for a quiz session

Constant Summary collapse

MINIMUM_CONFIDENCE =
0.009
SECONDS_PER_DAY =
60 * 60 * 24

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(quiz, binNumber) ⇒ Statistics

Returns a new instance of Statistics.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 21

def initialize(quiz, binNumber)
    @quiz = quiz
    @binNumber = binNumber
    @estimate = 0
    @correct = 0
    @incorrect = 0
    @lastTen = []
    @inTargetZone = false
    @timesInTargetZone = 0
    @levels = []
    1.upto(8) do
        @levels.push(LevelStats.new)
    end
    @learned = 0
    @reviewed = 0
    @reviewRateSum = 0
    @reviewTimer = Timer.new
    @learnTimer = Timer.new
    @currentTimer = nil
    resetConfidence 
end

Instance Attribute Details

#confidenceObject (readonly)

Returns the value of attribute confidence.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def confidence
  @confidence
end

#estimateObject (readonly)

Returns the value of attribute estimate.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def estimate
  @estimate
end

#lastTenObject (readonly)

Returns the value of attribute lastTen.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def lastTen
  @lastTen
end

#learnedObject

Returns the value of attribute learned.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def learned
  @learned
end

#levelsObject (readonly)

Returns the value of attribute levels.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def levels
  @levels
end

#reviewedObject

Returns the value of attribute reviewed.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def reviewed
  @reviewed
end

#timesInTargetZoneObject (readonly)

Returns the value of attribute timesInTargetZone.



13
14
15
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 13

def timesInTargetZone
  @timesInTargetZone
end

Instance Method Details

#accuracyObject

Returns the actual % accuracy of the quiz in an integer



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 174

def accuracy
    retVal = 0
    if @incorrect == 0
        if @correct != 0
            retVal = 100
        end
    else
        retVal = ((@correct * 100) / total).to_i
    end
    retVal
end

#averageReviewRateObject



105
106
107
108
109
110
111
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 105

def averageReviewRate
    retVal = 1.0
    if @reviewed != 0
        retVal = ((@reviewRateSum * 10).round / @reviewed).to_f / 10 
    end
    return retVal
end

#calculateConfidence(wasCorrect) ⇒ Object

Calculates a Bayesian estimate (I think!) of the confidence that the items seen to this point were known to an accuracy of at least 90%.

How this is calculated:

 According to Bayes law, P(H|E) = P(E|H) * P(H) / P(E)
 Where:
    P(H|E) is the probability that the hypothesis is correct
           given the evidence.  
    P(E|H) is the probability that the evididence would be
           shown if the hypothesis were correct.
    P(H)   is the probability that the hypothesis is correct
           given any evidence.
    P(E)   is the probability that the evidence would be seen
           in all cases.

This value is calculated incrementally in the following fashion:
    P[n] = P(E|H) * P[n-1] / P(E)
i.e., the nth value of P is dependent on the n-1th value of P

In our case, I will assume that the distribution of the 
probabilities is uniform (probably wrong!) and say that
if we got the question right
     P(E|H) = 0.95 
          that is, our probability of getting it right is
          somewhere between 90 and 100%.  I assume uniform
          distribution, therefore the average is in the middle.
or if we got the question wrong
     P(E|H) = 0.05
          obviously, the probability of getting it wrong given
          the hypothesis is 1 minus the probability of getting
          it right.
     P(E) = sum(probabilities for all the hypotheses)
          = P(chance < 90) + P(chance is 90+%)
     P(chance < 90) = 0.45(1 - P[n-1])
          that is, it's equal to 1/2 the range between 0 and 90
          times the probability that hypothesis is wrong.
     P(chance > 90) = 0.95P[n-1]
          that is, it's equal to 1/2 the range between 90 and 100
          times the probability that the hypothesis is right.
          So...
     P(E) = 0.45(1 - P[n-1]) + 0.95P[n-1]

 Giving a final answer of
     P[n] = P(E|H) * P[n-1] / (0.45(1 - P[n-1]) + 0.95P[n-1])

 I fully expect this is completely wrong due to my lack of
 ability in math and statistics.  But on the other hand, I won't
 care much as long as appears from the user's point of view to
 be working.


249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 249

def calculateConfidence(wasCorrect)
    if wasCorrect
        pEH = 0.95
    else
        pEH = 0.05
    end
    pE = (0.45 * (1.0 - @confidence)) + (0.95 * @confidence)
    @confidence = (pEH * @confidence) / pE
    if @confidence < MINIMUM_CONFIDENCE
        @confidence = MINIMUM_CONFIDENCE
    end
end

#correct(item) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 131

def correct(item)
    if item.bin != @binNumber
        return
    end
    @correct += 1
    @reviewed += 1
    recordReviewRate(item)
    level = getLevel(item)
    if !level.nil?
        level.correct
    end
    record(true)
    reEstimate
    calculateConfidence(true)
end

#correct=(value) ⇒ Object



163
164
165
166
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 163

def correct=(value)
    @correct = value
    reEstimate
end

#currentReviewRateObject



113
114
115
116
117
118
119
120
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 113

def currentReviewRate
    retVal = 1.0
    if size > 0
        rate = reviewBin[0].schedule.reviewRate
       retVal = ((rate * 100).round).to_f / 100
    end
    retVal
end

#getLevel(item) ⇒ Object

Get the appropriate LevelStat object for this item



89
90
91
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 89

def getLevel(item)
    return @levels[Counter.getLevel(item)]
end

#incorrect(item) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 147

def incorrect(item)
    if item.bin != @binNumber
        return
    end
    @incorrect += 1
    @reviewed += 1
    recordReviewRate(item)
    level = getLevel(item)
    if !level.nil?
        level.incorrect
    end
    record(false)
    reEstimate
    calculateConfidence(false)
end

#incorrect=(value) ⇒ Object



168
169
170
171
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 168

def incorrect=(value)
    @incorrect = value
    reEstimate
end

#inTargetZone?Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 70

def inTargetZone?
    # Don't start the countdown until we have reviewed
    # at least 10 item.
    if @reviewed <= 10
        return false
    end
    if !@inTargetZone
        if recentAccuracy >= 90
            @inTargetZone = true
        end
    else
        if (recentAccuracy < 90) && (@confidence < 90)
            @inTargetZone = false
        end
    end
    return @inTargetZone
end

#learnPaceObject



293
294
295
296
297
298
299
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 293

def learnPace
    if @learned > 0
        roundToOneDecimal(learnTime / @learned)
    else
        0.0
    end
end

#learnTimeObject



278
279
280
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 278

def learnTime
    @learnTimer.total
end

#learnTimePercentObject



309
310
311
312
313
314
315
316
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 309

def learnTimePercent
    total = learnPace + reviewPace
    if total > 0
        ((learnPace * 100) / total).to_i
    else
        0
    end
end

#recentAccuracyObject



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 57

def recentAccuracy
    retVal = 0
    if @lastTen.size > 0
        0.upto(@lastTen.size) do |i|
            if @lastTen[i]
                retVal += 1
            end
        end
        retVal = (retVal * 100 / @lastTen.size).to_i
    end
    retVal
end

#record(bool) ⇒ Object

Record a result. True means that the item was guessed correctly, false means it was incorrect.



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 45

def record(bool)
    @lastTen.push(bool)
    while @lastTen.size > 10
        @lastTen.delete_at(0)
    end
    if inTargetZone?
        @timesInTargetZone += 1
    else
        @timesInTargetZone = 0
    end
end

#recordReviewRate(item) ⇒ Object



93
94
95
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 93

def recordReviewRate(item)
    @reviewRateSum += item.schedule.reviewRate
end

#reEstimateObject

Generates an estimate of the % accuracy in an integer



191
192
193
194
195
196
197
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 191

def reEstimate
    hop = ((recentAccuracy - @estimate) * 0.3).to_i
    retVal = @estimate + hop
    if (retVal > 100) then retVal = 100 end
    if (retVal < 0) then retVal = 0 end
    @estimate = retVal
end

#resetConfidenceObject



262
263
264
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 262

def resetConfidence
    @confidence = MINIMUM_CONFIDENCE
end

#reviewBinObject



97
98
99
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 97

def reviewBin
    return @quiz.contents.bins[@binNumber]
end

#reviewPaceObject



301
302
303
304
305
306
307
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 301

def reviewPace
    if @reviewed > 0
        roundToOneDecimal(reviewTime / @reviewed)
    else
        0.0
    end
end

#reviewTimeObject



282
283
284
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 282

def reviewTime
    @reviewTimer.total
end

#roundToOneDecimal(value) ⇒ Object



286
287
288
289
290
291
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 286

def roundToOneDecimal(value)
    value = value * 10.0
    value = value.round
    value = value.to_f / 10.0
    value
end

#sizeObject



101
102
103
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 101

def size
    return reviewBin.length
end

#startTimer(isReview) ⇒ Object



266
267
268
269
270
271
272
273
274
275
276
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 266

def startTimer(isReview)
    if !@currentTimer.nil?
        @currentTimer.stop
    end
    if isReview
        @currentTimer = @reviewTimer
    else
        @currentTimer = @learnTimer
    end
    @currentTimer.start
end

#statsTableObject



122
123
124
125
126
127
128
129
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 122

def statsTable
    dCounter = DurationCounter.new

    reviewBin.each do |item|
        dCounter.count(item)
    end
    return dCounter
end

#totalObject



186
187
188
# File 'lib/jldrill/model/Quiz/Statistics.rb', line 186

def total
    @correct + @incorrect
end