normet


Namenormet JSON
Version 0.2.4 PyPI version JSON
download
home_pageNone
SummaryNormalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series
upload_time2025-10-29 16:30:48
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT License Copyright (c) 2023 Manchester Environmental Data Analytics Lab (MEDAL) 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.
keywords environmental-data time series deweathering normalisation counterfactual synthetic control causal inference air quality air pollution
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            `normet` (Normalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series) is a Python package designed for **environmental time-series analysis**. It provides a powerful and user-friendly suite of tools for air quality research, causal inference, and policy evaluation.

-----

## ✨ Core Strengths

  * **Automated & Intelligent**: Powered by **FLAML AutoML** and **H2O AutoML** backend, it automatically finds the optimal model, eliminating tedious manual tuning.
  * **All-in-One Solution**: Offers high-level functions that cover the entire workflow, from data preprocessing and model training to weather normalisation and counterfactual modelling.
  * **Robust Causal Inference**: Integrates both classic and machine-learning-based Synthetic Control Methods (SCM) and provides multiple uncertainty quantification tools (Bootstrap, Jackknife, Placebo Tests) to ensure reliable conclusions.
  * **Designed for Environmental Science**: Its features are built to address core challenges in air quality research, such as isolating meteorological impacts and evaluating policy effectiveness.

-----

## 🚀 Workflow

The core workflow of `normet` is designed to simplify complex analytical steps:

1.  **Data Preparation (`nm.prepare_data`)**: Automatically processes time-series data, including imputation, feature engineering (e.g., time-based variables), and dataset splitting.
2.  **Model Training (`nm.train_model`)**: Trains high-performance machine learning models using H2O AutoML.
3.  **Analysis & Application**:
      * **Weather Normalisation (`nm.normalise`)**: Removes the influence of meteorological conditions on pollutant concentrations.
      * **Time-Series Decomposition (`nm.decompose`)**: Decomposes the series into meteorology-driven and emission-driven components.
      * **Counterfactual Modelling (`nm.run_scm`)**: Estimates the causal effect of an intervention (e.g., a new policy).

-----

## 🔧 Installation

You can install the stable version of `normet` from PyPI:

```bash
pip install normet
```

Install the latest development version from GitHub:

```bash
pip install git+https://github.com/normet-dev/normet-py.git
```

### Backend Setup

`normet` relies on the **FLAML** or **H2O** machine learning platform as its core backend.
Install **FLAML** If you choose it as its core backend.
```bash
conda install flaml -c conda-forge
```

Or install **H2O** If you choose it as its core backend.

```bash
# Install the h2o package from PyPI
pip install h2o
```

-----

## 💡 Quick Start: One-Shot Weather Normalisation

With the `nm.do_all` function, you can perform a complete weather normalisation workflow in just a few lines of code.

```python
import normet as nm
import pandas as pd # For data manipulation

# Use the built-in example dataset
# (Assuming normet provides a function to load example data)
my1 = nm.load_data("MY1")

# Define the feature variables for the model
predictors = [
    "u10", "v10", "d2m", "t2m", "blh", "sp", "ssrd", "tcc", "tp", "rh2m",
    "date_unix", "day_julian", "weekday", "hour"
]

features_to_use = [
    "u10", "v10", "d2m", "t2m", "blh", "sp", "ssrd", "tcc", "tp", "rh2m"
]

# Run the end-to-end pipeline
# nm.do_all automatically handles data prep, model training, and normalisation
results = nm.do_all(
    df=my1,
    value="PM2.5",
    backend = "flaml", # Or "h2o"
    feature_names=predictors,
    variables_resample=features_to_use, # Specify met variables to resample to remove their effect
    n_samples=100 # Use a small sample size for a quick demo
)

# View the normalised (deweathered) time-series results
print("Normalised (deweathered) time-series:")
print(results['out'].head())

# Inspect the trained AutoML model object
print("\nTrained H2O AutoML Model:")
print(results['model'])

# Evaluate the model's performance
stats = nm.modStats(results['df_prep'], results['model'])
print("\nModel Performance Metrics:")
print(stats)
```

The `nm.do_all` function returns a **dictionary** containing three key elements:

1.  `out`: A **pandas DataFrame** with the normalised (deweathered) time-series.
2.  `df_prep`: The preprocessed data, including training/testing splits.
3.  `model`: The trained AutoML model object.

### Step-by-Step Execution

