The things the data is about and how they relate. Arrows are relations; the attributes are stored fields or derived values.
classDiagram
class Person {
}
class Garment {
text item
text details
money-eur price
date purchased
date divested
integer timesUsedInit
text materials
mass-g weight
image photo
}
class Category {
}
class OwnershipPeriod {
}
class WearDay {
date date
}
class Wear {
}
class Tag {
}
class MaterialComposition {
share fibreShare
}
class Fibre {
}
class FibreClass {
}
class ComparisonItem {
money-eur monthlyCost
share useFrequency
}
Garment --> Category : inCategory
Garment --> Tag : hasTag
Garment --> OwnershipPeriod : hasOwnershipPeriod
Garment --> MaterialComposition : hasComposition
Wear --> WearDay : onDay
Wear --> Garment : ofGarment
WearDay --> Garment : worn
Fibre --> FibreClass : inFibreClass
Class
Definition
Aligned with
Stored as
Person
The wardrobe's owner, who logs what they wear. In this data set there is exactly one.
schema:Person
Garment
One physical piece of clothing, footwear or accessory owned at some point since tracking began. Two identical items bought together are separate garments (e.g. "Diesel gray 01" and "Diesel gray 02").
The days a garment counts as owned: from its purchase date (at the earliest the start of tracking) to its divest date, inclusive; open-ended while it is still active.
Wear day
One calendar day of the log and the set of garments worn on it.
The fibres a garment's main part is made of, with their shares (summing to 100 %), read from its free-text materials label.
Fibre
A textile or material, e.g. cotton, wool, polyester, leather.
Fibre class
One of four groups of fibres: natural, regenerated cellulose, synthetic or other.
Comparison item
An item of other everyday spending (transport, subscriptions, tools…) with a monthly cost and a frequency of use, shown next to the clothing categories for comparison.
The last day the garment was owned; empty while active.
timesUsedInit
Garment → integer
Wears before tracking began on 1 January 2018.
materials
Garment → text
The materials label as written (free text).
weight
Garment → mass-g
The garment's weight in grams.
schema:weight
hasTag
Garment → Tag
The garment's descriptive tags.
photo
Garment → image
The garment's standard photo (228 × 228 PNG).
schema:image
hasOwnershipPeriod
Garment → OwnershipPeriod
Derived from purchased and divested.
hasComposition
Garment → MaterialComposition
Derived from materials.
onDay
Wear → WearDay
The day of the wear.
ofGarment
Wear → Garment
The garment worn.
prov:used
date
WearDay → date
The calendar date (no time zone).
worn
WearDay → Garment
The garments worn that day.
fibreShare
MaterialComposition → share
Each fibre's share of the main part, 0–1, summing to 1.
inFibreClass
Fibre → FibreClass
The fibre's class.
monthlyCost
ComparisonItem → money-eur
What the item costs per month, in euros (entered).
useFrequency
ComparisonItem → share
Share of days on which it is used, 0–1 (entered).
Metrics (semantic model)
Every figure the app and the MCP server compute: its definition, formula, grain, unit, how the chosen date range applies, the decisions behind it, and the code and tests that implement it. Shared rules: wears outside a garment's ownership period never count; dates are inclusive; one month is 30.5 days.
Times worn times-worn
Wears of the garment within the range (and within its ownership period), plus pre-tracking wears when the range starts on the start of tracking.
|{wear days d : range.from ≤ d ≤ range.to, d in ownership}| + (range.from ≤ 2018-01-01 ? timesUsedInit : 0)
counts times worn including pre-tracking wears, ignoring wears outside ownership counts pre-tracking wears only when the range starts at the start of tracking
Lifetime wears lifetime-wears
All wears over the garment's whole ownership period, including pre-tracking wears.
computes cost per wear over the whole ownership; unworn garments cost their full price computes cost per wear independently of any range, including wears after it (D-2)
Days owned days-owned
Days owned within the range up to its end, frozen at the divest date. When the range starts on the start of tracking, counted from the purchase date even before tracking (since pre-tracking wears are included too).
The sum of the prices of the category's garments owned at any point in the range, divided by the sum of their lifetime wears. Unworn garments count with their full price and no wears; if none of them has ever been worn, the sum of prices is divided by 1 (as an unworn garment costs its full price). Garments without a price are left out. No Total row value.
Σ price / max(1, Σ lifetime-wears), over the category's garments owned during the range with a price
Grain
category
Unit
EUR per wear
Date range
range (which garments) + lifetime (their wears)
Empty when
no garment of the category with a price was owned in the range
pools prices over lifetime wears of all garments owned in the range, unworn ones included (D-2) weights garments by lifetime wears, not by wears in the range (D-2) divides by 1 when no garment of the category has ever been worn, and skips unknown prices
Equivalent to the average of the garments' cost per wear weighted by lifetime wears, with unworn garments' prices included. Until 2026-10-16 it was weighted by wears in the range and left unworn garments out. The original R pipeline used Σ price of all owned garments / Σ wears in the range.
Days used days-used
Share of days with at least one wear of a category garment, from the later of the range start and the day the category's first garment was owned, to the range end. No Total row value.
|{days d in span : some category garment worn on d}| / |span|, span = max(range.from, min ownedFrom) .. range.to
gives the second-hand share of active garments, per category and in total
Divested items' averages divested-averages
Over divested garments owned at least 30 days: wears per month (Σ times worn / Σ days owned × 30.5) and the use-weighted cost per wear. Reference lines on category pages.
sums prices of active garments per category, in whole cents sums all categories for the total
Daily cost daily-cost
For each logged day in the range with wears: the sum of the lifetime cost per wear of the garments worn and owned that day (unknown prices count 0); "final" when all of them are divested. Average - the mean of 30 consecutive logged days centred on the day (days i−14 … i+15), so days without wears are skipped.
Σ garment-cost-per-wear over garments worn on d; average(i) = mean(cost[i−14 .. i+15])
sums lifetime cost per wear of the garments worn each day (D-2) centres a 30-value window on values i-14 … i+15
With "as of" a date (animations), cost per wear counts only wears up to that date.
Fibre mass fibre-mass
Weight × main-part composition, summed per fibre, with recycled and organic kg. Owned basis - garments active on the range's end date. Worn basis - each wear in the range counts the garment's full weight. Only garments with a weight and readable materials are included; coverage says how many.
derives yearly cost and cost per use as the Sheet did
Decisions
D-1: Public pages get the full day-by-day log 2026-10-09
Public pages and the MCP server read one world-readable document with all garments and the full wear log, and compute every metric from it, rather than precomputed summaries.
Why: Any metric for any date range can be computed in the browser with one document read.
A garment's cost per wear is price / all its wears over its whole ownership, regardless of the chosen date range, even when wears after the range lower it. A category's cost per wear for a range is Σ price / Σ lifetime wears over its garments owned at any point in the range, unworn garments included (divided by 1 if none has ever been worn).
Why: The concept must be treated consistently - a garment has one cost per wear, and unused purchases must not disappear from a category's cost.
Amended: 2026-10-16 (Olof): the category figure was the average of the garments' cost per wear weighted by wears in the range, over garments worn in the range, which left unworn garments out entirely. Now pooled over lifetime wears with unworn garments included. Raises categories with unused garments (e.g. Socks +43 % in 2025) and changes Yearly cost, Turnover and the monthly cost comparison with it.
Daily cost = Σ over garments worn that day (and owned then) of their lifetime cost per wear; "final" when every one of them is divested; plus a rolling mean centred on the day.