GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 6546a6...7a75cd )
by Anca
22:34
created

crowdtruth.models.metrics   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 596
Duplicated Lines 0 %

Test Coverage

Coverage 99.25%

Importance

Changes 0
Metric Value
wmc 59
eloc 327
dl 0
loc 596
ccs 265
cts 267
cp 0.9925
rs 4.08
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A Metrics.unit_annotation_score() 0 29 3
A Metrics.compute_ann_quality_factors() 0 41 3
B Metrics.unit_quality_score() 0 60 5
B Metrics.annotation_quality_score() 0 56 8
A Metrics.aqs_dict() 0 25 4
B Metrics.worker_unit_agreement() 0 64 6
F Metrics.run() 0 218 23
B Metrics.worker_worker_agreement() 0 67 7

How to fix   Complexity   

Complexity

Complex classes like crowdtruth.models.metrics often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
Initialization of CrowdTruth metrics
3
"""
4 1
import logging
5 1
import math
6
7 1
from collections import Counter
8
9 1
import numpy as np
10 1
import pandas as pd
11
12 1
SMALL_NUMBER_CONST = 0.00000001
13
14 1
class Metrics():
15
    """
16
    Computes and applies the CrowdTruth metrics for evaluating units, workers and annotations.
17
    """
18
19
    # Unit Quality Score
20 1
    @staticmethod
21
    def unit_quality_score(unit_id, unit_work_ann_dict, wqs, aqs):
22
        """
23
        Computes the unit quality score.
24
25
        The unit quality score (UQS) is computed as the average cosine similarity between
26
        all worker vectors for a given unit, weighted by the worker quality (WQS) and the
27
        annotation quality (AQS). The goal is to capture the degree of agreement in annotating
28
        the media unit.
29
30
        Through the weighted average, workers and annotations with lower quality will have
31
        less of an impact on the final score.
32
33
        To weigh the metrics with the annotation quality, we compute weighted_cosine, the weighted
34
        version of the cosine similarity.
35
36
        Args:
37
            unit_id: Unit id.
38
            unit_work_ann_dict: A dictionary that contains all the workers judgments for the unit.
39
            aqs: Dict of annotation_id (string) that contains the annotation quality score (float)
40
            wqs: Dict of worker_id (string) that contains the worker quality score (float)
41
42
        Returns:
43
            The quality score (UQS) of the given unit.
44
        """
45
46 1
        uqs_numerator = 0.0
47 1
        uqs_denominator = 0.0
48 1
        worker_ids = list(unit_work_ann_dict[unit_id].keys())
49
50 1
        for worker_i in range(len(worker_ids) - 1):
51 1
            for worker_j in range(worker_i + 1, len(worker_ids)):
52
                # print worker_ids[i] + " - " + worker_ids[j] + "\n"
53 1
                numerator = 0.0
54 1
                denominator_i = 0.0
55 1
                denominator_j = 0.0
56
57 1
                worker_i_vector = unit_work_ann_dict[unit_id][worker_ids[worker_i]]
58 1
                worker_j_vector = unit_work_ann_dict[unit_id][worker_ids[worker_j]]
59
60 1
                for ann in worker_i_vector:
61 1
                    worker_i_vector_ann = worker_i_vector[ann]
62 1
                    worker_j_vector_ann = worker_j_vector[ann]
63 1
                    numerator += aqs[ann] * (worker_i_vector_ann * worker_j_vector_ann)
64 1
                    denominator_i += aqs[ann] * (worker_i_vector_ann * worker_i_vector_ann)
65 1
                    denominator_j += aqs[ann] * (worker_j_vector_ann * worker_j_vector_ann)
66
67 1
                weighted_cosine = numerator / math.sqrt(denominator_i * denominator_j)
68
69 1
                uqs_numerator += weighted_cosine * wqs[worker_ids[worker_i]] * \
70
                                 wqs[worker_ids[worker_j]]
71 1
                uqs_denominator += wqs[worker_ids[worker_i]] * wqs[worker_ids[worker_j]]
72
73
        # if uqs_numerator / uqs_denominator > 1:
74
        #     logging.warning(str(unit_id) + " has UQS = " + str(uqs_numerator) +" /" +\
75
        #         str(uqs_denominator))
76
77 1
        if uqs_denominator < SMALL_NUMBER_CONST:
78 1
            uqs_denominator = SMALL_NUMBER_CONST
79 1
        return uqs_numerator / uqs_denominator
80
81
82
    # Worker - Unit Agreement
83 1
    @staticmethod
84
    def worker_unit_agreement(worker_id, unit_ann_dict, work_unit_ann_dict, uqs, aqs, wqs):
85
        """
