Conditions | 23 |
Total Lines | 215 |
Code Lines | 160 |
Lines | 0 |
Ratio | 0 % |
Tests | 129 |
CRAP Score | 23 |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like crowdtruth.models.metrics.Metrics.run() 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 | """ |
||
330 | 1 | @staticmethod |
|
331 | 1 | def run(results, config, max_delta=0.001): |
|
332 | ''' |
||
333 | iteratively run the CrowdTruth metrics |
||
334 | ''' |
||
335 | |||
336 | 1 | judgments = results['judgments'].copy() |
|
337 | 1 | units = results['units'].copy() |
|
338 | |||
339 | # unit_work_ann_dict, work_unit_ann_dict, unit_ann_dict |
||
340 | # to be done: change to use all vectors in one unit |
||
341 | 1 | col = list(config.output.values())[0] |
|
342 | 1 | unit_ann_dict = dict(units.copy()[col]) |
|
343 | |||
344 | 1 | def expanded_vector(worker, unit): |
|
345 | ''' |
||
346 | expand the vector of a worker on a given unit |
||
347 | ''' |
||
348 | 1 | vector = Counter() |
|
349 | 1 | for ann in unit: |
|
350 | 1 | if ann in worker: |
|
351 | 1 | vector[ann] = worker[ann] |
|
352 | else: |
||
353 | 1 | vector[ann] = 0 |
|
354 | 1 | return vector |
|
355 | |||
356 | # fill judgment vectors with unit keys |
||
357 | 1 | for index, row in judgments.iterrows(): |
|
358 | 1 | judgments.at[index, col] = expanded_vector(row[col], units.at[row['unit'], col]) |
|
359 | |||
360 | 1 | unit_work_ann_dict = judgments[['unit', 'worker', col]].copy().groupby('unit') |
|
361 | 1 | unit_work_ann_dict = {name : group.set_index('worker')[col].to_dict() \ |
|
362 | for name, group in unit_work_ann_dict} |
||
363 | |||
364 | 1 | work_unit_ann_dict = judgments[['worker', 'unit', col]].copy().groupby('worker') |
|
365 | 1 | work_unit_ann_dict = {name : group.set_index('unit')[col].to_dict() \ |
|
366 | for name, group in work_unit_ann_dict} |
||
367 | |||
368 | #initialize data structures |
||
369 | 1 | uqs_list = list() |
|
370 | 1 | wqs_list = list() |
|
371 | 1 | wwa_list = list() |
|
372 | 1 | wsa_list = list() |
|
373 | 1 | aqs_list = list() |
|
374 | |||
375 | 1 | uqs = dict((unit_id, 1.0) for unit_id in unit_work_ann_dict) |
|
376 | 1 | wqs = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
377 | 1 | wwa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
378 | 1 | wsa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
379 | |||
380 | 1 | uqs_list.append(uqs.copy()) |
|
381 | 1 | wqs_list.append(wqs.copy()) |
|
382 | 1 | wwa_list.append(wwa.copy()) |
|
383 | 1 | wsa_list.append(wsa.copy()) |
|
384 | |||
385 | 1 | def init_aqs(config, unit_ann_dict): |
|
386 | """ initialize aqs depending on whether or not it is an open ended task """ |
||
387 | 1 | aqs = dict() |
|
388 | 1 | if not config.open_ended_task: |
|
389 | 1 | aqs_keys = list(unit_ann_dict[list(unit_ann_dict.keys())[0]].keys()) |
|
390 | 1 | for ann in aqs_keys: |
|
391 | 1 | aqs[ann] = 1.0 |
|
392 | else: |
||
393 | 1 | for unit_id in unit_ann_dict: |
|
394 | 1 | for ann in unit_ann_dict[unit_id]: |
|
395 | 1 | aqs[ann] = 1.0 |
|
396 | 1 | return aqs |
|
397 | |||
398 | 1 | aqs = init_aqs(config, unit_ann_dict) |
|
399 | 1 | aqs_list.append(aqs.copy()) |
|
400 | |||
401 | 1 | uqs_len = len(list(uqs.keys())) * 1.0 |
|
402 | 1 | wqs_len = len(list(wqs.keys())) * 1.0 |
|
403 | 1 | aqs_len = len(list(aqs.keys())) * 1.0 |
|
404 | |||
405 | # compute metrics until stable values |
||
406 | 1 | iterations = 0 |
|
407 | 1 | while max_delta >= 0.001: |
|
408 | 1 | uqs_new = dict() |
|
409 | 1 | wqs_new = dict() |
|
410 | 1 | wwa_new = dict() |
|
411 | 1 | wsa_new = dict() |
|
412 | |||
413 | 1 | avg_uqs_delta = 0.0 |
|
414 | 1 | avg_wqs_delta = 0.0 |
|
415 | 1 | avg_aqs_delta = 0.0 |
|
416 | 1 | max_delta = 0.0 |
|
417 | |||
418 | # pdb.set_trace() |
||
419 | |||
420 | 1 | def compute_wqs(wwa_new, wsa_new, wqs_new, work_unit_ann_dict, unit_ann_dict, \ |
|
421 | unit_work_ann_dict, wqs_list, uqs_list, aqs_list, wqs_len, \ |
||
422 | max_delta, avg_wqs_delta): |
||
423 | """ compute worker quality score (WQS) """ |
||
424 | 1 | for worker_id, _ in work_unit_ann_dict.items(): |
|
425 | 1 | wwa_new[worker_id] = Metrics.worker_worker_agreement( \ |
|
426 | worker_id, work_unit_ann_dict, \ |
||
427 | unit_work_ann_dict, \ |
||
428 | wqs_list[len(wqs_list) - 1], \ |
||
429 | uqs_list[len(uqs_list) - 1], \ |
||
430 | aqs_list[len(aqs_list) - 1]) |
||
431 | 1 | wsa_new[worker_id] = Metrics.worker_unit_agreement( \ |
|
432 | worker_id, \ |
||
433 | unit_ann_dict, \ |
||
434 | work_unit_ann_dict, \ |
||
435 | uqs_list[len(uqs_list) - 1], \ |
||
436 | aqs_list[len(aqs_list) - 1], \ |
||
437 | wqs_list[len(aqs_list) - 1][worker_id]) |
||
438 | 1 | wqs_new[worker_id] = wwa_new[worker_id] * wsa_new[worker_id] |
|
439 | 1 | max_delta = max(max_delta, \ |
|
440 | abs(wqs_new[worker_id] - wqs_list[len(wqs_list) - 1][worker_id])) |
||
441 | 1 | avg_wqs_delta += abs(wqs_new[worker_id] - \ |
|
442 | wqs_list[len(wqs_list) - 1][worker_id]) |
||
443 | 1 | avg_wqs_delta /= wqs_len |
|
444 | |||
445 | 1 | return wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta |
|
446 | |||
447 | 1 | def reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new): |
|
448 | """ reconstruct unit_ann_dict with worker scores """ |
||
449 | 1 | new_unit_ann_dict = dict() |
|
450 | 1 | for unit_id, ann_dict in unit_ann_dict.items(): |
|
451 | 1 | new_unit_ann_dict[unit_id] = dict() |
|
452 | 1 | for ann, _ in ann_dict.items(): |
|
453 | 1 | new_unit_ann_dict[unit_id][ann] = 0.0 |
|
454 | 1 | for work_id, srd in work_unit_ann_dict.items(): |
|
455 | 1 | wqs_work_id = wqs_new[work_id] |
|
456 | 1 | for unit_id, ann_dict in srd.items(): |
|
457 | 1 | for ann, score in ann_dict.items(): |
|
458 | 1 | new_unit_ann_dict[unit_id][ann] += score * wqs_work_id |
|
459 | |||
460 | 1 | return new_unit_ann_dict |
|
461 | |||
462 | 1 | def compute_aqs(aqs, work_unit_ann_dict, uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta): |
|
463 | """ compute annotation quality score (aqs) """ |
||
464 | 1 | aqs_new = Metrics.annotation_quality_score(list(aqs.keys()), work_unit_ann_dict, \ |
|
465 | uqs_list[len(uqs_list) - 1], \ |
||
466 | wqs_list[len(wqs_list) - 1]) |
||
467 | 1 | for ann, _ in aqs_new.items(): |
|
468 | 1 | max_delta = max(max_delta, abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann])) |
|
469 | 1 | avg_aqs_delta += abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann]) |
|
470 | 1 | avg_aqs_delta /= aqs_len |
|
471 | 1 | return aqs_new, max_delta, avg_aqs_delta |
|
472 | |||
473 | 1 | def compute_uqs(uqs_new, unit_work_ann_dict, wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta): |
|
474 | """ compute unit quality score (uqs) """ |
||
475 | 1 | for unit_id, _ in unit_work_ann_dict.items(): |
|
476 | 1 | uqs_new[unit_id] = Metrics.unit_quality_score(unit_id, unit_work_ann_dict, \ |
|
477 | wqs_list[len(wqs_list) - 1], \ |
||
478 | aqs_list[len(aqs_list) - 1]) |
||
479 | 1 | max_delta = max(max_delta, \ |
|
480 | abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id])) |
||
481 | 1 | avg_uqs_delta += abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id]) |
|
482 | 1 | avg_uqs_delta /= uqs_len |
|
483 | 1 | return uqs_new, max_delta, avg_uqs_delta |
|
484 | |||
485 | 1 | if not config.open_ended_task: |
|
486 | # compute annotation quality score (aqs) |
||
487 | 1 | aqs_new, max_delta, avg_aqs_delta = compute_aqs(aqs, work_unit_ann_dict, \ |
|
488 | uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta) |
||
489 | |||
490 | # compute unit quality score (uqs) |
||
491 | 1 | uqs_new, max_delta, avg_uqs_delta = compute_uqs(uqs_new, unit_work_ann_dict, \ |
|
492 | wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta) |
||
493 | |||
494 | # compute worker quality score (WQS) |
||
495 | 1 | wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta = compute_wqs(\ |
|
496 | wwa_new, wsa_new, wqs_new, \ |
||
497 | work_unit_ann_dict, unit_ann_dict, unit_work_ann_dict, wqs_list, \ |
||
498 | uqs_list, aqs_list, wqs_len, max_delta, avg_wqs_delta) |
||
499 | |||
500 | # save results for current iteration |
||
501 | 1 | uqs_list.append(uqs_new.copy()) |
|
502 | 1 | wqs_list.append(wqs_new.copy()) |
|
503 | 1 | wwa_list.append(wwa_new.copy()) |
|
504 | 1 | wsa_list.append(wsa_new.copy()) |
|
505 | 1 | if not config.open_ended_task: |
|
506 | 1 | aqs_list.append(aqs_new.copy()) |
|
|
|||
507 | 1 | iterations += 1 |
|
508 | |||
509 | 1 | unit_ann_dict = reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new) |
|
510 | |||
511 | 1 | logging.info(str(iterations) + " iterations; max d= " + str(max_delta) + \ |
|
512 | " ; wqs d= " + str(avg_wqs_delta) + "; uqs d= " + str(avg_uqs_delta) + \ |
||
513 | "; aqs d= " + str(avg_aqs_delta)) |
||
514 | |||
515 | 1 | def save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, iteration_value): |
|
516 | """ save the unit annotation score for print """ |
||
517 | 1 | srs = Counter() |
|
518 | 1 | for unit_id in unit_ann_dict: |
|
519 | 1 | srs[unit_id] = Counter() |
|
520 | 1 | for ann in unit_ann_dict[unit_id]: |
|
521 | 1 | srs[unit_id][ann] = Metrics.unit_annotation_score(unit_id, \ |
|
522 | ann, unit_work_ann_dict, \ |
||
523 | iteration_value) |
||
524 | 1 | return srs |
|
525 | |||
526 | 1 | srs = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[len(wqs_list) - 1]) |
|
527 | 1 | srs_initial = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[0]) |
|
528 | |||
529 | 1 | results['units']['uqs'] = pd.Series(uqs_list[-1]) |
|
530 | 1 | results['units']['unit_annotation_score'] = pd.Series(srs) |
|
531 | 1 | results['workers']['wqs'] = pd.Series(wqs_list[-1]) |
|
532 | 1 | results['workers']['wwa'] = pd.Series(wwa_list[-1]) |
|
533 | 1 | results['workers']['wsa'] = pd.Series(wsa_list[-1]) |
|
534 | 1 | if not config.open_ended_task: |
|
535 | 1 | results['annotations']['aqs'] = pd.Series(aqs_list[-1]) |
|
536 | |||
537 | 1 | results['units']['uqs_initial'] = pd.Series(uqs_list[1]) |
|
538 | 1 | results['units']['unit_annotation_score_initial'] = pd.Series(srs_initial) |
|
539 | 1 | results['workers']['wqs_initial'] = pd.Series(wqs_list[1]) |
|
540 | 1 | results['workers']['wwa_initial'] = pd.Series(wwa_list[1]) |
|
541 | 1 | results['workers']['wsa_initial'] = pd.Series(wsa_list[1]) |
|
542 | 1 | if not config.open_ended_task: |
|
543 | 1 | results['annotations']['aqs_initial'] = pd.Series(aqs_list[1]) |
|
544 | return results |
||
545 |