III-02 · Samples, Features, Labels and Targets

Rows are observations. Columns have meanings.

Edit a tiny dataset and inspect its values, semantic types, units and array shapes before fitting anything.

Chapters in Part I
  1. III-01 · What Machine Learning Actually Learns
  2. III-02 · Samples, Features, Labels and Targets
  3. III-03 · Learning Problems
  4. III-04 · Training, Validation, Testing and Generalization
  5. III-05 · Parameters, Hyperparameters, Loss and Optimization
  6. III-06 · Underfitting, Overfitting, Bias, Variance and Baselines

Inspect the row and column contract

Six hand-authored study records; illustrative scores and categories, not measured learning outcomes. Switching target type loads that type’s preset targets.

X · sample rows, feature columns
4 × 2
Illustrative weightsA compatible linear predictor would use a weight column; this schema inspector fits no model.
2 × 1
y · targetsSemantic type: quantity
4 × 1
Bias conventionNo intercept column has been added to X.
Separate scalar
X has sample rows and feature columns. Values shown here are exact input numbers.
Sample IDStudy time
hours · real
Practice tasks
count · integer
Target · quantity
001
123
235
347

Changing n or d selects stored rows or columns without fabricating targets or deleting hidden values. Count features accept integers. Weight shapes describe a compatible predictor, not a fitted model.

Frame 1 / 1 · edits start from the displayed state.

Choose the active rows and columns, select a cell’s feature, and edit its value. Target presets let the same feature schema carry a score, a category or no observed target.

A number needs a place and a meaning

The value 22 could mean two hours, two completed tasks, a category code, or row number two. Its role comes from the schema and the goal. Before choosing an algorithm, establish what one observation represents and what information is available for it.

A sample is one observed entity or event under a specified collection process. A feature is an input attribute of that sample. A target is the output quantity or category the supervised goal seeks to predict. “Label” often means an observed category, but some writing uses it for any target; here we specify the semantic type directly.

Sample IDs preserve identity when rows are reordered or assigned to partitions. They are metadata in this sheet, not numerical predictors. Two IDs may still refer to measurements of the same entity; distinct identifiers do not establish statistical independence.

The six stored records are hand-authored examples of study hours, completed practice tasks, sleep hours and illustrative scores. They are not measured students and support no claim about education. Category presets use the names ready and practice. Changing target type restores that type’s preset targets; edited feature values remain.

Build the batch from feature columns

For sample ii with dd features, its mathematical feature vector is a column

xi=[xi1⋮xid]∈Rd×1.\mathbf{x}_i= \begin{bmatrix}x_{i1}\\ \vdots\\x_{id}\end{bmatrix} \in\mathbb{R}^{d\times1}.

A batch stores the transposed sample columns as rows:

X=[x1T⋮xnT]∈Rn×d,y=[y1⋮yn]∈Rn×1.X=\begin{bmatrix}\mathbf{x}_1^{\mathsf T}\\\vdots\\ \mathbf{x}_n^{\mathsf T}\end{bmatrix}\in\mathbb{R}^{n\times d}, \qquad y=\begin{bmatrix}y_1\\\vdots\\y_n\end{bmatrix} \in\mathbb{R}^{n\times1}.

Thus XijX_{ij} selects feature jj of sample ii. Increasing nn changes the number of observations. Increasing dd changes the input description. Neither operation automatically changes the dimensionality of the output.

For a compatible scalar linear predictor, a weight column W∈Rd×1W\in\mathbb{R}^{d\times1} produces

y^=XW+b1n∈Rn×1.\hat y=XW+b\mathbf{1}_n\in\mathbb{R}^{n\times1}.

The inner dimensions dd match. The bias is separate; there is no column of ones in this inspector’s XX. The inspector shows this compatible weight shape without fitting or assigning weights. The later capacity experiment explicitly uses a column of ones for its intercept, and labels that different convention.

Work one row and one batch

At reset, n=4,d=2n=4,d=2. The observed inputs and scores are

X=[00122334],y=[1357].X=\begin{bmatrix}0&0\\1&2\\2&3\\3&4\end{bmatrix}, \qquad y=\begin{bmatrix}1\\3\\5\\7\end{bmatrix}.

The first feature is hours and the second is a task count. Suppose, for a shape calculation only, W=[2,−0.5]TW=[2,-0.5]^{\mathsf T} and b=1b=1. For row three,

y^3=2(2)−0.5(3)+1=3.5.\hat y_3=2(2)-0.5(3)+1=3.5.

All four predictions are [1,2,3.5,5]T[1,2,3.5,5]^{\mathsf T}, a 4×14\times1 column. Its residual against the third target is 3.5−5=−1.53.5-5=-1.5. The coefficient on hours has score-per-hour units and the coefficient on tasks has score-per-task units. Compatible array dimensions do not make arbitrary units interchangeable.

If sleep time is added, XX becomes 4×34\times3 and a compatible weight column becomes 3×13\times1. The targets remain 4×14\times1. If the goal is unlabelled exploration, yy is absent rather than a zero column.

Types constrain valid observations

Study and sleep time accept finite real-valued hours from 00 to 1212. Practice tasks accept integer counts from 00 to 2020. Quantity targets accept scores from 00 to 2020. Category targets accept the two declared names. These are illustrative UI limits, not universal limits on real measurements.

Entering 2.52.5 tasks is rejected; the sheet does not silently round it to an integer. Entering 2.52.5 hours is valid. Finite numeric storage alone is insufficient validity: a feature can violate units, semantic type or a collection rule.