86
        Computes the worker agreement on a unit.
87
88
        The worker unit agreement (WUA) is the average cosine distance between the annotations
89
        of a worker i and all the other annotations for the units they have worked on,
90
        weighted by the unit and annotation quality. It calculates how much a worker disagrees
91
        with the crowd on a unit basis.
92
93
        Through the weighted average, units and anntation with lower quality will have less
94
        of an impact on the final score.
95
96
        Args:
97
            worker_id: Worker id.
98
            unit_ann_dict: Dictionary of units and their aggregated annotations.
99
            work_unit_ann_dict: Dictionary of units (and its annotation) annotated by the worker.
100
            uqs: Dict unit_id that contains the unit quality scores (float).
101
            aqs: Dict of annotation_id (string) that contains the annotation quality scores (float).
102
            wqs: Dict of worker_id (string) that contains the worker quality scores (float).
103
104
        Returns:
105
            The worker unit agreement score for the given worker.
106
        """
107
108 1
        wsa_numerator = 0.0
109 1
        wsa_denominator = 0.0
110 1
        work_unit_ann_dict_worker_id = work_unit_ann_dict[worker_id]
111
112 1
        for unit_id in work_unit_ann_dict_worker_id:
113 1
            numerator = 0.0
114 1
            denominator_w = 0.0
115 1
            denominator_s = 0.0
116
117 1
            worker_vector = work_unit_ann_dict[worker_id][unit_id]
118 1
            unit_vector = unit_ann_dict[unit_id]
119
120 1
            for ann in worker_vector:
121 1
                worker_vector_ann = worker_vector[ann] * wqs
122 1
                unit_vector_ann = unit_vector[ann]
123
124 1
                numerator += aqs[ann] * worker_vector_ann * \
125
                    (unit_vector_ann - worker_vector_ann)
126
                # if numerator < 0:
127
                #     print str(unit_vector_ann) + " - " + str(worker_vector_ann)
128 1
                denominator_w += aqs[ann] * \
129
                    (worker_vector_ann * worker_vector_ann)
130 1
                denominator_s += aqs[ann] * ( \
131
                    (unit_vector_ann - worker_vector_ann) * \
132
                    (unit_vector_ann - worker_vector_ann))
133 1
            weighted_cosine = None
134 1
            if math.sqrt(denominator_w * denominator_s) < SMALL_NUMBER_CONST:
135 1
                weighted_cosine = SMALL_NUMBER_CONST
136
            else:
137 1
                weighted_cosine = numerator / math.sqrt(denominator_w * denominator_s)
138 1
            wsa_numerator += weighted_cosine * uqs[unit_id]
139 1
            wsa_denominator += uqs[unit_id]
140 1
        if wsa_denominator < SMALL_NUMBER_CONST:
141 1
            wsa_denominator = SMALL_NUMBER_CONST
142
143 1
        if wsa_numerator / wsa_denominator < 0:
144
            logging.warning(
145
                str(worker_id) + " WUA = " + str(wsa_numerator) + "/" + str(wsa_denominator))
146 1
        return wsa_numerator / wsa_denominator
147
148
    # Worker - Worker Agreement
149 1
    @staticmethod
150
    def worker_worker_agreement(worker_id, work_unit_ann_dict, unit_work_ann_dict, wqs, uqs, aqs):
151
        """
152
        Computes the agreement between every two workers.
153
154
        The worker-worker agreement (WWA) is the average cosine distance between the annotations of
155
        a worker i and all other workers that have worked on the same media units as worker i,
156
        weighted by the worker and annotation qualities.
157
158
        The metric gives an indication as to whether there are consisently like-minded workers.
159
        This is useful for identifying communities of thought.
160
161
        Through the weighted average, workers and annotations with lower quality will have less
162
        of an impact on the final score of the given worker.
163
164
        Args:
165
            worker_id: Worker id.
