Typorithm · medium

Unique labels, inverse indexes and grouped counts

pythonnumpynumerical-computingindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Unique labels, inverse indexes and grouped counts

0%

Concept and contract

Aggregate repeated labels without separating each measurement from its owning group.

Grouping needs a mapping from each observation to a group, not just a list of distinct labels. unique can return sorted labels, an inverse index for each input position, and a count per group. The inverse indexes are nonnegative integer group codes, which can then drive bincount for aligned weighted sums. This lesson reports counts and total synthetic hours for each label.

Group counts describe observations in the supplied input, not the entire labor market. Duplicate source records and inconsistent label normalization must be resolved before interpreting a report. The function assumes already-normalized non-empty labels and validates equal vector lengths; it does not silently merge names that merely look similar.

Mechanism and worked example

The worked function obtains unique labels, inverse codes and counts in one call.

The worked function obtains unique labels, inverse codes and counts in one call. It then passes the original hours as weights to bincount, so each weight uses the group code at its original position. The result reports sorted labels, integer observation counts and floating-point totals in the same group order. Distinct synthetic values make incorrect alignments easy to see.

Weighted bincount returns numerical totals, not necessarily exact integers for arbitrarily large quantities. This example uses small finite nonnegative hours. Exact money and large counters may need another representation and arithmetic policy. A convenient grouping API does not remove those domain constraints.

Complete worked example

python

import numpy as np

def inspect_example(labels, hours):
    if any(type(label) is not str or not label for label in labels):
        raise ValueError("expected normalized non-empty labels")
    keys = np.asarray(labels, dtype=np.str_)
    values = np.asarray(hours, dtype=np.float64)
    if keys.ndim != 1 or keys.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if not np.isfinite(values).all() or (values < 0).any():
        raise ValueError("expected finite nonnegative hours")
    unique, inverse, counts = np.unique(keys, return_inverse=True, return_counts=True)
    totals = np.bincount(inverse, weights=values, minlength=unique.size)
    return {"labels": unique.tolist(), "counts": counts.tolist(), "totals": totals.tolist()}
Reasoning
The worked function obtains unique labels, inverse codes and counts in one call. It then passes the original hours as weights to bincount, so each weight uses the group code at its original position. The result reports sorted labels, integer observation counts and floating-point totals in the same group order. Distinct synthetic values make incorrect alignments easy to see. Weighted bincount returns numerical totals, not necessarily exact integers for arbitrarily large quantities. This example uses small finite nonnegative hours. Exact money and large counters may need another representation and arithmetic policy. A convenient grouping API does not remove those domain constraints.
Open in local Python playground ↗

Focused decision: context is in the complete example

python

unique, inverse, counts = np.unique(keys, return_inverse=True, return_counts=True)
totals = np.bincount(inverse, weights=values, minlength=unique.size)
Focused decision
Type this short numerical decision after reading its complete context. The complete imports, array construction, validation, and result checks remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

The controlled mistake sorts the weights independently before grouping.

The controlled mistake sorts the weights independently before grouping. The total across all groups stays the same, but individual groups receive other observations' measurements. A test that checks only the grand total would miss the defect. Another error is assuming unique preserves first-appearance label order; the default sorted labels used here deliberately establish a different output order.

Empty input should still return three aligned empty lists. Missing labels are rejected instead of being mixed with a valid group name such as "unknown". If an application wants a missing-value group, it must define and test that policy explicitly.

Controlled mistake: inspect the failing numerical contract

python

import numpy as np

def inspect_example(labels, hours):
    if any(type(label) is not str or not label for label in labels):
        raise ValueError("expected normalized non-empty labels")
    keys = np.asarray(labels, dtype=np.str_)
    values = np.asarray(hours, dtype=np.float64)
    if keys.ndim != 1 or keys.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if not np.isfinite(values).all() or (values < 0).any():
        raise ValueError("expected finite nonnegative hours")
    unique, inverse, counts = np.unique(keys, return_inverse=True, return_counts=True)
    totals = np.bincount(inverse, weights=np.sort(values), minlength=unique.size)
    return {"labels": unique.tolist(), "counts": counts.tolist(), "totals": totals.tolist()}
Reasoning
The controlled mistake sorts the weights independently before grouping. The total across all groups stays the same, but individual groups receive other observations' measurements. A test that checks only the grand total would miss the defect. Another error is assuming unique preserves first-appearance label order; the default sorted labels used here deliberately establish a different output order. Empty input should still return three aligned empty lists. Missing labels are rejected instead of being mixed with a valid group name such as "unknown". If an application wants a missing-value group, it must define and test that policy explicitly.
Open in local Python playground ↗

