Domain: Business Operations & Analytics

Using the local files `Books_df_unclean.csv`, `Genre_df_unclean.csv`, and `Sub_Genre_df_unclean.csv`, compute a deterministic main-genre stress ranking.

Apply these rules exactly.

1. Read all three CSV files as raw strings.
2. Restrict books to rows with non-empty `Title` and non-empty `Author` after trimming whitespace.
3. Canonicalize text with this exact procedure:
   - lowercase
   - trim outer whitespace
   - replace `&` with ` and `, `@` with `a`, and `|` with `l`
   - apply these fixed typo substitutions before punctuation stripping:
     - `photoiraphy -> photography`
     - `photoraphy -> photography`
     - `photograpphy -> photography`
     - `photorgaphy -> photography`
     - `photogarphy -> photography`
     - `photograpyh -> photography`
     - `phtoography -> photography`
     - `phoography -> photography`
     - `photogaphy -> photography`
     - `phhtography -> photography`
     - `photogrpahy -> photography`
     - `photograpmy -> photography`
     - `eonomics -> economics`
     - `boosk -> books`
     - `children sbooks -> children s books`
     - `childrenns -> childrens`
     - `fiml -> film`
   - replace every maximal run of non-alphanumeric characters with one space
   - collapse internal whitespace to single spaces
   - collapse repeated characters with the regex rule `(.)\1+ -> \1`
4. Parse numeric values with the first regex match of `[-+]?\d*\.?\d+` after removing commas. If no match exists, the parsed value is null.
5. A URL is valid iff its trimmed text matches `^https?://`.
6. Extract IDs as follows:
   - for books: from `URLs`, extract `zg_bs_g_(\d+)`
   - for genres and sub-genres: from the URL field, extract `/bestsellers/books/(\d+)`
7. Deduplicate `Genre_df_unclean.csv` by canonicalized title key after sorting by:
   - canonical title ascending
   - row quality descending, where `qc = 10 * url_valid + 3 * has_numeric_subgenre_count + 2 * has_extracted_genre_id`
   - numeric `Number of Sub-genres` descending
   - extracted genre id ascending
   - original title ascending
8. Build the canonical resolved genre lookup set from those deduplicated genre keys.
9. Resolve each raw main-genre key to the nearest deduplicated genre key using `difflib.SequenceMatcher(...).ratio()`, breaking score ties lexicographically.
10. Deduplicate `Sub_Genre_df_unclean.csv` twice:
   - by extracted sub-genre id after sorting by `sub_id`, canonical sub key, row quality descending, numeric `No. of Books` descending, resolved main-genre key ascending, and original title ascending
   - by `(canonical sub key, resolved main-genre key)` after sorting by those keys, row quality descending, numeric `No. of Books` descending, then `sub_id` ascending
11. For sub-genre row quality use:
   - `qc = 10 * url_valid + 3 * has_sub_id + 2 * has_numeric_book_count + 1 * has_nonempty_resolved_main_genre_key`
12. Mark `id_multititle_ids` as extracted sub-genre ids that map to more than one canonical sub-genre title in the raw sub-genre table.
13. Deduplicate books by `(canonical title key, canonical author key, extracted sub id)` after sorting by:
   - canonical title key ascending
   - canonical author key ascending
   - extracted sub id ascending
   - row quality descending, where `qc = 10 * url_valid + 2 * has_price + 2 * has_rating + 2 * has_people_rated + 2 * has_extracted_sub_id`
   - numeric people rated descending
   - numeric rating descending
   - numeric price descending
   - original `URLs` ascending lexicographically