166
            work_unit_ann_dict: Dictionary of worker annotation vectors on annotated units.
167
            unit_work_ann_dict: Dictionary of unit annotation vectors.
168
            uqs: Dict unit_id that contains the unit quality scores (float).
169
            aqs: Dict of annotation_id (string) that contains the annotation quality scores (float).
170
            wqs: Dict of worker_id (string) that contains the worker quality scores (float).
171
172
        Returns:
173
            The worker worker agreement score for the given worker.
174
        """
175
176 1
        wwa_numerator = 0.0
177 1
        wwa_denominator = 0.0
178
179 1
        worker_vector = work_unit_ann_dict[worker_id]
180 1
        unit_ids = list(work_unit_ann_dict[worker_id].keys())
181
182 1
        for unit_id in unit_ids:
183 1
            wv_unit_id = worker_vector[unit_id]
184 1
            unit_work_ann_dict_unit_id = unit_work_ann_dict[unit_id]
185 1
            for other_workid in unit_work_ann_dict_unit_id:
186 1
                if worker_id != other_workid:
187 1
                    numerator = 0.0
188 1
                    denominator_w = 0.0
189 1
                    denominator_ow = 0.0
190
191 1
                    unit_work_ann_dict_uid_oworkid = unit_work_ann_dict_unit_id[other_workid]
192 1
                    for ann in wv_unit_id:
193 1
                        unit_work_ann_dict_uid_oworkid_ann = unit_work_ann_dict_uid_oworkid[ann]
194 1
                        wv_unit_id_ann = wv_unit_id[ann]
195
196 1
                        numerator += aqs[ann] * (wv_unit_id_ann * \
197
                                     unit_work_ann_dict_uid_oworkid_ann)
198
199 1
                        denominator_w += aqs[ann] * (wv_unit_id_ann * wv_unit_id_ann)
200
201 1
                        denominator_ow += aqs[ann] * \
202
                                         (unit_work_ann_dict_uid_oworkid_ann *\
203
                                          unit_work_ann_dict_uid_oworkid_ann)
204
205 1
                    weighted_cosine = numerator / math.sqrt(denominator_w * denominator_ow)
206
                    # pdb.set_trace()
207 1
                    wwa_numerator += weighted_cosine * wqs[other_workid] * uqs[unit_id]
208 1
                    wwa_denominator += wqs[other_workid] * uqs[unit_id]
209 1
        if wwa_denominator < SMALL_NUMBER_CONST:
210 1
            wwa_denominator = SMALL_NUMBER_CONST
211
212 1
        if wwa_numerator / wwa_denominator < 0:
213
            logging.warning(
214
                str(worker_id) + " WWA = " + str(wwa_numerator) + "/" + str(wwa_denominator))
215 1
        return wwa_numerator / wwa_denominator
216
217
218
219
    # Unit - Annotation Score (UAS)
220 1
    @staticmethod
221
    def unit_annotation_score(unit_id, annotation, unit_work_annotation_dict, wqs):
222
        """
223
        Computes the unit annotation score.
224
225
        The unit - annotation score (UAS) calculates the likelihood that annotation a
226
        is expressed in unit u. It is the ratio of the number of workers that picked
227
        annotation a over all workers that annotated the unit, weighted by the worker quality.
228
229
        Args:
230
            unit_id: Unit id.
231
            annotation: Annotation.
232
            unit_work_annotation_dict: Dictionary of unit annotation vectors.
233
            wqs: Dict of worker_id (string) that contains the worker quality scores (float).
234
235
        Returns:
236
            The unit annotation score for the given unit and annotation.
237
        """
238
239 1
        uas_numerator = 0.0
240 1
        uas_denominator = 0.0
241
242 1
        worker_ids = unit_work_annotation_dict[unit_id]
243 1
        for worker_id in worker_ids:
244 1
            uas_numerator += worker_ids[worker_id][annotation] * wqs[worker_id]
245 1
            uas_denominator += wqs[worker_id]
246 1
        if uas_denominator < SMALL_NUMBER_CONST:
247 1
            uas_denominator = SMALL_NUMBER_CONST
248 1
        return uas_numerator / uas_denominator
249
250 1
    @staticmethod
251
    def compute_ann_quality_factors(numerator, denominator, work_unit_ann_dict_worker_i, \
252
                                    work_unit_ann_dict_worker_j, ann, uqs):
253
        """