For more control over the process, you can execute each step manually.

#### 1\. Prepare the Data (`nm.prepare_data`)

This function handles missing value imputation, adds time-based features, and splits the data into training and testing sets.

```python
df_prep = nm.prepare_data(
    df=my1,
    value='PM2.5',
    feature_names=features_to_use,
    split_method='random',
    fraction=0.75
)
```

#### 2\. Train the Model (`nm.train_model`)

Train a machine learning model using H2O AutoML. The configuration allows you to control the training process.

```python
# Define all predictor variables
target = 'value'

# Configure H2O AutoML
h2o_config = {
    'max_models': 10,
    'include_algos': ["GBM"],
    'sort_metric': "RMSE",
    'max_mem_size': "8G"
}



# Or Configure FLAML AutoML
flaml_config = {
    "time_budget": 90,          # seconds for the search
    "metric": "r2",             # optimize R^2 (use "mae"/"mse" if preferred)
    "estimator_list": ["lgbm"], # single estimator keeps things fast
}

# Train the model
model = nm.train_model(
    df=df_prep,
    value=target,
    backend="flaml",    #or "h2o"
    variables=predictors,
    model_config=flaml_config  #or h2o_config
)

# Evaluate model performance
nm.modStats(df_prep, model)
```

#### 3\. Perform Normalisation (`nm.normalise`)

Use the trained model to generate the weather-normalised time-series.

```python
df_normalised = nm.normalise(
    df=df_prep,
    model=model,
    feature_names=predictors,
    variables_resample=features_to_use,
    n_samples=100
)

print(df_normalised.head())
```

#### 4\. Using a Custom Weather Dataset

You can also provide a specific weather dataset via the `weather_df` argument. This is useful for answering questions like, "What would concentrations have been under the average weather conditions of a different year?"

```python
# For demonstration, create a custom weather dataset using the first 100 rows
custom_weather = df_prep.iloc[0:100][features_to_use].copy()

# Perform normalisation using the custom weather conditions
df_norm_custom = nm.normalise(
    df=df_prep,
    model=model,
    weather_df=custom_weather,
    feature_names=predictors,
    variables_resample=features_to_use,
    n_samples=100 # n_samples will now sample from `custom_weather`
)

print(df_norm_custom.head())
```

-----

## 📊 Core Features Showcase

In addition to the high-level pipeline, `normet` offers flexible, modular functions for custom, step-by-step analyses.

### 1\. Weather Normalisation & Time-Series Decomposition

#### Rolling Weather Normalisation (`nm.rolling`)

Ideal for short-term trend analysis, this function performs normalisation within a moving time window to capture dynamic changes.

```python
# Assuming you have `df_prep` and `model` from the quick start
df_norm_rolling = nm.rolling(
    df=df_prep,
    value='value',
    model=model,
    feature_names=predictors,
    variables_resample=features_to_use,
    n_samples=100,
    window_days=14,      # Window size in days
    rolling_every=7      # Step size in days
)
print(df_norm_rolling.head())
```

#### Time-Series Decomposition (`nm.decompose`)

Decomposes the original time series into its **emission-driven** and **meteorology-driven** components.

```python
# Decompose to get the emission-driven component
df_emi = nm.decompose(
    method="emission",
    df=df_prep,
    value="value",
    model=model,
    feature_names=predictors,
    n_samples=100
)
print(df_emi.head())

# Decompose to get the meteorology-driven component
df_met = nm.decompose(
    method="meteorology",
    df=df_prep,
    value="value",
    model=model,
    feature_names=predictors,
    n_samples=100
)
print(df_met.head())
```

### 2\. Counterfactual Modelling & Causal Inference

`normet` includes a powerful toolkit for Synthetic Control Methods (SCM) to evaluate the causal impact of policies or events.

#### Data Preparation