14. Link deduplicated books to the deduplicated sub-genre-by-id table on extracted sub id.
15. For unresolved rows only, attempt fallback linkage on `(canonical sub key, resolved main-genre key)` against the fallback sub-genre table.
16. Resolve the final main genre as linked sub-genre main genre when present, otherwise the book's resolved main genre.
17. Attach the deduplicated genre-level `Number of Sub-genres` count by resolved main genre.
18. Build row-level flags:
   - `id_inconsistent = 1` if either the linked sub-genre key disagrees with the book sub key when both are non-empty and an extracted sub id is present, or the extracted sub id belongs to `id_multititle_ids`
   - `sign_reversal = 1` if any parsed numeric field among people rated, price, rating, sub-genre book count, or genre sub-genre count is negative
   - `null_coercion = 1` if any non-empty `Price`, `Rating`, or `No. of People rated` fails numeric parsing, or a linked/extracted sub id exists but `No. of Books` is missing numerically, or a resolved main genre exists but `Number of Sub-genres` is missing numerically
   - `mixed_ts = 1` if the row itself matches multiple timestamp classes, or if the row has a non-`none` timestamp class and its resolved main genre has more than one timestamp class among its rows
   - `outlier_or_anomaly = 1` if the row is a robust price outlier within its extracted sub id, or rating is outside `[0, 5]`, or people rated is above the global 99.9th percentile using Type 7 interpolation
   - `fuzzy_genre_link = 1` if the raw canonical main-genre key is non-empty and differs from its nearest resolved genre key
   - `fallback_join = 1` if the join method is not direct id linkage
19. Timestamp classes are detected over the concatenated `Title` and `URLs` text with these regex families:
   - `ymd`: `\b\d{4}-\d{1,2}-\d{1,2}\b`
   - `dmy`: `\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b`
   - `monthday`: month-name followed by day and optional year
   - `monthyear`: month-name followed by year
   - `none` when no pattern matches
   - `mixedrow` when more than one family matches in the same row
20. Use robust z-score for price outliers within extracted sub id:
   - median by extracted sub id
   - MAD by extracted sub id
   - `robust_z = abs(price - median) / (1.4826 * MAD)`
   - price outlier iff `robust_z > 3.5`
21. Compute `error_score = 3 * id_inconsistent + 2 * sign_reversal + 2 * null_coercion + 2 * mixed_ts + outlier_or_anomaly + fuzzy_genre_link + fallback_join`.
22. Aggregate by resolved main genre and keep only non-empty resolved main genres.
23. For each resolved main genre compute:
   - `books_used`
   - `err_total`
   - `id_inconsistent`
   - `sign_reversal`
   - `null_coercion`
   - `mixed_ts`
   - `outlier_or_anomaly`
   - `fuzzy_genre_link`
   - `fallback_join`
   - `err_rate = err_total / books_used`
24. Rank rows by:
   - `err_total` descending
   - `err_rate` descending
   - `books_used` descending
   - resolved main genre ascending
25. Return the top 10 rows only.
26. Format `err_rate` as a string with exactly 6 decimal places using round-half-to-even.
27. Define `row_sig12` as the first 12 lowercase hexadecimal characters of `md5` over the pipe-joined preceding 11 fields in the output row.

Return exactly one Python list of 10 inner lists. Each inner list must be:

`[resolved_main_genre, books_used, err_total, err_rate_6dp, id_inconsistent, sign_reversal, null_coercion, mixed_ts, outlier_or_anomaly, fuzzy_genre_link, fallback_join, row_sig12]`

Print only that final Python list.