254
        Computes the factors for each unit annotation.
255
256
        Args:
257
            numerator: Current numerator
258
            denominator: Current denominator
259
            work_unit_ann_dict_worker_i: Dict of worker i annotation vectors on annotated units.
260
            work_unit_ann_dict_worker_j: Dict of worker j annotation vectors on annotated units.
261
            ann: Annotation value
262
            uqs: Dict unit_id that contains the unit quality scores (float).
263
264
        Returns:
265
            The annotation quality factors.
266
        """
267 1
        for unit_id, work_unit_ann_dict_work_i_unit in work_unit_ann_dict_worker_i.items():
268 1
            if unit_id in work_unit_ann_dict_worker_j:
269 1
                work_unit_ann_dict_work_j_unit = work_unit_ann_dict_worker_j[unit_id]
270
271 1
                work_unit_ann_dict_wj_unit_ann = work_unit_ann_dict_work_j_unit[ann]
272
273 1
                def compute_numerator_aqs(unit_id_ann_value, worker_i_ann_value, \
274
                                                          worker_j_ann_value):
275
                    """ compute numerator """
276 1
                    numerator = unit_id_ann_value * worker_i_ann_value * \
277
                                                worker_j_ann_value
278 1
                    return numerator
279
280 1
                def compute_denominator_aqs(unit_id_ann_value, worker_j_ann_value):
281
                    """ compute denominator """
282 1
                    denominator = unit_id_ann_value * worker_j_ann_value
283 1
                    return denominator
284
285 1
                numerator += compute_numerator_aqs(uqs[unit_id], \
286
                                                    work_unit_ann_dict_work_i_unit[ann], \
287
                                                    work_unit_ann_dict_wj_unit_ann)
288 1
                denominator += compute_denominator_aqs(uqs[unit_id], \
289
                                                        work_unit_ann_dict_wj_unit_ann)
290 1
        return numerator, denominator
291
292 1
    @staticmethod
293
    def aqs_dict(annotations, aqs_numerator, aqs_denominator):
294
        """
295
        Create the dictionary of annotation quality score values.
296
297
        Args:
298
            annotations: Dictionary of annotations.
299
            aqs_numerator: Annotation numerator.
300
            aqs_denominator: Annotation denominator.
301
302
303
        Returns:
304
            The dictionary of annotation quality scores.
305
        """
306
307 1
        aqs = dict()
308 1
        for ann in annotations:
309 1
            if aqs_denominator[ann] > SMALL_NUMBER_CONST:
310 1
                aqs[ann] = aqs_numerator[ann] / aqs_denominator[ann]
311
                # prevent division by zero by storing very small value instead
312 1
                if aqs[ann] < SMALL_NUMBER_CONST:
313 1
                    aqs[ann] = SMALL_NUMBER_CONST
314
            else:
315 1
                aqs[ann] = SMALL_NUMBER_CONST
316 1
        return aqs
317
318
319
    # Annotation Quality Score (AQS)
320 1
    @staticmethod
321
    def annotation_quality_score(annotations, work_unit_ann_dict, uqs, wqs):
322
        """
323
        Computes the annotation quality score.
324
325
        The annotation quality score AQS calculates the agreement of selecting an annotation a,
326
        over all the units it appears in. Therefore, it is only applicable to closed tasks, where
327
        the same annotation set is used for all units. It is based on the probability that if a
328
        worker j annotates annotation a in a unit, worker i will also annotate it.
329
330
        The annotation quality score is the weighted average of these probabilities for all possible
331
        pairs of workers. Through the weighted average, units and workers with lower quality will
332
        have less of an impact on the final score of the annotation.
333
334
        Args:
335
            annotations: Possible annotations.
336
            work_unit_annotation_dict: Dictionary of worker annotation vectors on annotated units.
337
            uqs: Dict unit_id that contains the unit quality scores (float).
338
            wqs: Dict of worker_id (string) that contains the worker quality scores (float).
339
340
        Returns:
341
            The worker worker agreement score for the given worker.
