An easy-to-use package for Positive Matrix Factorization (PMF) analysis of environmental data.
Project description
Easy PMF
Easy PMF is a comprehensive Python package for Positive Matrix Factorization (PMF) analysis, designed specifically for environmental data analysis such as air quality source apportionment. It provides an easy-to-use interface similar to EPA's PMF software with built-in visualization capabilities.
:warning: This project is in the early stages of development and may not yet be suitable for production use.
:warning: A LLM (Large Language Model) is being used to assist with development and documentation; much of the content was vibe coded without much oversight.
✨ Features
- Simple API: Easy-to-use interface similar to scikit-learn
- Comprehensive Visualizations: EPA PMF-style plots and heatmaps
- Multiple Dataset Support: Built-in support for various environmental datasets
- Robust Error Handling: Input validation and convergence checking
- Flexible Data Input: Support for CSV, TXT, and Excel files
- Interactive Analysis: Command-line tools for quick analysis
- Well Documented: Extensive documentation with examples
🚀 Quick Start
Installation
pip install easy-pmf
Basic Usage
import pandas as pd
from easy_pmf import PMF
# Load your concentration and uncertainty data
concentrations = pd.read_csv("concentrations.csv", index_col=0)
uncertainties = pd.read_csv("uncertainties.csv", index_col=0)
# Initialize PMF with 5 factors
pmf = PMF(n_components=5, random_state=42)
# Fit the model
pmf.fit(concentrations, uncertainties)
# Access results
factor_contributions = pmf.contributions_ # Time series of factor contributions
factor_profiles = pmf.profiles_ # Chemical profiles of each factor
# Check model performance
q_value = pmf.score(concentrations, uncertainties)
print(f"Model Q-value: {q_value:.2f}")
print(f"Converged: {pmf.converged_}")
print(f"Iterations: {pmf.n_iter_}")
Command Line Interface
# Analyze a single dataset interactively
easy-pmf
# Or use the analysis scripts directly
python quick_analysis.py
📊 Included Example Datasets
The package comes with three real-world datasets:
- Baton Rouge: Air quality data (307 samples × 41 species)
- St. Louis: Environmental monitoring data (418 samples × 13 species)
- Baltimore: PM2.5 composition data (657 samples × 26 species)
🎯 Use Cases
- Air Quality Analysis: Source apportionment of particulate matter
- Environmental Monitoring: Identifying pollution sources
- Research: Academic studies requiring PMF analysis
- Regulatory Compliance: EPA-style PMF analysis for reporting
📈 Visualization Capabilities
Easy PMF automatically generates comprehensive visualizations:
- Factor Profiles: Chemical signatures of each source
- Factor Contributions: Time series showing source strength
- Correlation Matrices: Relationships between factors
- EPA-style Plots: Publication-ready visualizations
- Summary Dashboards: Quick overview of results
📚 Documentation
PMF Class Parameters
n_components(int): Number of factors to extractmax_iter(int, default=1000): Maximum iterationstol(float, default=1e-4): Convergence tolerancerandom_state(int, optional): Random seed for reproducibility
Methods
fit(X, U=None): Fit PMF model to datatransform(X, U=None): Apply fitted model to new datascore(X, U=None): Calculate Q-value for goodness of fit
Data Format Requirements
- Concentrations: Rows = time points, Columns = chemical species
- Uncertainties: Same format as concentrations (optional)
- Index: Date/time information
- Values: Non-negative concentrations
🛠️ Advanced Usage
Custom Analysis Pipeline
from easy_pmf import PMF
import matplotlib.pyplot as plt
# Load and preprocess data
concentrations = pd.read_csv("data.csv", index_col=0)
uncertainties = pd.read_csv("uncertainties.csv", index_col=0)
# Remove low-signal species
concentrations = concentrations.loc[:, (concentrations > 0).any(axis=0)]
uncertainties = uncertainties[concentrations.columns]
# Try different numbers of factors
for n_factors in range(3, 8):
pmf = PMF(n_components=n_factors, random_state=42)
pmf.fit(concentrations, uncertainties)
q_value = pmf.score(concentrations, uncertainties)
print(f"Factors: {n_factors}, Q-value: {q_value:.2f}")
# Analyze best model
best_pmf = PMF(n_components=5, random_state=42)
best_pmf.fit(concentrations, uncertainties)
# Custom visualization
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
# Plot factor profiles
best_pmf.profiles_.T.plot(kind='bar', ax=ax1)
ax1.set_title('Factor Profiles')
ax1.set_xlabel('Chemical Species')
# Plot contributions over time
best_pmf.contributions_.plot(ax=ax2)
ax2.set_title('Factor Contributions Over Time')
ax2.set_ylabel('Contribution')
plt.tight_layout()
plt.show()
Batch Processing Multiple Datasets
import os
from easy_pmf import PMF
datasets = {
"site1": {"conc": "site1_conc.csv", "unc": "site1_unc.csv"},
"site2": {"conc": "site2_conc.csv", "unc": "site2_unc.csv"},
}
results = {}
for site, files in datasets.items():
print(f"Analyzing {site}...")
conc = pd.read_csv(files["conc"], index_col=0)
unc = pd.read_csv(files["unc"], index_col=0)
pmf = PMF(n_components=5, random_state=42)
pmf.fit(conc, unc)
results[site] = {
"contributions": pmf.contributions_,
"profiles": pmf.profiles_,
"q_value": pmf.score(conc, unc),
"converged": pmf.converged_
}
print(f" Q-value: {results[site]['q_value']:.2f}")
print(f" Converged: {results[site]['converged']}")
🔧 Development & Infrastructure
CI/CD Pipeline
This project features a comprehensive CI/CD infrastructure:
- ✅ Automated Testing: Matrix testing across Python 3.9-3.12 on Ubuntu, macOS, and Windows
- ✅ Code Quality: Automated linting, formatting, and type checking with pre-commit hooks
- ✅ Security Scanning: Dependency vulnerability scanning with Bandit
- ✅ Documentation: Automatic deployment to GitHub Pages
- ✅ Package Publishing: Automated PyPI publishing on releases
- ✅ Dependency Management: Weekly dependency updates and maintenance
Code Quality Standards
- Type Safety: Full type annotation coverage with mypy validation
- Code Style: Enforced with Ruff (linting and formatting)
- Testing: Comprehensive test suite with pytest
- Documentation: Auto-generated docs with MkDocs Material
- Pre-commit Hooks: Quality checks run on every commit using
uv
🤝 Contributing
🤝 Contributing
We welcome contributions! Please see our Contributing Guidelines for details on:
- Development setup with
uvand pre-commit hooks - Code quality standards and automated checks
- Testing requirements and CI/CD infrastructure
- Documentation guidelines and examples
- Pull request process and review requirements
Quick Start for Contributors
# Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/easy-pmf.git
cd easy-pmf
# Set up development environment
uv sync --all-extras
uv run pre-commit install
# Make changes and test
uv run pytest
uv run pre-commit run --all-files
Development Setup
git clone https://github.com/gerritjandebruin/easy-pmf.git
cd easy-pmf
# Install uv (modern Python package manager)
# On Windows: https://docs.astral.sh/uv/getting-started/installation/
# On macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh
# Create development environment and install dependencies
uv sync --all-extras
# Install pre-commit hooks for code quality
uv run pre-commit install
# Run tests to verify setup
uv run pytest
# Run type checking
uv run mypy .
# Run code formatting and linting
uv run ruff check --fix
uv run ruff format
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- EPA PMF software for inspiration
- Marloes van Os for first contributions and ideas
📞 Support
- Documentation: https://gerritjandebruin.github.io/easy-pmf/
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Easy PMF - Making positive matrix factorization accessible to everyone! 🌍
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easy_pmf-0.1.0.tar.gz.
File metadata
- Download URL: easy_pmf-0.1.0.tar.gz
- Upload date:
- Size: 200.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
338f6c61d761d63d178906c556995c88fd5df8bf64290740950f0a70b74f15ac
|
|
| MD5 |
a5fa451bad5c5749c3f8974af0659a42
|
|
| BLAKE2b-256 |
77193f7fa649484dcdf2bf052e097ec907dcad766baf9811b947ec05de8c9dba
|
Provenance
The following attestation bundles were made for easy_pmf-0.1.0.tar.gz:
Publisher:
publish.yml on gerritjandebruin/easy-pmf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easy_pmf-0.1.0.tar.gz -
Subject digest:
338f6c61d761d63d178906c556995c88fd5df8bf64290740950f0a70b74f15ac - Sigstore transparency entry: 373636323
- Sigstore integration time:
-
Permalink:
gerritjandebruin/easy-pmf@a8ddd66103ce8a068f751e3115a49d48dcd4a89b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/gerritjandebruin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8ddd66103ce8a068f751e3115a49d48dcd4a89b -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file easy_pmf-0.1.0-py3-none-any.whl.
File metadata
- Download URL: easy_pmf-0.1.0-py3-none-any.whl
- Upload date:
- Size: 197.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
010e423f0d1ebd5f3edc2631307ab496de77f6615fa6da4b861e24250f3ab89e
|
|
| MD5 |
ad6514b5922cf02c519440821b041e43
|
|
| BLAKE2b-256 |
4c00b914b505866f5dc2999c5116ccaa282c864f382c3e3bc40b8435544c8b78
|
Provenance
The following attestation bundles were made for easy_pmf-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on gerritjandebruin/easy-pmf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easy_pmf-0.1.0-py3-none-any.whl -
Subject digest:
010e423f0d1ebd5f3edc2631307ab496de77f6615fa6da4b861e24250f3ab89e - Sigstore transparency entry: 373636345
- Sigstore integration time:
-
Permalink:
gerritjandebruin/easy-pmf@a8ddd66103ce8a068f751e3115a49d48dcd4a89b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/gerritjandebruin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8ddd66103ce8a068f751e3115a49d48dcd4a89b -
Trigger Event:
workflow_dispatch
-
Statement type: