duongthienz commited on
Commit
a504eda
·
verified ·
1 Parent(s): 52bb804

Fixing proportion plot logic

Browse files

Value was exceeding 1 and I think it's calcCategories problem

Files changed (1) hide show
  1. utils.py +59 -36
utils.py CHANGED
@@ -455,57 +455,80 @@ def build_multifile_category_df(validNames, results, summaries, categories, cate
455
  speakerRenames=None):
456
  """Build df6 (category breakdown per file) for the multi-file expander.
457
 
458
- speakerRenames: {filename: {raw_sp: display_name}} used to apply display
459
- names to unassigned speaker columns (extraCats).
 
 
 
 
 
 
 
460
  """
461
  speakerRenames = speakerRenames or {}
462
- df6_dict = {"files": validNames}
463
  allCategories = copy.deepcopy(categories)
464
 
 
465
  for fn in validNames:
466
  currAnnotation, _ = results[fn]
467
  prefix = fn + ": "
468
- per_file_selections = [
469
- [t[len(prefix):] for t in tokens if t.startswith(prefix)]
470
  for tokens in categorySelect
471
- ]
472
- try:
473
- catSummary, extraCats = su.calcCategories(currAnnotation, per_file_selections)
474
- except (UnboundLocalError, Exception):
475
- summaries[fn]["categories"] = ([], [])
476
- continue
477
- # Apply display names to raw speaker IDs in extraCats
478
  renames = speakerRenames.get(fn, {})
479
- extraCats = [renames.get(sp, sp) for sp in extraCats]
480
- summaries[fn]["categories"] = (catSummary, extraCats)
481
- for extra in extraCats:
482
- df6_dict.setdefault(extra, [])
483
- if extra not in allCategories:
484
- allCategories.append(extra)
485
 
486
  for category in categories:
487
  df6_dict.setdefault(category, [])
488
 
 
489
  for fn in validNames:
490
- summary, extras = summaries[fn]["categories"]
491
- # If this file was skipped in the first loop (calcCategories failed),
492
- # summary will be [] — fill every category column with 0 so all
493
- # arrays stay the same length.
494
- if not summary:
495
- for category in allCategories:
496
- df6_dict[category].append(0)
497
- continue
498
- theseCategories = categories + extras
499
- annotation_end = max(
500
- (s.end for s in results[fn][0].itersegments()),
501
- default=results[fn][1]
502
- )
503
- safe_total = max(annotation_end, results[fn][1], 1)
504
- for j, timeSlots in enumerate(summary):
505
- val = sum(t.duration for _, t in timeSlots) / safe_total
506
- df6_dict[theseCategories[j]].append(min(val, 1.0))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  for category in allCategories:
508
- if category not in theseCategories:
509
  df6_dict[category].append(0)
510
 
511
  return pd.DataFrame(df6_dict), allCategories
 
455
  speakerRenames=None):
456
  """Build df6 (category breakdown per file) for the multi-file expander.
457
 
458
+ Uses su.sumTimes() per speaker (same as the single-file charts) so that:
459
+ - Each speaker's time = union of their segments (overlaps within one speaker
460
+ are merged by get_timeline().duration())
461
+ - Multiple speakers in the same role are subset-unioned before summing so
462
+ cross-speaker overlaps within a role are counted only once
463
+ - Values are proportions (0-1) of the file's total duration
464
+
465
+ speakerRenames: {filename: {raw_sp: display_name}} — applied to unassigned
466
+ speaker column headers.
467
  """
468
  speakerRenames = speakerRenames or {}
469
+ df6_dict = {"files": validNames}
470
  allCategories = copy.deepcopy(categories)
471
 
472
+ # First pass: discover unassigned speaker columns across all files
473
  for fn in validNames:
474
  currAnnotation, _ = results[fn]
475
  prefix = fn + ": "
476
+ assigned = {
477
+ t[len(prefix):]
478
  for tokens in categorySelect
479
+ for t in tokens
480
+ if t.startswith(prefix)
481
+ }
 
 
 
 
482
  renames = speakerRenames.get(fn, {})
483
+ for sp in currAnnotation.labels():
484
+ if sp not in assigned:
485
+ display = renames.get(sp, sp)
486
+ if display not in allCategories:
487
+ allCategories.append(display)
488
+ df6_dict.setdefault(display, [])
489
 
490
  for category in categories:
491
  df6_dict.setdefault(category, [])
492
 
493
+ # Second pass: compute proportions per file
494
  for fn in validNames:
495
+ currAnnotation, totalSeconds = results[fn]
496
+ safe_total = max(totalSeconds, 1)
497
+ prefix = fn + ": "
498
+ renames = speakerRenames.get(fn, {})
499
+
500
+ # For each role: union all assigned speakers into one subset, then sum.
501
+ # su.sumTimes uses get_timeline(False).duration() which merges overlaps.
502
+ for i, category in enumerate(categories):
503
+ assigned_sps = [
504
+ t[len(prefix):]
505
+ for t in categorySelect[i]
506
+ if t.startswith(prefix)
507
+ ] if i < len(categorySelect) else []
508
+ # Filter to speakers that actually exist in this annotation
509
+ valid_sps = [sp for sp in assigned_sps if sp in currAnnotation.labels()]
510
+ if valid_sps:
511
+ val = su.sumTimes(currAnnotation.subset(valid_sps)) / safe_total
512
+ else:
513
+ val = 0.0
514
+ df6_dict[category].append(min(val, 1.0))
515
+
516
+ # For unassigned speakers: each gets their own column
517
+ assigned_all = {
518
+ t[len(prefix):]
519
+ for tokens in categorySelect
520
+ for t in tokens
521
+ if t.startswith(prefix)
522
+ }
523
+ unassigned = [sp for sp in currAnnotation.labels() if sp not in assigned_all]
524
+ for sp in unassigned:
525
+ display = renames.get(sp, sp)
526
+ val = su.sumTimes(currAnnotation.subset([sp])) / safe_total
527
+ df6_dict[display].append(min(val, 1.0))
528
+
529
+ # Fill 0 for any extra columns this file doesn't have
530
  for category in allCategories:
531
+ if len(df6_dict[category]) < len(df6_dict["files"]):
532
  df6_dict[category].append(0)
533
 
534
  return pd.DataFrame(df6_dict), allCategories