Independent practice

Keep only groups containing at least two observations.

Keep only groups containing at least two observations. Preserve the original grouping calculations and apply one group-level mask to labels, counts and totals together. The small task filters completed groups; it should not drop input rows before computing counts or sort each result field independently.

Predict a case containing a repeated UA label and a single IL observation. Only UA should remain, and its total must include all of its original rows. A group with zero total hours can still have multiple valid observations and must not be removed merely because its numerical sum is zero.

Step 1: Report only groups with at least two observations, keeping all result fields aligned. Step 2: Retain the working imports, input validation and array shapes. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

import numpy as np

def inspect_example(labels, hours):
    if any(type(label) is not str or not label for label in labels):
        raise ValueError("expected normalized non-empty labels")
    keys = np.asarray(labels, dtype=np.str_)
    values = np.asarray(hours, dtype=np.float64)
    if keys.ndim != 1 or keys.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if not np.isfinite(values).all() or (values < 0).any():
        raise ValueError("expected finite nonnegative hours")
    unique, inverse, counts = np.unique(keys, return_inverse=True, return_counts=True)
    totals = np.bincount(inverse, weights=values, minlength=unique.size)
    return {"labels": unique.tolist(), "counts": counts.tolist(), "totals": totals.tolist()}

# Independent task: Report only groups with at least two observations, keeping all result fields aligned.
Reasoning
Report only groups with at least two observations, keeping all result fields aligned.
Open in local Python playground ↗
  • Hint 1: Build the eligibility mask from counts after aggregation.
  • Hint 2: Apply counts >= 2 to unique, counts and totals using the same mask.

Solution, tests and trade-offs

The practice solution uses counts >= 2 as a shared mask over the three group-aligned arrays.

The practice solution uses counts >= 2 as a shared mask over the three group-aligned arrays. Cases include singleton-only input, a repeated group with zero-valued observations and a mixture whose labels arrive out of sorted order. These checks distinguish count thresholds from total thresholds and verify that the retained fields remain aligned.

The result intentionally omits excluded groups rather than adding fake zero observations. A later report can separately track excluded group counts if that is part of its interface. Do not change the returned population without explaining how the next comparison or denominator should interpret it.

Practice solution and acceptance checks

python

import numpy as np

def inspect_example(labels, hours):
    if any(type(label) is not str or not label for label in labels):
        raise ValueError("expected normalized non-empty labels")
    keys = np.asarray(labels, dtype=np.str_)
    values = np.asarray(hours, dtype=np.float64)
    if keys.ndim != 1 or keys.shape != values.shape:
        raise ValueError("expected aligned vectors")
    if not np.isfinite(values).all() or (values < 0).any():
        raise ValueError("expected finite nonnegative hours")
    unique, inverse, counts = np.unique(keys, return_inverse=True, return_counts=True)
    totals = np.bincount(inverse, weights=values, minlength=unique.size)
    keep = counts >= 2
    return {"labels": unique[keep].tolist(), "counts": counts[keep].tolist(), "totals": totals[keep].tolist()}
Reasoning
The practice solution uses counts >= 2 as a shared mask over the three group-aligned arrays. Cases include singleton-only input, a repeated group with zero-valued observations and a mixture whose labels arrive out of sorted order. These checks distinguish count thresholds from total thresholds and verify that the retained fields remain aligned. The result intentionally omits excluded groups rather than adding fake zero observations. A later report can separately track excluded group counts if that is part of its interface. Do not change the returned population without explaining how the next comparison or denominator should interpret it.
Open in local Python playground ↗

Interview reasoning and next step

Explain the difference between unique labels and inverse indexes, then trace one weight through bincount into its group total.

Explain the difference between unique labels and inverse indexes, then trace one weight through bincount into its group total. Describe why independently sorting a measurement vector breaks the report even when its grand total is unchanged. State the label ordering and missing-label policy instead of leaving consumers to infer them.

On the PC, type the unique/inverse/counts sequence and the aligned weighted aggregation, then recall the group-level mask. In the app, reason about the same concrete counts. Next, combine array batches while preserving column meaning and accounting for every row.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/numpy-lab-20261106.zip. Install pinned dependencies in an isolated Python environment. Run verify.py, then change the working exercise baseline and run --practice np-grouping. The real NumPy arrays, numerical contracts, random state checks and temporary numeric files use owned fixtures only. No production database, feed, account or paid service is used. This lab executes Python; the PC typing trainer and native reading progress do not execute it.

Aggregate repeated labels without separating each measurement from its owning group.