Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Fixing proportion plot logic
Browse filesValue was exceeding 1 and I think it's calcCategories problem
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 |
-
|
| 459 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
"""
|
| 461 |
speakerRenames = speakerRenames or {}
|
| 462 |
-
df6_dict
|
| 463 |
allCategories = copy.deepcopy(categories)
|
| 464 |
|
|
|
|
| 465 |
for fn in validNames:
|
| 466 |
currAnnotation, _ = results[fn]
|
| 467 |
prefix = fn + ": "
|
| 468 |
-
|
| 469 |
-
|
| 470 |
for tokens in categorySelect
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 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 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
|
| 486 |
for category in categories:
|
| 487 |
df6_dict.setdefault(category, [])
|
| 488 |
|
|
|
|
| 489 |
for fn in validNames:
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
for category in allCategories:
|
| 508 |
-
if category
|
| 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
|