Grading rubric:
criterion: The final answer is a Python list with exactly 10 inner lists, and every inner list has exactly 12 elements.
weight: 10
criterion: The row at index 0 is exactly `['children s boks', 1364, 3387, '2.483138', 506, 218, 442, 0, 302, 177, 70, 'a9d9df4ef0d6']`.
weight: 15
criterion: The row at index 1 is exactly `['romane', 993, 2994, '3.015106', 329, 161, 232, 0, 180, 986, 55, '9410d1c794a9']`.
weight: 15
criterion: The row at index 9 is exactly `['society and social sciences', 184, 606, '3.293478', 36, 29, 184, 0, 43, 15, 14, 'b7e630bdd393']`.
weight: 15
criterion: The complete top-10 table includes these exact middle rows in order: `['sports', 702, 2687, '3.827635', 238, 128, 702, 0, 155, 101, 57, 'f40b93f8c319']`, `['literature and fiction', 373, 1037, '2.780161', 153, 61, 162, 0, 78, 48, 6, '601c5b25c792']`, `['historical fiction', 305, 806, '2.642623', 103, 41, 108, 0, 52, 147, 0, '9668f62996d5']`, `['arts film and photography', 187, 739, '3.951872', 78, 34, 187, 0, 36, 19, 8, '4ddc58c982fa']`, `['medicine and health sciences', 239, 650, '2.719665', 73, 32, 119, 0, 51, 37, 41, '2f44d500b657']`, `['computing internet and digital media', 332, 641, '1.930723', 100, 51, 42, 2, 78, 30, 43, 'c5ba7bb21c2d']`, and `['crafs home and lifestyle', 241, 629, '2.609959', 42, 32, 65, 0, 51, 241, 17, 'a412cf47fb21']`.
weight: 20
criterion: The sum of `err_total` across the 10 rows is exactly `14176`, and the sum of `books_used` across the 10 rows is exactly `4920`.
weight: 10
criterion: The MD5 of the serialized table string `'||'.join('|'.join(map(str, row)) for row in rows)` is exactly `3d8ec0655fed734ee644a93526aac367`, and the stability checksum `sum(id_inconsistent*1000000 + null_coercion*1000 + mixed_ts*10 + fallback_join)` is exactly `1660243331`.
weight: 15

Golden answer:
# Code