Execute the actual schema reference

NumPy constructs XX as a float64 rectangular matrix. A quantity target column uses float64; category labels use strings. Neither representation silently tells an algorithm whether the semantic learning problem is regression or classification. Run .venv/bin/python -B code/python/p01/samples_features.py for the three target schemas.

code/python/p01/samples_features.py
"""III-02: sample rows, feature columns and semantically typed target columns."""
from copy import deepcopy
import json
import numpy as np
from numeric import is_finite_number

FEATURE_COLUMNS = [dict(name='Study time', unit='hours', integer=False, max=12), dict(name='Practice tasks', unit='count', integer=True, max=20), dict(name='Sleep time', unit='hours', integer=False, max=12)]
FEATURE_CANONICAL = dict(samples=[dict(id=f'r{i+1}', values=values, target=target) for i, (values, target) in enumerate([([0,0,6],1), ([1,2,8],3), ([2,3,7],5), ([3,4,8],7), ([4,5,6],9), ([5,8,7],11)])], sampleCount=4, featureCount=2, targetKind='quantity')

def derive_features(state):
    n, d = state['sampleCount'], state['featureCount']
    if type(n) is not int or type(d) is not int or not 1 <= n <= 6 or not 1 <= d <= 3 or len(state['samples']) != 6 or state['targetKind'] not in ('quantity', 'category', 'none'):
        raise ValueError('Require 1–6 active samples, 1–3 features and a supported target kind')
    ids = set()
    for row in state['samples']:
        if not isinstance(row['id'], str) or not row['id'].strip() or row['id'] in ids or len(row['values']) != 3:
            raise ValueError('Unique nonempty IDs and rectangular feature rows are required')
        ids.add(row['id'])
        for value, column in zip(row['values'], FEATURE_COLUMNS):
            if not is_finite_number(value) or not 0 <= value <= column['max'] or (column['integer'] and value != int(value)):
                raise ValueError('Feature type, finite value or unit range is invalid')
        kind, target = state['targetKind'], row['target']
        if kind == 'quantity' and (not is_finite_number(target) or not 0 <= target <= 20):
            raise ValueError('Quantity target must be a finite score from 0 to 20')
        if kind == 'category' and (not isinstance(target, str) or target not in ('ready', 'practice')):
            raise ValueError('Category target must be ready or practice')
        if kind == 'none' and target is not None:
            raise ValueError('Unlabelled schema has no target')
    rows = state['samples'][:n]
    X = np.array([row['values'][:d] for row in rows], dtype=np.float64)
    y = None if state['targetKind'] == 'none' else np.array([[row['target']] for row in rows], dtype=np.float64 if state['targetKind'] == 'quantity' else str)
    return dict(state=deepcopy(state), rows=deepcopy(rows), columns=FEATURE_COLUMNS[:d], X=X.tolist(), y=None if y is None else y.tolist(),
                shapes=dict(X=list(X.shape), weights=[d,1], targets=None if y is None else list(y.shape)),
                targetReason='No observed target column; an unsupervised goal must be specified separately.' if y is None else None)

def feature_target_kind(state, kind):
    result = deepcopy(state)
    result['targetKind'] = kind
    for i, row in enumerate(result['samples']):
        row['target'] = None if kind == 'none' else ('ready' if FEATURE_CANONICAL['samples'][i]['target'] >= 7 else 'practice') if kind == 'category' else FEATURE_CANONICAL['samples'][i]['target']
    return result

if __name__ == '__main__':
    print(json.dumps({kind: derive_features(feature_target_kind(FEATURE_CANONICAL, kind)) for kind in ('quantity', 'category', 'none')}, indent=2, allow_nan=False))

The tests enumerate every supported n,dn,d shape and check column lengths separately. They also reject fractional counts, duplicate IDs, nonfinite values and mismatched target schemas. No numerical tolerance is needed to decide an integer count or array shape.

Explore the schema without losing observations

Select row r3 and edit study hours. The same value appears in the table and the authoritative XX. Enter more than four decimal places: the table retains the complete input number. Reduce nn to two, then restore four; hidden records retain their values. Reducing dd selects fewer feature columns without deleting the stored sleep-time column.

Switch to categorical targets. Which cells changed, and which shapes stayed the same? Switch to no target and explain why the batch still exists. Reset returns all six stored records and the original four-row, two-feature view.

Numerical and conceptual limits

Every active batch is rectangular, every stored ID is unique and nonempty, and each stored row retains three feature values. Ragged input cannot be treated as a numeric matrix by simply reporting the longest row. This sheet represents absence at the target-column level; it does not teach imputation for partially missing features.

Binary64 is an approximation to real numbers. Given inputs use round-trip decimal text, while derived results elsewhere use marked approximations. Features with different scales can affect optimization even when their shapes agree. Fitting a centering or scaling statistic uses training data only, as the split experiment demonstrates.

A valid table is the beginning of a learning problem. The next chapter asks whether a target denotes an amount, a category, or information deliberately unused by the goal.

Check your understanding

For four samples and two features, what is X’s shape?
What does X₂₁ mean in this chapter’s one-based notation?
A practice-task count is entered as 2.5. What should happen?
In this sheet’s “No observed target” mode, all active samples are unlabelled. How is y represented?
Does adding sleep-time hours mean the target now has three columns?

Checkpoints

You can identify samples, features, targets and identity metadata; construct n×dn\times d batches from column feature vectors; check a compatible weight and target shape; and distinguish missing supervision from numeric zero. Shapes, units and semantics are separate checks that all matter before fitting.