342
        """
343
344 1
        aqs_numerator = dict()
345 1
        aqs_denominator = dict()
346
347 1
        for ann in annotations:
348 1
            aqs_numerator[ann] = 0.0
349 1
            aqs_denominator[ann] = 0.0
350
351 1
        for worker_i, work_unit_ann_dict_worker_i in work_unit_ann_dict.items():
352
            #work_unit_ann_dict_worker_i = work_unit_ann_dict[worker_i]
353 1
            work_unit_ann_dict_i_keys = list(work_unit_ann_dict_worker_i.keys())
354 1
            for worker_j, work_unit_ann_dict_worker_j in work_unit_ann_dict.items():
355
                #work_unit_ann_dict_worker_j = work_unit_ann_dict[worker_j]
356 1
                work_unit_ann_dict_j_keys = list(work_unit_ann_dict_worker_j.keys())
357
358 1
                length_keys = len(np.intersect1d(np.array(work_unit_ann_dict_i_keys), \
359
                                                 np.array(work_unit_ann_dict_j_keys)))
360
361 1
                if worker_i != worker_j and length_keys > 0:
362 1
                    for ann in annotations:
363 1
                        numerator = 0.0
364 1
                        denominator = 0.0
365
366 1
                        numerator, denominator = Metrics.compute_ann_quality_factors(numerator, \
367
                                                    denominator, work_unit_ann_dict_worker_i, \
368
                                                    work_unit_ann_dict_worker_j, ann, uqs)
369
370 1
                        if denominator > 0:
371 1
                            aqs_numerator[ann] += wqs[worker_i] * wqs[worker_j] * \
372
                                                        numerator / denominator
373 1
                            aqs_denominator[ann] += wqs[worker_i] * wqs[worker_j]
374
375 1
        return Metrics.aqs_dict(annotations, aqs_numerator, aqs_denominator)
376
377
378 1
    @staticmethod
379 1
    def run(results, config, max_delta=0.001):
380
        '''
381
        iteratively run the CrowdTruth metrics
382
        '''
383
384 1
        judgments = results['judgments'].copy()
385 1
        units = results['units'].copy()
386
387
        # unit_work_ann_dict, work_unit_ann_dict, unit_ann_dict
388
        # to be done: change to use all vectors in one unit
389 1
        col = list(config.output.values())[0]
390 1
        unit_ann_dict = dict(units.copy()[col])
391
392 1
        def expanded_vector(worker, unit):
393
            '''
394
            expand the vector of a worker on a given unit
395
            '''
396 1
            vector = Counter()
397 1
            for ann in unit:
398 1
                if ann in worker:
399 1
                    vector[ann] = worker[ann]
400
                else:
401 1
                    vector[ann] = 0
402 1
            return vector
403
404
        # fill judgment vectors with unit keys
405 1
        for index, row in judgments.iterrows():
406 1
            judgments.at[index, col] = expanded_vector(row[col], units.at[row['unit'], col])
407
408 1
        unit_work_ann_dict = judgments[['unit', 'worker', col]].copy().groupby('unit')
409 1
        unit_work_ann_dict = {name : group.set_index('worker')[col].to_dict() \
410
                                for name, group in unit_work_ann_dict}
411
412 1
        work_unit_ann_dict = judgments[['worker', 'unit', col]].copy().groupby('worker')
413 1
        work_unit_ann_dict = {name : group.set_index('unit')[col].to_dict() \
414
                                for name, group in work_unit_ann_dict}
415
416
        #initialize data structures
417 1
        uqs_list = list()
418 1
        wqs_list = list()
419 1
        wwa_list = list()
420 1
        wsa_list = list()
421 1
        aqs_list = list()
422
423 1
        uqs = dict((unit_id, 1.0) for unit_id in unit_work_ann_dict)
424 1
        wqs = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict)
425 1
        wwa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict)
426 1
        wsa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict)
427
428 1
        uqs_list.append(uqs.copy())
429 1
        wqs_list.append(wqs.copy())
430 1
        wwa_list.append(wwa.copy())
431 1
        wsa_list.append(wsa.copy())
432
433 1
        def init_aqs(config, unit_ann_dict):
434
            """ initialize aqs depending on whether or not it is an open ended task """
435 1
            aqs = dict()
436 1
            if not config.open_ended_task:
437 1
                aqs_keys = list(unit_ann_dict[list(unit_ann_dict.keys())[0]].keys())
438 1
                for ann in aqs_keys:
439 1
                    aqs[ann] = 1.0
440
            else:
441 1
                for unit_id in unit_ann_dict:
442 1
                    for ann in unit_ann_dict[unit_id]:
443 1
                        aqs[ann] = 1.0
444 1
            return aqs
445
446 1
        aqs = init_aqs(config, unit_ann_dict)
447 1
        aqs_list.append(aqs.copy())
448
449 1
        uqs_len = len(list(uqs.keys())) * 1.0
450 1
        wqs_len = len(list(wqs.keys())) * 1.0
451 1
        aqs_len = len(list(aqs.keys())) * 1.0
452
453
        # compute metrics until stable values
454 1
        iterations = 0
455 1
        while max_delta >= 0.001:
456 1
            uqs_new = dict()
457 1
            wqs_new = dict()
458 1
            wwa_new = dict()
459 1
            wsa_new = dict()
460
461 1
            avg_uqs_delta = 0.0
462 1
            avg_wqs_delta = 0.0
463 1
            avg_aqs_delta = 0.0
464 1
            max_delta = 0.0
465
466
            # pdb.set_trace()
467
468 1
            def compute_wqs(wwa_new, wsa_new, wqs_new, work_unit_ann_dict, unit_ann_dict, \
469
                            unit_work_ann_dict, wqs_list, uqs_list, aqs_list, wqs_len, \
470
                            max_delta, avg_wqs_delta):
471
                """ compute worker quality score (WQS) """
472 1
                for worker_id, _ in work_unit_ann_dict.items():
473 1
                    wwa_new[worker_id] = Metrics.worker_worker_agreement( \
474
                             worker_id, work_unit_ann_dict, \
475
                             unit_work_ann_dict, \
476
                             wqs_list[len(wqs_list) - 1], \
477
                             uqs_list[len(uqs_list) - 1], \
478
                             aqs_list[len(aqs_list) - 1])
479
                    # assert wwa_new[worker_id] >= 0
480 1
                    wsa_new[worker_id] = Metrics.worker_unit_agreement( \
481
                             worker_id, \
482
                             unit_ann_dict, \
483
                             work_unit_ann_dict, \
484
                             uqs_list[len(uqs_list) - 1], \
485
                             aqs_list[len(aqs_list) - 1], \
486
                             wqs_list[len(wqs_list) - 1][worker_id])
487
                    # assert wsa_new[worker_id] >= 0
488 1
                    wqs_new[worker_id] = wwa_new[worker_id] * wsa_new[worker_id]
489 1
                    max_delta = max(max_delta, \
490
                                abs(wqs_new[worker_id] - wqs_list[len(wqs_list) - 1][worker_id]))
491 1
                    avg_wqs_delta += abs(wqs_new[worker_id] - \
492
                                         wqs_list[len(wqs_list) - 1][worker_id])
493 1
                avg_wqs_delta /= wqs_len
494
495 1
                return wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta
496
497 1
            def compute_aqs(aqs, work_unit_ann_dict, uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta):
498
                """ compute annotation quality score (aqs) """
499 1
                aqs_new = Metrics.annotation_quality_score(list(aqs.keys()), work_unit_ann_dict, \
500
                                                        uqs_list[len(uqs_list) - 1], \
501
                                                        wqs_list[len(wqs_list) - 1])
502 1
                for ann, _ in aqs_new.items():
503 1
                    max_delta = max(max_delta, abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann]))
504 1
                    avg_aqs_delta += abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann])
505 1
                avg_aqs_delta /= aqs_len
506 1
                return aqs_new, max_delta, avg_aqs_delta
507
508 1
            def compute_uqs(uqs_new, unit_work_ann_dict, wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta):
509
                """ compute unit quality score (uqs) """
510 1
                for unit_id, _ in unit_work_ann_dict.items():
511 1
                    uqs_new[unit_id] = Metrics.unit_quality_score(unit_id, unit_work_ann_dict, \
512
                                                                      wqs_list[len(wqs_list) - 1], \
513
                                                                      aqs_list[len(aqs_list) - 1])
514
                    # assert uqs_new[unit_id] >= 0
515 1
                    max_delta = max(max_delta, \
516
                                abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id]))
517 1
                    avg_uqs_delta += abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id])
518 1
                avg_uqs_delta /= uqs_len
519 1
                return uqs_new, max_delta, avg_uqs_delta
520
521 1
            def reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new):
522
                """ reconstruct unit_ann_dict with worker scores """
523 1
                new_unit_ann_dict = dict()
524 1
                for unit_id, ann_dict in unit_ann_dict.items():
525 1
                    new_unit_ann_dict[unit_id] = dict()
526 1
                    for ann, _ in ann_dict.items():
527 1
                        new_unit_ann_dict[unit_id][ann] = 0.0
528 1
                for work_id, srd in work_unit_ann_dict.items():
529 1
                    wqs_work_id = wqs_new[work_id]
530 1
                    for unit_id, ann_dict in srd.items():
531 1
                        for ann, score in ann_dict.items():
532 1
                            new_unit_ann_dict[unit_id][ann] += score * wqs_work_id
533
534 1
                return new_unit_ann_dict
535
536 1
            if not config.open_ended_task:
537
                # compute annotation quality score (aqs)
538 1
                aqs_new, max_delta, avg_aqs_delta = compute_aqs(aqs, work_unit_ann_dict, \
539
                                uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta)
540
541
            # compute unit quality score (uqs)
542 1
            uqs_new, max_delta, avg_uqs_delta = compute_uqs(uqs_new, unit_work_ann_dict, \
543
                                    wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta)
544
545
            # compute worker quality score (WQS)
546 1
            wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta = compute_wqs(\
547
                        wwa_new, wsa_new, wqs_new, \
548
                        work_unit_ann_dict, unit_ann_dict, unit_work_ann_dict, wqs_list, \
549
                        uqs_list, aqs_list, wqs_len, max_delta, avg_wqs_delta)
550
551
            # save results for current iteration
552 1
            uqs_list.append(uqs_new.copy())
553 1
            wqs_list.append(wqs_new.copy())
554 1
            wwa_list.append(wwa_new.copy())
555 1
            wsa_list.append(wsa_new.copy())
556 1
            if not config.open_ended_task:
557 1
                aqs_list.append(aqs_new.copy())
0 ignored issues
show
introduced by
The variable aqs_new does not seem to be defined for all execution paths.
Loading history...
558 1
            iterations += 1
559
560 1
            unit_ann_dict = reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new)
561
562 1
            logging.info(str(iterations) + " iterations; max d= " + str(max_delta) + \
563
                        " ; wqs d= " + str(avg_wqs_delta) + "; uqs d= " + str(avg_uqs_delta) + \
564
                        "; aqs d= " + str(avg_aqs_delta))
565
566 1
        def save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, iteration_value):
567
            """ save the unit annotation score for print """
568 1
            uas = Counter()
569 1
            for unit_id in unit_ann_dict:
570 1
                uas[unit_id] = Counter()
571 1
                for ann in unit_ann_dict[unit_id]:
572 1
                    uas[unit_id][ann] = Metrics.unit_annotation_score(unit_id, \
573
                                                ann, unit_work_ann_dict, \
574
                                                iteration_value)
575 1
            return uas
576
577 1
        uas = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[len(wqs_list) - 1])
578 1
        uas_initial = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[0])
579
580 1
        results['units']['uqs'] = pd.Series(uqs_list[-1])
581 1
        results['units']['unit_annotation_score'] = pd.Series(uas)
582 1
        results['workers']['wqs'] = pd.Series(wqs_list[-1])
583 1
        results['workers']['wwa'] = pd.Series(wwa_list[-1])
584 1
        results['workers']['wsa'] = pd.Series(wsa_list[-1])
585 1
        if not config.open_ended_task:
586 1
            results['annotations']['aqs'] = pd.Series(aqs_list[-1])
587
588 1
        results['units']['uqs_initial'] = pd.Series(uqs_list[1])
589 1
        results['units']['unit_annotation_score_initial'] = pd.Series(uas_initial)
590 1
        results['workers']['wqs_initial'] = pd.Series(wqs_list[1])
591 1
        results['workers']['wwa_initial'] = pd.Series(wwa_list[1])
592 1
        results['workers']['wsa_initial'] = pd.Series(wsa_list[1])
593 1
        if not config.open_ended_task:
594 1
            results['annotations']['aqs_initial'] = pd.Series(aqs_list[1])
595
        return results
596