```python
import csv
import hashlib
import math
import re
from difflib import SequenceMatcher
from pathlib import Path
from statistics import median


BASE = Path(__file__).resolve().parent


def read_csv(name):
    with (BASE / name).open(newline="", encoding="utf-8") as handle:
        return list(csv.DictReader(handle))


def canon_text(value):
    if value is None:
        return ""
    text = str(value).lower().strip()
    text = text.replace("&", " and ").replace("@", "a").replace("|", "l")
    replacements = {
        "photoiraphy": "photography",
        "photoraphy": "photography",
        "photograpphy": "photography",
        "photorgaphy": "photography",
        "photogarphy": "photography",
        "photograpyh": "photography",
        "phtoography": "photography",
        "phoography": "photography",
        "photogaphy": "photography",
        "phhtography": "photography",
        "photogrpahy": "photography",
        "photograpmy": "photography",
        "eonomics": "economics",
        "boosk": "books",
        "children sbooks": "children s books",
        "childrenns": "childrens",
        "fiml": "film",
    }
    for source, target in replacements.items():
        text = text.replace(source, target)
    text = re.sub(r"[^a-z0-9]+", " ", text)
    text = " ".join(text.split())
    text = re.sub(r"(.)\1+", r"\1", text)
    return text


def parse_num(value):
    if value is None:
        return None
    text = str(value).strip().replace(",", "")
    if text == "":
        return None
    match = re.search(r"[-+]?\d*\.?\d+", text)
    if not match:
        return None
    try:
        return float(match.group(0))
    except ValueError:
        return None


def valid_url(value):
    if value is None:
        return 0
    return int(bool(re.match(r"^https?://", str(value).strip())))


def extract_book_sub_id(value):
    if value is None:
        return None
    match = re.search(r"zg_bs_g_(\d+)", str(value))
    return match.group(1) if match else None


def extract_sub_id(value):
    if value is None:
        return None
    match = re.search(r"/bestsellers/books/(\d+)", str(value))
    return match.group(1) if match else None


books = read_csv("Books_df_unclean.csv")
genres = read_csv("Genre_df_unclean.csv")
sub_genres = read_csv("Sub_Genre_df_unclean.csv")

eligible_books = [
    row for row in books if row["Title"].strip() != "" and row["Author"].strip() != ""
]

for row in genres:
    row["genre_key_raw"] = canon_text(row["Title"])
    row["genre_id"] = extract_sub_id(row["URL"])
    row["n_sub_num"] = parse_num(row["Number of Sub-genres"])
    row["url_valid"] = valid_url(row["URL"])
    row["qc"] = row["url_valid"] * 10 + int(row["n_sub_num"] is not None) * 3 + int(row["genre_id"] is not None) * 2

genres_sorted = sorted(
    genres,
    key=lambda row: (
        row["genre_key_raw"],
        -row["qc"],
        -(row["n_sub_num"] if row["n_sub_num"] is not None else float("-inf")),
        row["genre_id"] if row["genre_id"] is not None else "",
        row["Title"],
    ),
)

dedup_genres = []
seen_genre_keys = set()
for row in genres_sorted:
    if row["genre_key_raw"] not in seen_genre_keys:
        seen_genre_keys.add(row["genre_key_raw"])
        dedup_genres.append(row)

resolved_genre_keys = sorted({row["genre_key_raw"] for row in dedup_genres if row["genre_key_raw"]})


def nearest_genre_key(key):
    if key == "":
        return ""
    if key in resolved_genre_keys:
        return key
    best_key = None
    best_score = -1.0
    for candidate in resolved_genre_keys:
        score = SequenceMatcher(None, key, candidate).ratio()
        if (score > best_score) or (abs(score - best_score) < 1e-12 and (best_key is None or candidate < best_key)):
            best_key = candidate
            best_score = score
    return best_key if best_key is not None else ""


for row in sub_genres:
    row["sub_key"] = canon_text(row["Title"])
    row["main_genre_raw_key"] = canon_text(row["Main Genre"])
    row["main_genre_key"] = nearest_genre_key(row["main_genre_raw_key"])
    row["sub_id"] = extract_sub_id(row["URLs"])
    row["url_valid"] = valid_url(row["URLs"])
    row["n_books_num"] = parse_num(row["No. of Books"])
    row["qc"] = row["url_valid"] * 10 + int(row["sub_id"] is not None) * 3 + int(row["n_books_num"] is not None) * 2 + int(row["main_genre_key"] != "")

sub_by_id_sorted = sorted(
    sub_genres,
    key=lambda row: (
        row["sub_id"] if row["sub_id"] is not None else "",
        row["sub_key"],
        -row["qc"],
        -(row["n_books_num"] if row["n_books_num"] is not None else float("-inf")),
        row["main_genre_key"],
        row["Title"],
    ),
)

dedup_sub_by_id = []
seen_sub_ids = set()
for row in sub_by_id_sorted:
    if row["sub_id"] is not None and row["sub_id"] not in seen_sub_ids:
        seen_sub_ids.add(row["sub_id"])
        dedup_sub_by_id.append(row)

sub_fallback_sorted = sorted(
    sub_genres,
    key=lambda row: (
        row["sub_key"],
        row["main_genre_key"],
        -row["qc"],
        -(row["n_books_num"] if row["n_books_num"] is not None else float("-inf")),
        row["sub_id"] if row["sub_id"] is not None else "",
    ),
)

dedup_sub_fallback = []
seen_sub_fallback_keys = set()
for row in sub_fallback_sorted:
    key = (row["sub_key"], row["main_genre_key"])
    if key not in seen_sub_fallback_keys:
        seen_sub_fallback_keys.add(key)
        dedup_sub_fallback.append(row)

sub_title_counts = {}
for row in sub_genres:
    if row["sub_id"] is not None:
        sub_title_counts.setdefault(row["sub_id"], set()).add(row["sub_key"])
id_multititle_ids = {sub_id for sub_id, titles in sub_title_counts.items() if len(titles) > 1}

for row in eligible_books:
    row["title_key"] = canon_text(row["Title"])
    row["author_key"] = canon_text(row["Author"])
    row["main_genre_raw_key"] = canon_text(row["Main Genre"])
    row["main_genre_key"] = nearest_genre_key(row["main_genre_raw_key"])
    row["sub_key"] = canon_text(row["Sub Genre"])
    row["sub_id"] = extract_book_sub_id(row["URLs"])
    row["url_valid"] = valid_url(row["URLs"])
    row["price_num"] = parse_num(row["Price"])
    row["rating_num"] = parse_num(row["Rating"])
    row["people_num"] = parse_num(row["No. of People rated"])
    row["qc"] = row["url_valid"] * 10 + int(row["price_num"] is not None) * 2 + int(row["rating_num"] is not None) * 2 + int(row["people_num"] is not None) * 2 + int(row["sub_id"] is not None) * 2

books_sorted = sorted(
    eligible_books,
    key=lambda row: (
        row["title_key"],
        row["author_key"],
        row["sub_id"] if row["sub_id"] is not None else "",
        -row["qc"],
        -(row["people_num"] if row["people_num"] is not None else float("-inf")),
        -(row["rating_num"] if row["rating_num"] is not None else float("-inf")),
        -(row["price_num"] if row["price_num"] is not None else float("-inf")),
        row["URLs"],
    ),
)

dedup_books = []
seen_book_keys = set()
for row in books_sorted:
    key = (row["title_key"], row["author_key"], row["sub_id"])
    if key not in seen_book_keys:
        seen_book_keys.add(key)
        dedup_books.append(row)

sub_by_id_lookup = {row["sub_id"]: row for row in dedup_sub_by_id}
sub_fallback_lookup = {(row["sub_key"], row["main_genre_key"]): row for row in dedup_sub_fallback}
genre_count_lookup = {row["genre_key_raw"]: row["n_sub_num"] for row in dedup_genres}

linked_rows = []
for row in dedup_books:
    merged = dict(row)
    sub_match = sub_by_id_lookup.get(row["sub_id"])
    merged["join_method"] = "id"
    if sub_match is not None:
        merged["sub_key_sub"] = sub_match["sub_key"]
        merged["main_genre_key_sub"] = sub_match["main_genre_key"]
        merged["n_books_num"] = sub_match["n_books_num"]
    else:
        merged["sub_key_sub"] = None
        merged["main_genre_key_sub"] = None
        merged["n_books_num"] = None
        fallback = sub_fallback_lookup.get((row["sub_key"], row["main_genre_key"]))
        if fallback is not None:
            merged["sub_key_sub"] = fallback["sub_key"]
            merged["main_genre_key_sub"] = fallback["main_genre_key"]
            merged["n_books_num"] = fallback["n_books_num"]
            if merged["sub_id"] is None:
                merged["sub_id"] = fallback["sub_id"]
            merged["join_method"] = "fallback_sub_key_genre"
    merged["genre_key_resolved"] = merged["main_genre_key_sub"] if merged["main_genre_key_sub"] is not None else merged["main_genre_key"]
    merged["n_sub_num"] = genre_count_lookup.get(merged["genre_key_resolved"])
    linked_rows.append(merged)

pattern_ymd = re.compile(r"\b\d{4}-\d{1,2}-\d{1,2}\b")
pattern_dmy = re.compile(r"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b")
pattern_monthday = re.compile(r"\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*[\s\-]\d{1,2}(?:,\s*\d{2,4})?\b", re.I)
pattern_monthyear = re.compile(r"\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\s+\d{4}\b", re.I)


def ts_class(title, url):
    text = f"{title} {url}"
    classes = []
    if pattern_ymd.search(text):
        classes.append("ymd")
    if pattern_dmy.search(text):
        classes.append("dmy")
    if pattern_monthday.search(text):
        classes.append("monthday")
    if pattern_monthyear.search(text):
        classes.append("monthyear")
    if not classes:
        return "none"
    if len(set(classes)) > 1:
        return "mixedrow"
    return classes[0]


for row in linked_rows:
    row["id_inconsistent"] = int(
        (
            row["sub_id"] is not None
            and row["sub_key_sub"] is not None
            and row["sub_key"] != ""
            and row["sub_key"] != row["sub_key_sub"]
        )
        or row["sub_id"] in id_multititle_ids
    )
    row["sign_reversal"] = int(any(value is not None and value < 0 for value in [row["people_num"], row["price_num"], row["rating_num"], row["n_books_num"], row["n_sub_num"]]))
    row["null_coercion"] = int(
        ((row["Price"].strip() != "") and row["price_num"] is None)
        or ((row["Rating"].strip() != "") and row["rating_num"] is None)
        or ((row["No. of People rated"].strip() != "") and row["people_num"] is None)
        or (row["n_books_num"] is None and row["sub_id"] is not None)
        or (row["n_sub_num"] is None and row["genre_key_resolved"] is not None)
    )
    row["ts_class"] = ts_class(row["Title"], row["URLs"])

genre_ts_classes = {}
for row in linked_rows:
    if row["ts_class"] != "none":
        genre_ts_classes.setdefault(row["genre_key_resolved"], set()).add(row["ts_class"])
mixed_genres = {genre_key for genre_key, classes in genre_ts_classes.items() if len(classes) > 1}

prices_by_sub = {}
for row in linked_rows:
    prices_by_sub.setdefault(row["sub_id"], []).append(row["price_num"])

sub_medians = {}
sub_mads = {}
for sub_id, values in prices_by_sub.items():
    clean_values = [value for value in values if value is not None]
    if clean_values:
        center = median(clean_values)
        sub_medians[sub_id] = center
        sub_mads[sub_id] = median(abs(value - center) for value in clean_values)
    else:
        sub_medians[sub_id] = None
        sub_mads[sub_id] = None

people_values = sorted(value for value in (row["people_num"] for row in linked_rows) if value is not None)
position = (len(people_values) - 1) * 0.999
lower = math.floor(position)
upper = math.ceil(position)
if lower == upper:
    p999 = people_values[lower]
else:
    fraction = position - lower
    p999 = people_values[lower] * (1 - fraction) + people_values[upper] * fraction

for row in linked_rows:
    row["mixed_ts"] = int(row["ts_class"] == "mixedrow" or (row["ts_class"] != "none" and row["genre_key_resolved"] in mixed_genres))
    robust_z = None
    sub_median = sub_medians.get(row["sub_id"])
    sub_mad = sub_mads.get(row["sub_id"])
    if row["price_num"] is not None and sub_median is not None and sub_mad not in (None, 0):
        robust_z = abs(row["price_num"] - sub_median) / (1.4826 * sub_mad)
    row["price_outlier"] = int(robust_z is not None and robust_z > 3.5)
    row["rating_anomaly"] = int(row["rating_num"] is not None and (row["rating_num"] < 0 or row["rating_num"] > 5))
    row["outlier_or_anomaly"] = int(row["price_outlier"] == 1 or row["rating_anomaly"] == 1 or (row["people_num"] is not None and row["people_num"] > p999))
    row["fuzzy_genre_link"] = int(row["main_genre_raw_key"] != "" and row["main_genre_raw_key"] != row["main_genre_key"])
    row["fallback_join"] = int(row["join_method"] != "id")
    row["error_score"] = 3 * row["id_inconsistent"] + 2 * row["sign_reversal"] + 2 * row["null_coercion"] + 2 * row["mixed_ts"] + row["outlier_or_anomaly"] + row["fuzzy_genre_link"] + row["fallback_join"]

aggregated = {}
for row in linked_rows:
    genre_key = row["genre_key_resolved"] or ""
    bucket = aggregated.setdefault(
        genre_key,
        {
            "books_used": 0,
            "err_total": 0,
            "id_inconsistent": 0,
            "sign_reversal": 0,
            "null_coercion": 0,
            "mixed_ts": 0,
            "outlier_or_anomaly": 0,
            "fuzzy_genre_link": 0,
            "fallback_join": 0,
        },
    )
    bucket["books_used"] += 1
    bucket["err_total"] += row["error_score"]
    bucket["id_inconsistent"] += row["id_inconsistent"]
    bucket["sign_reversal"] += row["sign_reversal"]
    bucket["null_coercion"] += row["null_coercion"]
    bucket["mixed_ts"] += row["mixed_ts"]
    bucket["outlier_or_anomaly"] += row["outlier_or_anomaly"]
    bucket["fuzzy_genre_link"] += row["fuzzy_genre_link"]
    bucket["fallback_join"] += row["fallback_join"]

ranked = []
for genre_key, bucket in aggregated.items():
    if genre_key == "":
        continue
    err_rate = bucket["err_total"] / bucket["books_used"]
    ranked.append(
        [
            genre_key,
            bucket["books_used"],
            bucket["err_total"],
            f"{err_rate:.6f}",
            bucket["id_inconsistent"],
            bucket["sign_reversal"],
            bucket["null_coercion"],
            bucket["mixed_ts"],
            bucket["outlier_or_anomaly"],
            bucket["fuzzy_genre_link"],
            bucket["fallback_join"],
        ]
    )

ranked.sort(key=lambda row: (-row[2], -float(row[3]), -row[1], row[0]))
top_10 = ranked[:10]

for row in top_10:
    row.append(hashlib.md5("|".join(map(str, row)).encode("utf-8")).hexdigest()[:12])

print(top_10)
```

