Metadata-Version: 2.4
Name: activitymodel
Version: 0.1.0
Summary: Occupant activity calendars from the French time-use survey: diary library, census matching, annual calendars
Author-email: Yassine Abdelouadoud <yassine.abdelouadoud@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Yassine Abdelouadoud
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: <4.0.0,>=3.10
Description-Content-Type: text/markdown
License-File: LICENCE.md
Requires-Dist: numpy>=1.24
Requires-Dist: polars!=1.43.0,!=1.44.0,>=1.0
Requires-Dist: pyarrow>=14.0
Requires-Dist: scipy>=1.10
Provides-Extra: survey
Requires-Dist: pandas>=2.0; extra == "survey"
Provides-Extra: fast
Requires-Dist: fastcluster>=1.2; extra == "fast"
Provides-Extra: analysis
Requires-Dist: scikit-learn>=1.3; extra == "analysis"
Requires-Dist: matplotlib>=3.7; extra == "analysis"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: fastcluster>=1.2; extra == "dev"
Dynamic: license-file

# activitymodel

Occupant activity calendars for building energy simulation, built from the French
time-use survey (INSEE *Enquête Emploi du temps 2009-2010*, PROGEDO-ADISP
doi:10.13144/lil-0695).

`activitymodel` is a pure library: it never resolves a data path at import time
and never fetches data. It is used by three packages at three moments:

| moment | who imports it | what it does |
|---|---|---|
| dataset build (Snakemake, maintainers) | `buildingdata` pipeline | `activitymodel.pipeline.build_library` turns the ADISP delivery into a **diary library** (four parquet tables) that buildingdata uploads to GCS |
| census dataset build | `buildingdata` pipeline / `buildingmodel` | `harmonise_census_individuals` puts INSEE RP `indcvi` individuals in the same vocabulary the library was built with |
| simulation | `building_eload` | `assign_clusters` + `generate_calendar` turn the occupants of a district into a year of 10-minute activity codes per occupant |

It replaces the two prototypes `activity_clustering` and
`activity-occupant-association`; `doc/design.md` records what was wrong with
them and why each part looks the way it does now.

## Quick start

```python
import polars as pl
import activitymodel as am

# 1. (pipeline) build the library from the survey files, once
from activitymodel.pipeline import LibraryBuildOptions, build_library
library = build_library("/data/edt2010", LibraryBuildOptions())
library.write("/data/buildingdata_pipeline/processed/activity_library")

# 2. (anywhere) load it back - buildingdata will serve the four tables
library = am.DiaryLibrary.read("/data/buildingdata_pipeline/processed/activity_library")

# 3. (simulation) harmonise census occupants and generate a year
occupants = am.harmonise_census_individuals(pl.read_parquet("indcvi_slice.parquet"))
occupants = occupants.with_row_index("occupant_id")
calendar = am.generate_calendar(library, occupants, year=2024, seed=42)

calendar.day_index.shape        # (n_occupants, 366)  int32 pointers into the day pool
codes = calendar.codes(0, 500)  # (500, 52704) uint8, detailed activities, Europe/Paris clock
long = calendar.to_long(0, 500) # occupant-major polars frame with an Enum activity column
```

```bash
python -m activitymodel.pipeline /data/edt2010 out/ --weekday 16 --saturday 10 --sunday 10
```

## What is in the box

```
activitymodel/
  taxonomy.py      the 24 detailed activities, their ACTPR codes, the coarser levels
                   (presence = building_eload's, macro, mid)
  vocabulary.py    shared socio-demographic categories, age bands, matching ladder
  harmonise.py     survey individuals / census individuals -> shared vocabulary
  survey/          read the ADISP files, code the diaries (location and day-kind aware)
  clustering.py    one-hot + PCA + Ward (or weighted k-means), k-selection curves
  library.py       DiaryLibrary: the published dataset, its schema and validation
  matching.py      occupants -> (cluster_weekday, cluster_saturday, cluster_sunday)
  calendar.py      day sampling, French public holidays, AnnualCalendar
  diagnostics/     activity profiles, matrix distances, association scores
  pipeline.py      build_library() and the command line
  tests/           synthetic fixtures; `-m integration` runs on the real files
doc/
  design.md            gap analysis of the prototypes and the decisions taken
  dataset_contract.md  the four tables, their columns, dtypes and invariants
  integration.md       what changes in buildingdata, buildingmodel,
                       buildingcalibration and building_eload, in order
  integration/         reference snippets for those changes
```

## Measured on the real data (Paris, RP 2021, library 16/10/10 clusters)

| step | result |
|---|---|
| library build (26,056 diaries, Ward on 50 components) | 29 s wall |
| `assign_clusters` on 663,781 occupants | 1.3 s; 95.3 % (weighted) matched at the finest level |
| `generate_calendar` for 20,105 occupants, 2024 | 1.6 s, 28 MiB of `day_index` |
| `codes()` for 500 occupant-years | 25 MiB, 0.01 s |
| total-variation distance, matched Paris weekday profile vs national weighted survey | 0.048 |

## Install

```bash
uv venv --python 3.12 .venv
uv pip install -e ".[dev,survey,fast]"
.venv/bin/python -m pytest -q
```

Extras: `survey` (pandas, to read the Stata delivery), `fast` (fastcluster),
`analysis` (scikit-learn, matplotlib), `dev`.

## Licence and data

Code: MIT. The survey is available on request from PROGEDO-ADISP and is not
redistributable; the diary library holds no survey row (days are detached from
people, people are aggregated into cells). See `doc/dataset_contract.md`.