```python
import pandas as pd

# Load the SCM example data
scm_data = pd.read_csv('data_AQ_Weekly.csv',parse_dates=['date'])

# Ensure the date column is datetime and filter
df=scm_data.query(f"date>='2015-05-01'").query(f"date<'2016-04-30'")

# Define the treated unit, donor pool, and intervention date
treated_unit = "2+26 cities"
donor_pool = [
    "Dongguan", "Zhongshan", "Foshan", "Beihai", "Nanning", "Nanchang", "Xiamen",
    "Taizhou", "Ningbo", "Guangzhou", "Huizhou", "Hangzhou", "Liuzhou",
    "Shantou", "Jiangmen", "Heyuan", "Quanzhou", "Haikou", "Shenzhen",
    "Wenzhou", "Huzhou", "Zhuhai", "Fuzhou", "Shaoxing", "Zhaoqing",
    "Zhoushan", "Quzhou", "Jinhua", "Shaoguan", "Sanya", "Jieyang",
    "Meizhou", "Shanwei", "Zhanjiang", "Chaozhou", "Maoming", "Yangjiang"
]
df=df[df['ID'].isin(donor_pool+["2+26 cities"])]
cutoff_date = "2015-10-23" # Define the intervention start date
```

#### Running a Synthetic Control Analysis (`nm.run_scm`)

```python
# Run classic SCM or the machine learning-based MLSCM
scm_result = nm.run_scm(
    df=df,
    date_col="date",
    outcome_col="SO2wn",
    unit_col="ID",
    treated_unit=treated_unit,
    donors=donor_pool,
    cutoff_date=cutoff_date,
    scm_backend="scm" # Options: 'scm' or 'mlscm'
)
print(scm_result.tail())
```

#### Placebo Tests (`nm.placebo_in_space`)

Check the significance of the main effect by iteratively treating each control unit as the "treated" unit and running a "fake" intervention.

```python
placebo_results = nm.placebo_in_space(
    df=df,
    date_col="date",
    outcome_col="SO2wn",
    unit_col="ID",
    treated_unit=treated_unit,
    donors=donor_pool,
    cutoff_date=cutoff_date,
    scm_backend="scm", # Options: 'scm' or 'mlscm'
    verbose=False
)

# Calculate confidence bands from the placebo effects
bands = nm.effect_bands_space(placebo_results, level=0.95, method="quantile")

# Plot the main effect with the placebo bands
nm.plot_effect_with_bands(bands, cutoff_date=cutoff_date, title="SCM Effect (95% placebo bands)")
```

#### Uncertainty Quantification (`nm.uncertainty_bands`)

Generate confidence intervals for the causal effect using **Bootstrap** or **Jackknife** methods.

```python
# Bootstrap method
boot_bands = nm.uncertainty_bands(
    df=df,
    date_col="date",
    outcome_col="SO2wn",
    unit_col="ID",
    treated_unit=treated_unit,
    donors=donor_pool,
    cutoff_date=cutoff_date,
    scm_backend="scm", # Options: 'scm' or 'mlscm'
    method="bootstrap",
    B=50 # Use a small number of replications for a quick demo
)
nm.plot_uncertainty_bands(boot_bands, cutoff_date=cutoff_date)

# Jackknife (leave-one-out) method
jack_bands = nm.uncertainty_bands(
    df=df,
    date_col="date",
    outcome_col="SO2wn",
    unit_col="ID",
    treated_unit=treated_unit,
    donors=donor_pool,
    cutoff_date=cutoff_date,
    scm_backend="scm",
    method="jackknife"
)
nm.plot_uncertainty_bands(jack_bands, cutoff_date=cutoff_date)
```

-----

## 📦 Dependencies

  * Python (\>= 3.8)
  * **Core Dependencies**: `h2o`, `pandas`, `numpy`
  * **SCM Features**: `scikit-learn`, `statsmodels` (or equivalent SCM libraries)
  * **Suggested**: `logging` (Python stdlib), `tqdm` (for progress bars)

-----

## 📜 How to Cite

If you use `normet` in your research, please cite it as follows:

```bibtex
@Manual{normet-pkg,
  title = {normet: Normalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series},
  author = {Congbo Song and Other Contributors},
  year = {2025},
  note = {Python package version 0.0.1},
  organization = {University of Manchester},
  url = {https://github.com/normet-dev/normet-py},
}
```

-----

## 📄 License

This project is licensed under the **MIT LICENSE**.

-----

## 🤝 How to Contribute

Contributions are welcome\! This project is released with a Contributor Code of Conduct. By participating, you agree to abide by its terms.

