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 ( 7a75cd...35bd8c )
by Anca
21:54
created

crowdtruth.models.metrics   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 578
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 57
eloc 321
dl 0
loc 578
ccs 263
cts 263
cp 1
rs 5.04
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 55 5
B Metrics.annotation_quality_score() 0 56 8
A Metrics.aqs_dict() 0 25 4
B Metrics.worker_unit_agreement() 0 58 5
F Metrics.run() 0 215 23
B Metrics.worker_worker_agreement() 0 63 6

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