# Reasoning

The code reads the three raw CSV files, canonicalizes text with the specified typo fixes and repeated-letter collapse, extracts numeric values and ids with regex rules, and performs deterministic deduplication for genres, sub-genres, and books. Books are linked first by extracted sub-genre id and then, only when unresolved, by `(canonical sub key, resolved main-genre key)`. The resolved main genre is then used to attach the genre-level sub-genre count and to aggregate error counts.

Each row receives the required flags: inconsistent id, sign reversal, null coercion, mixed timestamp class, outlier or anomaly, fuzzy genre link, and fallback join. The final `error_score` is aggregated by resolved main genre. The top ten rows are ranked by `err_total`, then `err_rate`, then `books_used`, then genre name. `err_rate` is formatted to six decimals with Python formatting, which uses round-half-to-even, and `row_sig12` is the first 12 lowercase hex characters of the MD5 hash of the pipe-joined first 11 output fields. Because the prompt explicitly restricts books to non-empty trimmed `Title` and `Author`, the verified output below reflects that stricter filtered input rather than the stale draft table.

# Verified Result

```python
[['children s boks', 1364, 3387, '2.483138', 506, 218, 442, 0, 302, 177, 70, 'a9d9df4ef0d6'], ['romane', 993, 2994, '3.015106', 329, 161, 232, 0, 180, 986, 55, '9410d1c794a9'], ['sports', 702, 2687, '3.827635', 238, 128, 702, 0, 155, 101, 57, 'f40b93f8c319'], ['literature and fiction', 373, 1037, '2.780161', 153, 61, 162, 0, 78, 48, 6, '601c5b25c792'], ['historical fiction', 305, 806, '2.642623', 103, 41, 108, 0, 52, 147, 0, '9668f62996d5'], ['arts film and photography', 187, 739, '3.951872', 78, 34, 187, 0, 36, 19, 8, '4ddc58c982fa'], ['medicine and health sciences', 239, 650, '2.719665', 73, 32, 119, 0, 51, 37, 41, '2f44d500b657'], ['computing internet and digital media', 332, 641, '1.930723', 100, 51, 42, 2, 78, 30, 43, 'c5ba7bb21c2d'], ['crafs home and lifestyle', 241, 629, '2.609959', 42, 32, 65, 0, 51, 241, 17, 'a412cf47fb21'], ['society and social sciences', 184, 606, '3.293478', 36, 29, 184, 0, 43, 15, 14, 'b7e630bdd393']]
```