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 | """ |
||
369 | 1 | @staticmethod |
|
370 | 1 | def run(results, config, max_delta=0.001): |
|
371 | ''' |
||
372 | iteratively run the CrowdTruth metrics |
||
373 | ''' |
||
374 | |||
375 | 1 | judgments = results['judgments'].copy() |
|
376 | 1 | units = results['units'].copy() |
|
377 | |||
378 | # unit_work_ann_dict, work_unit_ann_dict, unit_ann_dict |
||
379 | # to be done: change to use all vectors in one unit |
||
380 | 1 | col = list(config.output.values())[0] |
|
381 | 1 | unit_ann_dict = dict(units.copy()[col]) |
|
382 | |||
383 | 1 | def expanded_vector(worker, unit): |
|
384 | ''' |
||
385 | expand the vector of a worker on a given unit |
||
386 | ''' |
||
387 | 1 | vector = Counter() |
|
388 | 1 | for ann in unit: |
|
389 | 1 | if ann in worker: |
|
390 | 1 | vector[ann] = worker[ann] |
|
391 | else: |
||
392 | 1 | vector[ann] = 0 |
|
393 | 1 | return vector |
|
394 | |||
395 | # fill judgment vectors with unit keys |
||
396 | 1 | for index, row in judgments.iterrows(): |
|
397 | 1 | judgments.at[index, col] = expanded_vector(row[col], units.at[row['unit'], col]) |
|
398 | |||
399 | 1 | unit_work_ann_dict = judgments[['unit', 'worker', col]].copy().groupby('unit') |
|
400 | 1 | unit_work_ann_dict = {name : group.set_index('worker')[col].to_dict() \ |
|
401 | for name, group in unit_work_ann_dict} |
||
402 | |||
403 | 1 | work_unit_ann_dict = judgments[['worker', 'unit', col]].copy().groupby('worker') |
|
404 | 1 | work_unit_ann_dict = {name : group.set_index('unit')[col].to_dict() \ |
|
405 | for name, group in work_unit_ann_dict} |
||
406 | |||
407 | #initialize data structures |
||
408 | 1 | uqs_list = list() |
|
409 | 1 | wqs_list = list() |
|
410 | 1 | wwa_list = list() |
|
411 | 1 | wsa_list = list() |
|
412 | 1 | aqs_list = list() |
|
413 | |||
414 | 1 | uqs = dict((unit_id, 1.0) for unit_id in unit_work_ann_dict) |
|
415 | 1 | wqs = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
416 | 1 | wwa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
417 | 1 | wsa = dict((worker_id, 1.0) for worker_id in work_unit_ann_dict) |
|
418 | |||
419 | 1 | uqs_list.append(uqs.copy()) |
|
420 | 1 | wqs_list.append(wqs.copy()) |
|
421 | 1 | wwa_list.append(wwa.copy()) |
|
422 | 1 | wsa_list.append(wsa.copy()) |
|
423 | |||
424 | 1 | def init_aqs(config, unit_ann_dict): |
|
425 | """ initialize aqs depending on whether or not it is an open ended task """ |
||
426 | 1 | aqs = dict() |
|
427 | 1 | if not config.open_ended_task: |
|
428 | 1 | aqs_keys = list(unit_ann_dict[list(unit_ann_dict.keys())[0]].keys()) |
|
429 | 1 | for ann in aqs_keys: |
|
430 | 1 | aqs[ann] = 1.0 |
|
431 | else: |
||
432 | 1 | for unit_id in unit_ann_dict: |
|
433 | 1 | for ann in unit_ann_dict[unit_id]: |
|
434 | 1 | aqs[ann] = 1.0 |
|
435 | 1 | return aqs |
|
436 | |||
437 | 1 | aqs = init_aqs(config, unit_ann_dict) |
|
438 | 1 | aqs_list.append(aqs.copy()) |
|
439 | |||
440 | 1 | uqs_len = len(list(uqs.keys())) * 1.0 |
|
441 | 1 | wqs_len = len(list(wqs.keys())) * 1.0 |
|
442 | 1 | aqs_len = len(list(aqs.keys())) * 1.0 |
|
443 | |||
444 | # compute metrics until stable values |
||
445 | 1 | iterations = 0 |
|
446 | 1 | while max_delta >= 0.001: |
|
447 | 1 | uqs_new = dict() |
|
448 | 1 | wqs_new = dict() |
|
449 | 1 | wwa_new = dict() |
|
450 | 1 | wsa_new = dict() |
|
451 | |||
452 | 1 | avg_uqs_delta = 0.0 |
|
453 | 1 | avg_wqs_delta = 0.0 |
|
454 | 1 | avg_aqs_delta = 0.0 |
|
455 | 1 | max_delta = 0.0 |
|
456 | |||
457 | # pdb.set_trace() |
||
458 | |||
459 | 1 | def compute_wqs(wwa_new, wsa_new, wqs_new, work_unit_ann_dict, unit_ann_dict, \ |
|
460 | unit_work_ann_dict, wqs_list, uqs_list, aqs_list, wqs_len, \ |
||
461 | max_delta, avg_wqs_delta): |
||
462 | """ compute worker quality score (WQS) """ |
||
463 | 1 | for worker_id, _ in work_unit_ann_dict.items(): |
|
464 | 1 | wwa_new[worker_id] = Metrics.worker_worker_agreement( \ |
|
465 | worker_id, work_unit_ann_dict, \ |
||
466 | unit_work_ann_dict, \ |
||
467 | wqs_list[len(wqs_list) - 1], \ |
||
468 | uqs_list[len(uqs_list) - 1], \ |
||
469 | aqs_list[len(aqs_list) - 1]) |
||
470 | 1 | wsa_new[worker_id] = Metrics.worker_unit_agreement( \ |
|
471 | worker_id, \ |
||
472 | unit_ann_dict, \ |
||
473 | work_unit_ann_dict, \ |
||
474 | uqs_list[len(uqs_list) - 1], \ |
||
475 | aqs_list[len(aqs_list) - 1], \ |
||
476 | wqs_list[len(wqs_list) - 1][worker_id]) |
||
477 | 1 | wqs_new[worker_id] = wwa_new[worker_id] * wsa_new[worker_id] |
|
478 | 1 | max_delta = max(max_delta, \ |
|
479 | abs(wqs_new[worker_id] - wqs_list[len(wqs_list) - 1][worker_id])) |
||
480 | 1 | avg_wqs_delta += abs(wqs_new[worker_id] - \ |
|
481 | wqs_list[len(wqs_list) - 1][worker_id]) |
||
482 | 1 | avg_wqs_delta /= wqs_len |
|
483 | |||
484 | 1 | return wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta |
|
485 | |||
486 | 1 | def compute_aqs(aqs, work_unit_ann_dict, uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta): |
|
487 | """ compute annotation quality score (aqs) """ |
||
488 | 1 | aqs_new = Metrics.annotation_quality_score(list(aqs.keys()), work_unit_ann_dict, \ |
|
489 | uqs_list[len(uqs_list) - 1], \ |
||
490 | wqs_list[len(wqs_list) - 1]) |
||
491 | 1 | for ann, _ in aqs_new.items(): |
|
492 | 1 | max_delta = max(max_delta, abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann])) |
|
493 | 1 | avg_aqs_delta += abs(aqs_new[ann] - aqs_list[len(aqs_list) - 1][ann]) |
|
494 | 1 | avg_aqs_delta /= aqs_len |
|
495 | 1 | return aqs_new, max_delta, avg_aqs_delta |
|
496 | |||
497 | 1 | def compute_uqs(uqs_new, unit_work_ann_dict, wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta): |
|
498 | """ compute unit quality score (uqs) """ |
||
499 | 1 | for unit_id, _ in unit_work_ann_dict.items(): |
|
500 | 1 | uqs_new[unit_id] = Metrics.unit_quality_score(unit_id, unit_work_ann_dict, \ |
|
501 | wqs_list[len(wqs_list) - 1], \ |
||
502 | aqs_list[len(aqs_list) - 1]) |
||
503 | 1 | max_delta = max(max_delta, \ |
|
504 | abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id])) |
||
505 | 1 | avg_uqs_delta += abs(uqs_new[unit_id] - uqs_list[len(uqs_list) - 1][unit_id]) |
|
506 | 1 | avg_uqs_delta /= uqs_len |
|
507 | 1 | return uqs_new, max_delta, avg_uqs_delta |
|
508 | |||
509 | 1 | def reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new): |
|
510 | """ reconstruct unit_ann_dict with worker scores """ |
||
511 | 1 | new_unit_ann_dict = dict() |
|
512 | 1 | for unit_id, ann_dict in unit_ann_dict.items(): |
|
513 | 1 | new_unit_ann_dict[unit_id] = dict() |
|
514 | 1 | for ann, _ in ann_dict.items(): |
|
515 | 1 | new_unit_ann_dict[unit_id][ann] = 0.0 |
|
516 | 1 | for work_id, srd in work_unit_ann_dict.items(): |
|
517 | 1 | wqs_work_id = wqs_new[work_id] |
|
518 | 1 | for unit_id, ann_dict in srd.items(): |
|
519 | 1 | for ann, score in ann_dict.items(): |
|
520 | 1 | new_unit_ann_dict[unit_id][ann] += score * wqs_work_id |
|
521 | |||
522 | 1 | return new_unit_ann_dict |
|
523 | |||
524 | 1 | if not config.open_ended_task: |
|
525 | # compute annotation quality score (aqs) |
||
526 | 1 | aqs_new, max_delta, avg_aqs_delta = compute_aqs(aqs, work_unit_ann_dict, \ |
|
527 | uqs_list, wqs_list, aqs_list, aqs_len, max_delta, avg_aqs_delta) |
||
528 | |||
529 | # compute unit quality score (uqs) |
||
530 | 1 | uqs_new, max_delta, avg_uqs_delta = compute_uqs(uqs_new, unit_work_ann_dict, \ |
|
531 | wqs_list, aqs_list, uqs_list, uqs_len, max_delta, avg_uqs_delta) |
||
532 | |||
533 | # compute worker quality score (WQS) |
||
534 | 1 | wwa_new, wsa_new, wqs_new, max_delta, avg_wqs_delta = compute_wqs(\ |
|
535 | wwa_new, wsa_new, wqs_new, \ |
||
536 | work_unit_ann_dict, unit_ann_dict, unit_work_ann_dict, wqs_list, \ |
||
537 | uqs_list, aqs_list, wqs_len, max_delta, avg_wqs_delta) |
||
538 | |||
539 | # save results for current iteration |
||
540 | 1 | uqs_list.append(uqs_new.copy()) |
|
541 | 1 | wqs_list.append(wqs_new.copy()) |
|
542 | 1 | wwa_list.append(wwa_new.copy()) |
|
543 | 1 | wsa_list.append(wsa_new.copy()) |
|
544 | 1 | if not config.open_ended_task: |
|
545 | 1 | aqs_list.append(aqs_new.copy()) |
|
|
|||
546 | 1 | iterations += 1 |
|
547 | |||
548 | 1 | unit_ann_dict = reconstruct_unit_ann_dict(unit_ann_dict, work_unit_ann_dict, wqs_new) |
|
549 | |||
550 | 1 | logging.info(str(iterations) + " iterations; max d= " + str(max_delta) + \ |
|
551 | " ; wqs d= " + str(avg_wqs_delta) + "; uqs d= " + str(avg_uqs_delta) + \ |
||
552 | "; aqs d= " + str(avg_aqs_delta)) |
||
553 | |||
554 | 1 | def save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, iteration_value): |
|
555 | """ save the unit annotation score for print """ |
||
556 | 1 | uas = Counter() |
|
557 | 1 | for unit_id in unit_ann_dict: |
|
558 | 1 | uas[unit_id] = Counter() |
|
559 | 1 | for ann in unit_ann_dict[unit_id]: |
|
560 | 1 | uas[unit_id][ann] = Metrics.unit_annotation_score(unit_id, \ |
|
561 | ann, unit_work_ann_dict, \ |
||
562 | iteration_value) |
||
563 | 1 | return uas |
|
564 | |||
565 | 1 | uas = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[len(wqs_list) - 1]) |
|
566 | 1 | uas_initial = save_unit_ann_score(unit_ann_dict, unit_work_ann_dict, wqs_list[0]) |
|
567 | |||
568 | 1 | results['units']['uqs'] = pd.Series(uqs_list[-1]) |
|
569 | 1 | results['units']['unit_annotation_score'] = pd.Series(uas) |
|
570 | 1 | results['workers']['wqs'] = pd.Series(wqs_list[-1]) |
|
571 | 1 | results['workers']['wwa'] = pd.Series(wwa_list[-1]) |
|
572 | 1 | results['workers']['wsa'] = pd.Series(wsa_list[-1]) |
|
573 | 1 | if not config.open_ended_task: |
|
574 | 1 | results['annotations']['aqs'] = pd.Series(aqs_list[-1]) |
|
575 | |||
576 | 1 | results['units']['uqs_initial'] = pd.Series(uqs_list[1]) |
|
577 | 1 | results['units']['unit_annotation_score_initial'] = pd.Series(uas_initial) |
|
578 | 1 | results['workers']['wqs_initial'] = pd.Series(wqs_list[1]) |
|
579 | 1 | results['workers']['wwa_initial'] = pd.Series(wwa_list[1]) |
|
580 | 1 | results['workers']['wsa_initial'] = pd.Series(wsa_list[1]) |
|
581 | 1 | if not config.open_ended_task: |
|
582 | 1 | results['annotations']['aqs_initial'] = pd.Series(aqs_list[1]) |
|
583 | return results |
||
584 |