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