Please submit bug reports and feature requests via the [GitHub Issues](https://github.com/normet-dev/normet-py/issues).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "normet",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "environmental-data, time series, deweathering, normalisation, counterfactual, synthetic control, causal inference, air quality, air pollution",
    "author": null,
    "author_email": "Congbo Song <congbo.song@ncas.ac.uk>",
    "download_url": "https://files.pythonhosted.org/packages/1d/b1/229de95b119866b909df050262b73abb7281539216580b8e9e26dd318a85/normet-0.2.4.tar.gz",
    "platform": null,
    "description": "`normet` (Normalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series) is a Python package designed for **environmental time-series analysis**. It provides a powerful and user-friendly suite of tools for air quality research, causal inference, and policy evaluation.\n\n-----\n\n## \u2728 Core Strengths\n\n  * **Automated & Intelligent**: Powered by **FLAML AutoML** and **H2O AutoML** backend, it automatically finds the optimal model, eliminating tedious manual tuning.\n  * **All-in-One Solution**: Offers high-level functions that cover the entire workflow, from data preprocessing and model training to weather normalisation and counterfactual modelling.\n  * **Robust Causal Inference**: Integrates both classic and machine-learning-based Synthetic Control Methods (SCM) and provides multiple uncertainty quantification tools (Bootstrap, Jackknife, Placebo Tests) to ensure reliable conclusions.\n  * **Designed for Environmental Science**: Its features are built to address core challenges in air quality research, such as isolating meteorological impacts and evaluating policy effectiveness.\n\n-----\n\n## \ud83d\ude80 Workflow\n\nThe core workflow of `normet` is designed to simplify complex analytical steps:\n\n1.  **Data Preparation (`nm.prepare_data`)**: Automatically processes time-series data, including imputation, feature engineering (e.g., time-based variables), and dataset splitting.\n2.  **Model Training (`nm.train_model`)**: Trains high-performance machine learning models using H2O AutoML.\n3.  **Analysis & Application**:\n      * **Weather Normalisation (`nm.normalise`)**: Removes the influence of meteorological conditions on pollutant concentrations.\n      * **Time-Series Decomposition (`nm.decompose`)**: Decomposes the series into meteorology-driven and emission-driven components.\n      * **Counterfactual Modelling (`nm.run_scm`)**: Estimates the causal effect of an intervention (e.g., a new policy).\n\n-----\n\n## \ud83d\udd27 Installation\n\nYou can install the stable version of `normet` from PyPI:\n\n```bash\npip install normet\n```\n\nInstall the latest development version from GitHub:\n\n```bash\npip install git+https://github.com/normet-dev/normet-py.git\n```\n\n### Backend Setup\n\n`normet` relies on the **FLAML** or **H2O** machine learning platform as its core backend.\nInstall **FLAML** If you choose it as its core backend.\n```bash\nconda install flaml -c conda-forge\n```\n\nOr install **H2O** If you choose it as its core backend.\n\n```bash\n# Install the h2o package from PyPI\npip install h2o\n```\n\n-----\n\n## \ud83d\udca1 Quick Start: One-Shot Weather Normalisation\n\nWith the `nm.do_all` function, you can perform a complete weather normalisation workflow in just a few lines of code.\n\n```python\nimport normet as nm\nimport pandas as pd # For data manipulation\n\n# Use the built-in example dataset\n# (Assuming normet provides a function to load example data)\nmy1 = nm.load_data(\"MY1\")\n\n# Define the feature variables for the model\npredictors = [\n    \"u10\", \"v10\", \"d2m\", \"t2m\", \"blh\", \"sp\", \"ssrd\", \"tcc\", \"tp\", \"rh2m\",\n    \"date_unix\", \"day_julian\", \"weekday\", \"hour\"\n]\n\nfeatures_to_use = [\n    \"u10\", \"v10\", \"d2m\", \"t2m\", \"blh\", \"sp\", \"ssrd\", \"tcc\", \"tp\", \"rh2m\"\n]\n\n# Run the end-to-end pipeline\n# nm.do_all automatically handles data prep, model training, and normalisation\nresults = nm.do_all(\n    df=my1,\n    value=\"PM2.5\",\n    backend = \"flaml\", # Or \"h2o\"\n    feature_names=predictors,\n    variables_resample=features_to_use, # Specify met variables to resample to remove their effect\n    n_samples=100 # Use a small sample size for a quick demo\n)\n\n# View the normalised (deweathered) time-series results\nprint(\"Normalised (deweathered) time-series:\")\nprint(results['out'].head())\n\n# Inspect the trained AutoML model object\nprint(\"\\nTrained H2O AutoML Model:\")\nprint(results['model'])\n\n# Evaluate the model's performance\nstats = nm.modStats(results['df_prep'], results['model'])\nprint(\"\\nModel Performance Metrics:\")\nprint(stats)\n```\n\nThe `nm.do_all` function returns a **dictionary** containing three key elements:\n\n1.  `out`: A **pandas DataFrame** with the normalised (deweathered) time-series.\n2.  `df_prep`: The preprocessed data, including training/testing splits.\n3.  `model`: The trained AutoML model object.\n\n### Step-by-Step Execution\n\nFor more control over the process, you can execute each step manually.\n\n#### 1\\. Prepare the Data (`nm.prepare_data`)\n\nThis function handles missing value imputation, adds time-based features, and splits the data into training and testing sets.\n\n```python\ndf_prep = nm.prepare_data(\n    df=my1,\n    value='PM2.5',\n    feature_names=features_to_use,\n    split_method='random',\n    fraction=0.75\n)\n```\n\n#### 2\\. Train the Model (`nm.train_model`)\n\nTrain a machine learning model using H2O AutoML. The configuration allows you to control the training process.\n\n```python\n# Define all predictor variables\ntarget = 'value'\n\n# Configure H2O AutoML\nh2o_config = {\n    'max_models': 10,\n    'include_algos': [\"GBM\"],\n    'sort_metric': \"RMSE\",\n    'max_mem_size': \"8G\"\n}\n\n\n\n# Or Configure FLAML AutoML\nflaml_config = {\n    \"time_budget\": 90,          # seconds for the search\n    \"metric\": \"r2\",             # optimize R^2 (use \"mae\"/\"mse\" if preferred)\n    \"estimator_list\": [\"lgbm\"], # single estimator keeps things fast\n}\n\n# Train the model\nmodel = nm.train_model(\n    df=df_prep,\n    value=target,\n    backend=\"flaml\",    #or \"h2o\"\n    variables=predictors,\n    model_config=flaml_config  #or h2o_config\n)\n\n# Evaluate model performance\nnm.modStats(df_prep, model)\n```\n\n#### 3\\. Perform Normalisation (`nm.normalise`)\n\nUse the trained model to generate the weather-normalised time-series.\n\n```python\ndf_normalised = nm.normalise(\n    df=df_prep,\n    model=model,\n    feature_names=predictors,\n    variables_resample=features_to_use,\n    n_samples=100\n)\n\nprint(df_normalised.head())\n```\n\n#### 4\\. Using a Custom Weather Dataset\n\nYou can also provide a specific weather dataset via the `weather_df` argument. This is useful for answering questions like, \"What would concentrations have been under the average weather conditions of a different year?\"\n\n```python\n# For demonstration, create a custom weather dataset using the first 100 rows\ncustom_weather = df_prep.iloc[0:100][features_to_use].copy()\n\n# Perform normalisation using the custom weather conditions\ndf_norm_custom = nm.normalise(\n    df=df_prep,\n    model=model,\n    weather_df=custom_weather,\n    feature_names=predictors,\n    variables_resample=features_to_use,\n    n_samples=100 # n_samples will now sample from `custom_weather`\n)\n\nprint(df_norm_custom.head())\n```\n\n-----\n\n## \ud83d\udcca Core Features Showcase\n\nIn addition to the high-level pipeline, `normet` offers flexible, modular functions for custom, step-by-step analyses.\n\n### 1\\. Weather Normalisation & Time-Series Decomposition\n\n#### Rolling Weather Normalisation (`nm.rolling`)\n\nIdeal for short-term trend analysis, this function performs normalisation within a moving time window to capture dynamic changes.\n\n```python\n# Assuming you have `df_prep` and `model` from the quick start\ndf_norm_rolling = nm.rolling(\n    df=df_prep,\n    value='value',\n    model=model,\n    feature_names=predictors,\n    variables_resample=features_to_use,\n    n_samples=100,\n    window_days=14,      # Window size in days\n    rolling_every=7      # Step size in days\n)\nprint(df_norm_rolling.head())\n```\n\n#### Time-Series Decomposition (`nm.decompose`)\n\nDecomposes the original time series into its **emission-driven** and **meteorology-driven** components.\n\n```python\n# Decompose to get the emission-driven component\ndf_emi = nm.decompose(\n    method=\"emission\",\n    df=df_prep,\n    value=\"value\",\n    model=model,\n    feature_names=predictors,\n    n_samples=100\n)\nprint(df_emi.head())\n\n# Decompose to get the meteorology-driven component\ndf_met = nm.decompose(\n    method=\"meteorology\",\n    df=df_prep,\n    value=\"value\",\n    model=model,\n    feature_names=predictors,\n    n_samples=100\n)\nprint(df_met.head())\n```\n\n### 2\\. Counterfactual Modelling & Causal Inference\n\n`normet` includes a powerful toolkit for Synthetic Control Methods (SCM) to evaluate the causal impact of policies or events.\n\n#### Data Preparation\n\n```python\nimport pandas as pd\n\n# Load the SCM example data\nscm_data = pd.read_csv('data_AQ_Weekly.csv',parse_dates=['date'])\n\n# Ensure the date column is datetime and filter\ndf=scm_data.query(f\"date>='2015-05-01'\").query(f\"date<'2016-04-30'\")\n\n# Define the treated unit, donor pool, and intervention date\ntreated_unit = \"2+26 cities\"\ndonor_pool = [\n    \"Dongguan\", \"Zhongshan\", \"Foshan\", \"Beihai\", \"Nanning\", \"Nanchang\", \"Xiamen\",\n    \"Taizhou\", \"Ningbo\", \"Guangzhou\", \"Huizhou\", \"Hangzhou\", \"Liuzhou\",\n    \"Shantou\", \"Jiangmen\", \"Heyuan\", \"Quanzhou\", \"Haikou\", \"Shenzhen\",\n    \"Wenzhou\", \"Huzhou\", \"Zhuhai\", \"Fuzhou\", \"Shaoxing\", \"Zhaoqing\",\n    \"Zhoushan\", \"Quzhou\", \"Jinhua\", \"Shaoguan\", \"Sanya\", \"Jieyang\",\n    \"Meizhou\", \"Shanwei\", \"Zhanjiang\", \"Chaozhou\", \"Maoming\", \"Yangjiang\"\n]\ndf=df[df['ID'].isin(donor_pool+[\"2+26 cities\"])]\ncutoff_date = \"2015-10-23\" # Define the intervention start date\n```\n\n#### Running a Synthetic Control Analysis (`nm.run_scm`)\n\n```python\n# Run classic SCM or the machine learning-based MLSCM\nscm_result = nm.run_scm(\n    df=df,\n    date_col=\"date\",\n    outcome_col=\"SO2wn\",\n    unit_col=\"ID\",\n    treated_unit=treated_unit,\n    donors=donor_pool,\n    cutoff_date=cutoff_date,\n    scm_backend=\"scm\" # Options: 'scm' or 'mlscm'\n)\nprint(scm_result.tail())\n```\n\n#### Placebo Tests (`nm.placebo_in_space`)\n\nCheck the significance of the main effect by iteratively treating each control unit as the \"treated\" unit and running a \"fake\" intervention.\n\n```python\nplacebo_results = nm.placebo_in_space(\n    df=df,\n    date_col=\"date\",\n    outcome_col=\"SO2wn\",\n    unit_col=\"ID\",\n    treated_unit=treated_unit,\n    donors=donor_pool,\n    cutoff_date=cutoff_date,\n    scm_backend=\"scm\", # Options: 'scm' or 'mlscm'\n    verbose=False\n)\n\n# Calculate confidence bands from the placebo effects\nbands = nm.effect_bands_space(placebo_results, level=0.95, method=\"quantile\")\n\n# Plot the main effect with the placebo bands\nnm.plot_effect_with_bands(bands, cutoff_date=cutoff_date, title=\"SCM Effect (95% placebo bands)\")\n```\n\n#### Uncertainty Quantification (`nm.uncertainty_bands`)\n\nGenerate confidence intervals for the causal effect using **Bootstrap** or **Jackknife** methods.\n\n```python\n# Bootstrap method\nboot_bands = nm.uncertainty_bands(\n    df=df,\n    date_col=\"date\",\n    outcome_col=\"SO2wn\",\n    unit_col=\"ID\",\n    treated_unit=treated_unit,\n    donors=donor_pool,\n    cutoff_date=cutoff_date,\n    scm_backend=\"scm\", # Options: 'scm' or 'mlscm'\n    method=\"bootstrap\",\n    B=50 # Use a small number of replications for a quick demo\n)\nnm.plot_uncertainty_bands(boot_bands, cutoff_date=cutoff_date)\n\n# Jackknife (leave-one-out) method\njack_bands = nm.uncertainty_bands(\n    df=df,\n    date_col=\"date\",\n    outcome_col=\"SO2wn\",\n    unit_col=\"ID\",\n    treated_unit=treated_unit,\n    donors=donor_pool,\n    cutoff_date=cutoff_date,\n    scm_backend=\"scm\",\n    method=\"jackknife\"\n)\nnm.plot_uncertainty_bands(jack_bands, cutoff_date=cutoff_date)\n```\n\n-----\n\n## \ud83d\udce6 Dependencies\n\n  * Python (\\>= 3.8)\n  * **Core Dependencies**: `h2o`, `pandas`, `numpy`\n  * **SCM Features**: `scikit-learn`, `statsmodels` (or equivalent SCM libraries)\n  * **Suggested**: `logging` (Python stdlib), `tqdm` (for progress bars)\n\n-----\n\n## \ud83d\udcdc How to Cite\n\nIf you use `normet` in your research, please cite it as follows:\n\n```bibtex\n@Manual{normet-pkg,\n  title = {normet: Normalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series},\n  author = {Congbo Song and Other Contributors},\n  year = {2025},\n  note = {Python package version 0.0.1},\n  organization = {University of Manchester},\n  url = {https://github.com/normet-dev/normet-py},\n}\n```\n\n-----\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the **MIT LICENSE**.\n\n-----\n\n## \ud83e\udd1d How to Contribute\n\nContributions are welcome\\! This project is released with a Contributor Code of Conduct. By participating, you agree to abide by its terms.\n\nPlease submit bug reports and feature requests via the [GitHub Issues](https://github.com/normet-dev/normet-py/issues).\n",
    "bugtrack_url": null,
    "license": "MIT License\n        \n        Copyright (c) 2023 Manchester Environmental Data Analytics Lab (MEDAL)\n        \n        Permission is hereby granted, free of charge, to any person obtaining a copy\n        of this software and associated documentation files (the \"Software\"), to deal\n        in the Software without restriction, including without limitation the rights\n        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n        copies of the Software, and to permit persons to whom the Software is\n        furnished to do so, subject to the following conditions:\n        \n        The above copyright notice and this permission notice shall be included in all\n        copies or substantial portions of the Software.\n        \n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n        SOFTWARE.\n        ",
    "summary": "Normalisation, Decomposition, and Counterfactual Modelling for Environmental Time-series",
    "version": "0.2.4",
    "project_urls": {
        "Homepage": "https://github.com/normet-dev/normet-py",
        "Issues": "https://github.com/normet-dev/normet-py/issues"
    },
    "split_keywords": [
        "environmental-data",
        " time series",
        " deweathering",
        " normalisation",
        " counterfactual",
        " synthetic control",
        " causal inference",
        " air quality",
        " air pollution"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6e2a6e5db8506ee33dab1d404a87049a3bda4f5e2ab88fef03b985da4961e0cc",
                "md5": "c11c1241a13ca601699ce8c564055983",
                "sha256": "fdd1c5fc7ae7fdca3cfb372669754ec181aa843f8f280f008296ae6ed5e82b4c"
            },
            "downloads": -1,
            "filename": "normet-0.2.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c11c1241a13ca601699ce8c564055983",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 70852,
            "upload_time": "2025-10-29T16:30:47",
            "upload_time_iso_8601": "2025-10-29T16:30:47.408387Z",
            "url": "https://files.pythonhosted.org/packages/6e/2a/6e5db8506ee33dab1d404a87049a3bda4f5e2ab88fef03b985da4961e0cc/normet-0.2.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1db1229de95b119866b909df050262b73abb7281539216580b8e9e26dd318a85",
                "md5": "da8b18c9d357cb1b59dc02d860c98af6",
                "sha256": "634a24e71223d957de87ef5544e5967d91424b448b33e0a8457fbf24c1d82d82"
            },
            "downloads": -1,
            "filename": "normet-0.2.4.tar.gz",
            "has_sig": false,
            "md5_digest": "da8b18c9d357cb1b59dc02d860c98af6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 60353,
            "upload_time": "2025-10-29T16:30:48",
            "upload_time_iso_8601": "2025-10-29T16:30:48.899394Z",
            "url": "https://files.pythonhosted.org/packages/1d/b1/229de95b119866b909df050262b73abb7281539216580b8e9e26dd318a85/normet-0.2.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-29 16:30:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "normet-dev",
    "github_project": "normet-py",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "normet"
}
        
Elapsed time: 3.31796s