{"id":328017,"name":"ESAT","description":"An open-source software package that provides API and CLI functionality to create source apportionment workflows specifically targeting environmental datasets.","url":"https://github.com/quanted/esat","last_synced_at":"2026-04-27T16:30:16.013Z","repository":{"id":215165443,"uuid":"563361014","full_name":"quanted/esat","owner":"quanted","description":"EPA's Environmental Source Apportionment Toolkit (ESAT)  python package","archived":false,"fork":false,"pushed_at":"2025-11-14T13:58:14.000Z","size":544172,"stargazers_count":24,"open_issues_count":13,"forks_count":12,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-04-12T09:02:14.459Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://quanted.github.io/esat/","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quanted.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-08T13:05:17.000Z","updated_at":"2026-04-07T04:14:45.000Z","dependencies_parsed_at":"2024-01-17T00:36:35.409Z","dependency_job_id":"c9309415-59ee-4582-9400-9f40d62b2987","html_url":"https://github.com/quanted/esat","commit_stats":null,"previous_names":["quanted/nmf_py","quanted/esat"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/quanted/esat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted","download_url":"https://codeload.github.com/quanted/esat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31791171,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"owner":{"login":"quanted","name":"quanted","uuid":"16341712","kind":"organization","description":"","email":null,"website":null,"location":null,"twitter":null,"company":null,"icon_url":"https://avatars.githubusercontent.com/u/16341712?v=4","repositories_count":93,"last_synced_at":"2024-04-16T02:52:08.762Z","metadata":{"has_sponsors_listing":false},"html_url":"https://github.com/quanted","funding_links":[],"total_stars":62,"followers":4,"following":0,"created_at":"2022-11-04T17:55:50.231Z","updated_at":"2024-04-16T02:52:33.361Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted/repositories"},"packages":[{"id":11155062,"name":"esat","ecosystem":"pypi","description":"EPA's Environmental Source Apportionment Toolkit python package.","homepage":null,"licenses":"Apache-2.0","normalized_licenses":["Apache-2.0"],"repository_url":"https://github.com/quanted/esat","keywords_array":[],"namespace":null,"versions_count":2,"first_release_published_at":"2024-12-10T17:08:44.000Z","latest_release_published_at":"2025-06-23T17:57:53.000Z","latest_release_number":"2025.0.1","last_synced_at":"2026-04-24T15:01:16.768Z","created_at":"2024-12-10T17:16:07.034Z","updated_at":"2026-04-24T15:01:16.768Z","registry_url":"https://pypi.org/project/esat/","install_command":"pip install esat --index-url https://pypi.org/simple","documentation_url":"https://quanted.github.io/esat/","metadata":{"funding":null,"documentation":"https://quanted.github.io/esat/","classifiers":["Development Status :: 3 - Alpha","Programming Language :: Python","Programming Language :: Python :: Implementation :: CPython","Programming Language :: Python :: Implementation :: PyPy","Programming Language :: Rust"],"normalized_name":"esat","project_status":null},"repo_metadata":{"id":215165443,"uuid":"563361014","full_name":"quanted/esat","owner":"quanted","description":"EPA's Environmental Source Apportionment Toolkit (ESAT)  python package","archived":false,"fork":false,"pushed_at":"2025-11-14T13:58:14.000Z","size":544172,"stargazers_count":24,"open_issues_count":13,"forks_count":12,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-04-12T09:02:14.459Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://quanted.github.io/esat/","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quanted.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-08T13:05:17.000Z","updated_at":"2026-04-07T04:14:45.000Z","dependencies_parsed_at":"2024-01-17T00:36:35.409Z","dependency_job_id":"c9309415-59ee-4582-9400-9f40d62b2987","html_url":"https://github.com/quanted/esat","commit_stats":null,"previous_names":["quanted/nmf_py","quanted/esat"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/quanted/esat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted","download_url":"https://codeload.github.com/quanted/esat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31791171,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"},"owner_record":{"login":"quanted","name":"quanted","uuid":"16341712","kind":"organization","description":"","email":null,"website":null,"location":null,"twitter":null,"company":null,"icon_url":"https://avatars.githubusercontent.com/u/16341712?v=4","repositories_count":93,"last_synced_at":"2024-04-16T02:52:08.762Z","metadata":{"has_sponsors_listing":false},"html_url":"https://github.com/quanted","funding_links":[],"total_stars":62,"followers":4,"following":0,"created_at":"2022-11-04T17:55:50.231Z","updated_at":"2024-04-16T02:52:33.361Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quanted/repositories"},"tags":[{"name":"v2025.0.1","sha":"f1547eb0487d919f5fc2935e7c8c72a67378b2f2","kind":"commit","published_at":"2025-06-23T17:35:40.000Z","download_url":"https://codeload.github.com/quanted/esat/tar.gz/v2025.0.1","html_url":"https://github.com/quanted/esat/releases/tag/v2025.0.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/quanted/esat@v2025.0.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags/v2025.0.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags/v2025.0.1/manifests"},{"name":"v2024.0.3-alpha","sha":"019401e5222eefaab4cfb8713595a2438148a65b","kind":"commit","published_at":"2024-11-19T14:54:57.000Z","download_url":"https://codeload.github.com/quanted/esat/tar.gz/v2024.0.3-alpha","html_url":"https://github.com/quanted/esat/releases/tag/v2024.0.3-alpha","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/quanted/esat@v2024.0.3-alpha","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags/v2024.0.3-alpha","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/tags/v2024.0.3-alpha/manifests"}]},"repo_metadata_updated_at":"2026-04-18T14:45:07.941Z","dependent_packages_count":0,"downloads":76,"downloads_period":"last-month","dependent_repos_count":0,"rankings":{"downloads":null,"dependent_repos_count":55.77697854303709,"dependent_packages_count":9.909521894811455,"stargazers_count":null,"forks_count":null,"docker_downloads_count":null,"average":32.84325021892427},"purl":"pkg:pypi/esat","advisories":[],"docker_usage_url":"https://docker.ecosyste.ms/usage/pypi/esat","docker_dependents_count":null,"docker_downloads_count":null,"usage_url":"https://repos.ecosyste.ms/usage/pypi/esat","dependent_repositories_url":"https://repos.ecosyste.ms/api/v1/usage/pypi/esat/dependencies","status":null,"funding_links":[],"critical":null,"issue_metadata":{"last_synced_at":"2026-04-15T01:34:20.867Z","issues_count":18,"pull_requests_count":29,"avg_time_to_close_issue":415691.6666666667,"avg_time_to_close_pull_request":104.73913043478261,"issues_closed_count":6,"pull_requests_closed_count":23,"pull_request_authors_count":2,"issue_authors_count":8,"avg_comments_per_issue":0.5555555555555556,"avg_comments_per_pull_request":0.0,"merged_pull_requests_count":23,"bot_issues_count":0,"bot_pull_requests_count":0,"past_year_issues_count":5,"past_year_pull_requests_count":7,"past_year_avg_time_to_close_issue":null,"past_year_avg_time_to_close_pull_request":209.0,"past_year_issues_closed_count":0,"past_year_pull_requests_closed_count":5,"past_year_pull_request_authors_count":2,"past_year_issue_authors_count":4,"past_year_avg_comments_per_issue":0.4,"past_year_avg_comments_per_pull_request":0.0,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":0,"past_year_merged_pull_requests_count":5,"issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/issues","maintainers":[],"active_maintainers":[]},"versions_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/esat/versions","version_numbers_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/esat/version_numbers","dependent_packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/esat/dependent_packages","related_packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/esat/related_packages","codemeta_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/esat/codemeta","maintainers":[{"uuid":"dbsmith","login":"dbsmith","name":null,"email":null,"url":null,"packages_count":1,"html_url":"https://pypi.org/user/dbsmith/","role":"Owner","created_at":"2024-12-10T17:38:25.618Z","updated_at":"2024-12-10T17:38:25.618Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/dbsmith/packages"}],"registry":{"name":"pypi.org","url":"https://pypi.org","ecosystem":"pypi","default":true,"packages_count":850257,"maintainers_count":363283,"namespaces_count":0,"keywords_count":271316,"github":"pypi","metadata":{"funded_packages_count":53172},"icon_url":"https://github.com/pypi.png","created_at":"2022-04-04T15:19:23.364Z","updated_at":"2026-04-09T05:08:03.587Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages","maintainers_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers","namespaces_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/namespaces"}}],"commits":{"id":9624647,"full_name":"quanted/esat","default_branch":"main","total_commits":485,"total_committers":3,"total_bot_commits":0,"total_bot_committers":0,"mean_commits":161.66666666666666,"dds":0.08041237113402067,"past_year_total_commits":58,"past_year_total_committers":2,"past_year_total_bot_commits":0,"past_year_total_bot_committers":0,"past_year_mean_commits":29.0,"past_year_dds":0.017241379310344862,"last_synced_at":"2026-04-24T15:01:23.954Z","last_synced_commit":"82d25373bf90473b2d4bee10a48aad66bf443a13","created_at":"2025-04-11T10:31:28.787Z","updated_at":"2026-04-24T15:01:23.820Z","committers":[{"name":"Deron Smith","email":"deron.brock@gmail.com","login":"deronsmith","count":446},{"name":"mikecyterski","email":"cyterski.mike@epa.gov","login":"mikecyterski","count":38},{"name":"leahgibson","email":"ldgibson@rams.colostate.edu","login":"leahgibson","count":1}],"past_year_committers":[{"name":"Deron Smith","email":"deron.brock@gmail.com","login":"deronsmith","count":57},{"name":"leahgibson","email":"ldgibson@rams.colostate.edu","login":"leahgibson","count":1}],"commits_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/commits","host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-04-26T00:00:10.019Z","repositories_count":6218986,"commits_count":899349391,"contributors_count":34873943,"owners_count":1145706,"icon_url":"https://github.com/github.png","host_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories"}},"issues_stats":{"full_name":"quanted/esat","html_url":"https://github.com/quanted/esat","last_synced_at":"2026-04-22T14:00:37.114Z","status":"error","issues_count":18,"pull_requests_count":29,"avg_time_to_close_issue":415691.6666666667,"avg_time_to_close_pull_request":104.73913043478261,"issues_closed_count":6,"pull_requests_closed_count":23,"pull_request_authors_count":2,"issue_authors_count":8,"avg_comments_per_issue":0.5555555555555556,"avg_comments_per_pull_request":0.0,"merged_pull_requests_count":23,"bot_issues_count":0,"bot_pull_requests_count":0,"past_year_issues_count":5,"past_year_pull_requests_count":7,"past_year_avg_time_to_close_issue":null,"past_year_avg_time_to_close_pull_request":209.0,"past_year_issues_closed_count":0,"past_year_pull_requests_closed_count":5,"past_year_pull_request_authors_count":2,"past_year_issue_authors_count":4,"past_year_avg_comments_per_issue":0.4,"past_year_avg_comments_per_pull_request":0.0,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":0,"past_year_merged_pull_requests_count":5,"created_at":"2025-04-11T10:31:28.872Z","updated_at":"2026-04-22T14:00:37.114Z","repository_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat","issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/quanted%2Fesat/issues","issue_labels_count":{"update":4,"bug":2},"pull_request_labels_count":{},"issue_author_associations_count":{"NONE":10,"CONTRIBUTOR":8},"pull_request_author_associations_count":{"CONTRIBUTOR":28,"NONE":1},"issue_authors":{"deronsmith":7,"ifoxfoot":3,"leahgibson":2,"niravlekinwala":2,"wereinwild":1,"mengqi-z":1,"jiangwy85":1,"collord":1},"pull_request_authors":{"deronsmith":28,"collord":1},"host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-04-24T00:00:07.455Z","repositories_count":14385347,"issues_count":34465891,"pull_requests_count":112845799,"authors_count":11244256,"icon_url":"https://github.com/github.png","host_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories","owners_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/owners","authors_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors"},"past_year_issue_labels_count":{},"past_year_pull_request_labels_count":{},"past_year_issue_author_associations_count":{"NONE":4,"CONTRIBUTOR":1},"past_year_pull_request_author_associations_count":{"CONTRIBUTOR":6,"NONE":1},"past_year_issue_authors":{"leahgibson":2,"collord":1,"jiangwy85":1,"wereinwild":1},"past_year_pull_request_authors":{"deronsmith":6,"collord":1},"maintainers":[],"active_maintainers":[]},"events":{"total":{"ReleaseEvent":5,"DeleteEvent":4,"PullRequestEvent":23,"ForkEvent":9,"IssuesEvent":12,"WatchEvent":11,"IssueCommentEvent":7,"PushEvent":113,"PullRequestReviewEvent":5,"PullRequestReviewCommentEvent":9,"CreateEvent":3},"last_year":{"ReleaseEvent":4,"DeleteEvent":1,"PullRequestEvent":6,"ForkEvent":4,"IssuesEvent":2,"WatchEvent":6,"PushEvent":38,"PullRequestReviewEvent":5,"PullRequestReviewCommentEvent":9,"CreateEvent":1}},"keywords":[],"dependencies":[{"ecosystem":"actions","filepath":".github/workflows/CI.yml","sha":null,"kind":"manifest","created_at":"2024-01-02T23:58:52.559Z","updated_at":"2024-01-02T23:58:52.559Z","repository_link":"https://github.com/quanted/esat/blob/main/.github/workflows/CI.yml","dependencies":[{"id":15400205306,"package_name":"actions/checkout","ecosystem":"actions","requirements":"v3","direct":true,"kind":"composite","optional":false},{"id":15400205307,"package_name":"actions/setup-python","ecosystem":"actions","requirements":"v4","direct":true,"kind":"composite","optional":false},{"id":15400205308,"package_name":"PyO3/maturin-action","ecosystem":"actions","requirements":"v1","direct":true,"kind":"composite","optional":false},{"id":15400205309,"package_name":"actions/upload-artifact","ecosystem":"actions","requirements":"v3","direct":true,"kind":"composite","optional":false},{"id":15400205310,"package_name":"actions/download-artifact","ecosystem":"actions","requirements":"v3","direct":true,"kind":"composite","optional":false}]},{"ecosystem":"cargo","filepath":"Cargo.toml","sha":null,"kind":"manifest","created_at":"2024-01-02T23:58:52.907Z","updated_at":"2024-01-02T23:58:52.907Z","repository_link":"https://github.com/quanted/esat/blob/main/Cargo.toml","dependencies":[]},{"ecosystem":"pypi","filepath":"pyproject.toml","sha":null,"kind":"manifest","created_at":"2024-01-02T23:59:02.284Z","updated_at":"2024-01-02T23:59:02.284Z","repository_link":"https://github.com/quanted/esat/blob/main/pyproject.toml","dependencies":[]},{"ecosystem":"pypi","filepath":"setup.py","sha":null,"kind":"manifest","created_at":"2024-01-02T23:59:02.770Z","updated_at":"2024-01-02T23:59:02.770Z","repository_link":"https://github.com/quanted/esat/blob/main/setup.py","dependencies":[]},{"ecosystem":"conda","filepath":"environment.yml","sha":null,"kind":"manifest","created_at":"2024-01-17T00:36:33.540Z","updated_at":"2024-01-17T00:36:33.540Z","repository_link":"https://github.com/quanted/esat/blob/main/environment.yml","dependencies":[{"id":15653571411,"package_name":"brotli","ecosystem":"conda","requirements":"1.0.9.*","direct":true,"kind":"runtime","optional":false},{"id":15653571412,"package_name":"brotli-bin","ecosystem":"conda","requirements":"1.0.9.*","direct":true,"kind":"runtime","optional":false},{"id":15653571413,"package_name":"bzip2","ecosystem":"conda","requirements":"1.0.8.*","direct":true,"kind":"runtime","optional":false},{"id":15653571414,"package_name":"ca-certificates","ecosystem":"conda","requirements":"2023.5.7.*","direct":true,"kind":"runtime","optional":false},{"id":15653571415,"package_name":"certifi","ecosystem":"conda","requirements":"2023.5.7.*","direct":true,"kind":"runtime","optional":false},{"id":15653571416,"package_name":"charset-normalizer","ecosystem":"conda","requirements":"3.1.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571417,"package_name":"colorama","ecosystem":"conda","requirements":"0.4.6.*","direct":true,"kind":"runtime","optional":false},{"id":15653571418,"package_name":"idna","ecosystem":"conda","requirements":"3.4.*","direct":true,"kind":"runtime","optional":false},{"id":15653571419,"package_name":"intel-openmp","ecosystem":"conda","requirements":"2023.1.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571420,"package_name":"libblas","ecosystem":"conda","requirements":"3.9.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571421,"package_name":"libbrotlicommon","ecosystem":"conda","requirements":"1.0.9.*","direct":true,"kind":"runtime","optional":false},{"id":15653571422,"package_name":"libbrotlidec","ecosystem":"conda","requirements":"1.0.9.*","direct":true,"kind":"runtime","optional":false},{"id":15653571423,"package_name":"libbrotlienc","ecosystem":"conda","requirements":"1.0.9.*","direct":true,"kind":"runtime","optional":false},{"id":15653571424,"package_name":"libcblas","ecosystem":"conda","requirements":"3.9.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571425,"package_name":"libexpat","ecosystem":"conda","requirements":"2.5.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571426,"package_name":"libffi","ecosystem":"conda","requirements":"3.4.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571427,"package_name":"libhwloc","ecosystem":"conda","requirements":"2.9.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571613,"package_name":"libiconv","ecosystem":"conda","requirements":"1.17.*","direct":true,"kind":"runtime","optional":false},{"id":15653571614,"package_name":"liblapack","ecosystem":"conda","requirements":"3.9.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571615,"package_name":"libsqlite","ecosystem":"conda","requirements":"3.42.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571616,"package_name":"libxml2","ecosystem":"conda","requirements":"2.11.4.*","direct":true,"kind":"runtime","optional":false},{"id":15653571617,"package_name":"libzlib","ecosystem":"conda","requirements":"1.2.13.*","direct":true,"kind":"runtime","optional":false},{"id":15653571618,"package_name":"m2w64-gcc-libgfortran","ecosystem":"conda","requirements":"5.3.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571619,"package_name":"m2w64-gcc-libs","ecosystem":"conda","requirements":"5.3.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571620,"package_name":"m2w64-gcc-libs-core","ecosystem":"conda","requirements":"5.3.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571621,"package_name":"m2w64-gmp","ecosystem":"conda","requirements":"6.1.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571622,"package_name":"m2w64-libwinpthread-git","ecosystem":"conda","requirements":"5.0.0.4634.697f757.*","direct":true,"kind":"runtime","optional":false},{"id":15653571623,"package_name":"mkl","ecosystem":"conda","requirements":"2022.1.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571624,"package_name":"msys2-conda-epoch","ecosystem":"conda","requirements":"20160418.*","direct":true,"kind":"runtime","optional":false},{"id":15653571625,"package_name":"numpy","ecosystem":"conda","requirements":"1.24.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571626,"package_name":"openssl","ecosystem":"conda","requirements":"3.1.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571627,"package_name":"packaging","ecosystem":"conda","requirements":"23.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571628,"package_name":"pandas","ecosystem":"conda","requirements":"2.0.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571629,"package_name":"pip","ecosystem":"conda","requirements":"23.1.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571630,"package_name":"platformdirs","ecosystem":"conda","requirements":"3.5.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571631,"package_name":"pooch","ecosystem":"conda","requirements":"1.7.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571632,"package_name":"pthreads-win32","ecosystem":"conda","requirements":"2.9.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571793,"package_name":"pysocks","ecosystem":"conda","requirements":"1.7.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571794,"package_name":"python","ecosystem":"conda","requirements":"3.11.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571795,"package_name":"python-dateutil","ecosystem":"conda","requirements":"2.8.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571796,"package_name":"python-tzdata","ecosystem":"conda","requirements":"2023.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571797,"package_name":"python_abi","ecosystem":"conda","requirements":"3.11.*","direct":true,"kind":"runtime","optional":false},{"id":15653571798,"package_name":"pytz","ecosystem":"conda","requirements":"2023.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571799,"package_name":"requests","ecosystem":"conda","requirements":"2.31.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571800,"package_name":"scipy","ecosystem":"conda","requirements":"1.10.1.*","direct":true,"kind":"runtime","optional":false},{"id":15653571801,"package_name":"setuptools","ecosystem":"conda","requirements":"67.7.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571802,"package_name":"six","ecosystem":"conda","requirements":"1.16.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571803,"package_name":"tbb","ecosystem":"conda","requirements":"2021.9.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571804,"package_name":"tk","ecosystem":"conda","requirements":"8.6.12.*","direct":true,"kind":"runtime","optional":false},{"id":15653571805,"package_name":"tqdm","ecosystem":"conda","requirements":"4.65.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571806,"package_name":"typing-extensions","ecosystem":"conda","requirements":"4.6.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571807,"package_name":"typing_extensions","ecosystem":"conda","requirements":"4.6.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571808,"package_name":"tzdata","ecosystem":"conda","requirements":"2023c.*","direct":true,"kind":"runtime","optional":false},{"id":15653571809,"package_name":"ucrt","ecosystem":"conda","requirements":"10.0.22621.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571810,"package_name":"urllib3","ecosystem":"conda","requirements":"2.0.2.*","direct":true,"kind":"runtime","optional":false},{"id":15653571811,"package_name":"vc","ecosystem":"conda","requirements":"14.3.*","direct":true,"kind":"runtime","optional":false},{"id":15653571812,"package_name":"vc14_runtime","ecosystem":"conda","requirements":"14.34.31931.*","direct":true,"kind":"runtime","optional":false},{"id":15653571813,"package_name":"vs2015_runtime","ecosystem":"conda","requirements":"14.34.31931.*","direct":true,"kind":"runtime","optional":false},{"id":15653571814,"package_name":"wheel","ecosystem":"conda","requirements":"0.40.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571815,"package_name":"win_inet_pton","ecosystem":"conda","requirements":"1.1.0.*","direct":true,"kind":"runtime","optional":false},{"id":15653571816,"package_name":"xz","ecosystem":"conda","requirements":"5.2.6.*","direct":true,"kind":"runtime","optional":false}]}],"score":9.053335623166019,"created_at":"2025-10-26T17:05:15.455Z","updated_at":"2026-04-27T16:30:16.016Z","avatar_url":"https://github.com/quanted.png","language":"Jupyter Notebook","category":"Natural Resources","sub_category":"Water Supply and Quality","monthly_downloads":76,"total_dependent_repos":0,"total_dependent_packages":0,"readme":"# Environmental Source Apportionment Toolkit (ESAT)\r\nLast Update: 04-16-2025\r\n\r\n## Table of Contents\r\n - [Description](#description)\r\n - [Quick Start](#quick-start)\r\n   - [Installation](#installation) \r\n   - [Example Code](#example-code)\r\n - [Background](#background)\r\n    - [ESAT Predecessor](#esat-predecessor)\r\n    - [Features](#features)\r\n    - [Notebooks](#notebooks)\r\n - [Development](#development)\r\n    - [Requirements](#requirements)\r\n    - [Rust Compiling](#rust-compiling)\r\n    - [Creating Docs](#creating-docs)\r\n    - [Building Wheels and Compiling Rust](#building-wheels-and-compiling-rust)\r\n    - [Code Tests](#code-tests)\r\n    - [Community Contributions](#community-contributions)\r\n\r\n## Description\r\nThe Environmental Source Apportionment Toolkit (ESAT) is an open-source software package that provides API and CLI \r\nfunctionality to create source apportionment workflows specifically targeting environmental datasets. Source \r\napportionment in environment science is the process of mathematically estimating the profiles and contributions of \r\nmultiple sources in some dataset, and in the case of ESAT, while considering data uncertainty. There are many potential \r\nuse cases for source apportionment in environmental science research, such as in the fields of air quality, water \r\nquality and potentially many others.\r\n\r\nThe ESAT toolkit is written in Python and Rust, and uses common packages such as numpy, scipy and pandas for data \r\nprocessing. The source apportionment algorithms provided in ESAT include two variants of non-negative matrix \r\nfactorization (NMF), both of which have been written in Rust and contained within the python package. A collection of \r\ndata processing and visualization features are included for data and model analytics. The ESAT package includes a \r\nsynthetic data generator and comparison tools to evaluate ESAT model outputs.\r\n\r\nThe ESAT python package has been published with the Journal of Open Source Software (JOSS):\r\n\r\n[![DOI](https://joss.theoj.org/papers/10.21105/joss.07316/status.svg)](https://doi.org/10.21105/joss.07316)\r\n\r\n## Quick Start\r\n\r\nESAT supports python version 3.10, 3.11, and 3.12. As the python package contains compiled code there are OS and python\r\nspecific versions, supporting Mac Intel, Mac M1+, Linux, and Windows.\r\n\r\n### Documentation\r\nThe Python API and CLI documentation can be found at the GitHub ESAT IO site: [https://quanted.github.io/esat/](https://quanted.github.io/esat/)\r\n\r\n### Installation\r\nThe ESAT python package contains all compiled code and required dependencies and can be installed using pip\r\n```bash\r\npip install esat\r\n```\r\nwhich will install the latest version that supports and is available for your python version and OS.\r\n\r\nDevelopment versions of ESAT can be found on the GitHub actions page, for logged-in users, \r\nunder the 'Build and Publish Wheel' workflow. The latest version of the package will be available as an artifact for \r\ndownload in the 'Artifacts' section of the completed workflow. There wheel files can be found for specific versions\r\nof python and supported operating systems. \r\n\r\nIf an error message appears during installation stating that the \r\npackage is not supported check that the correct OS and python version are being installed for that system. The python \r\nwheels can be installed directly using \r\n```bash\r\npip install \u003cwheel file name\u003e\r\n```\r\nThe esat python package is recommended to be installed in its own dedicated python virtual environment or conda environment.\r\n\r\nTo run the jupyter notebooks, install jupyterlab into the esat python environmental\r\n```bash\r\npip install jupyterlab\r\n```\r\n\r\n### Example Code\r\nJupyter notebooks containing complete code examples, using sample datasets, are available for the \r\n[source apportionment workflow](notebooks/epa_esat_workflow_01.ipynb) and the [simulator workflow](notebooks/epa_esat_simulator_01.ipynb). \r\n\r\n## Background\r\n\r\n### ESAT Predecessor\r\nA widely used application used for environmental source apportionment is the EPA's Positive Matrix Factorization version\r\n5 (PMF5), which is a broadly used tool with an international user community. The PMF5 application is a mathematical tool\r\nthat processes a dataset of feature concentrations across many samples (and concentration uncertainties) to estimate a \r\nset of source profiles and their contributions. PMF5 can be used on a wide range of environmental data and is a powerful\r\ntool for estimating source apportionment: \r\nhttps://www.epa.gov/air-research/positive-matrix-factorization-model-environmental-data-analyses\r\n\r\nPMF5 was released in 2014 and is no longer being supported. The math engine used in PMF5 is proprietary and the source\r\ncode has not been made public. One of the primary purposes of ESAT was to recreate the source apportionment workflow and\r\nmathematics as an open-source software package to offer a modernized option for environmental source apportionment. \r\nOther reasons for developing ESAT was to offer increased maintainability, development efficient, thorough documentation,\r\nmodern optimizations, new features and customized workflows for novel use cases.\r\n\r\n### Features\r\nESAT python package focuses on source apportionment estimates using NMF algorithms. These\r\nalgorithms are implemented both in python using numpy functions and in Rust (default) for an optimization option. The\r\ntwo currently available are:\r\n 1. LS-NMF: Least-squares NMF, a well documented and widely uses NMF algorithm. The ls-nmf algorithm is available in the NMF R package.\r\n\r\n 2. WS-NMF: Weight-Semi NMF, a variant of the NMF algorithm which allows for negative values in both the input data matrix and in the factor contribution matrix.\r\n\r\nSource apportionment solution error estimation methods are also available, which are the same methods that are found in PMF5.\r\nThese are:\r\n 1. Bootstrap (BS): the input dataset is divided into multiple blocks and randomly reassembled to attempt to quantify the variability in the factor profiles and contributions.\r\n\r\n 2. Displacement (DISP): the solution factor profiles are all individually shifted (both increased and decreased) to determine the amount of change required for the loss value to reach specific dQ (change in Q) values.\r\n\r\n 3. Bootstrap-Displacement (BS-DISP): the combination of the two error estimation methods. Where for each Bootstrap dataset/model, all or targeted factor profile values are adjusted using the DISP method.\r\n\r\nESAT includes constrained models, as found in PMF5, where selecting a source apportionment model there is the option to add constraints through defining specific value constraints or define value correlations as a collection of linear equations.\r\n\r\nLastly, ESAT includes a data simulator which allows for random or use defined synthetic source profiles and contributions to be used in ESAT to evaluating how well the original synthetic data can be recreated.\r\n\r\n### Limitations\r\nMatrix factorization algorithms are fall under the group of optimization, or minimization, algorithms which attempt to \r\nfind a minima based upon some loss function and stopping condition. These algorithms are classified as NP-Complete, a \r\ncategory of algorithms which are nondeterministic polynomial time complete and there is no known way to quickly find a \r\nsolution. Given this limitation of NMF, a solution can only be considered a local minima with no known way to guarantee \r\nor prove it is the globally optimal solution. One approach for helping determine that a solution is a good solution is \r\nby producing many such solutions with a constricted convergence criteria or stopping condition. Then evaluating these \r\nsolutions to determine which, if any, correspond to the best actual representation or model of the data given domain \r\nknowledge and expertise. \r\n\r\nNMF algorithms are data-agnostic, operates the same on any correctly structured data regardless of domain, potential \r\nleading to another limitation, interpretation of the solution. An important component in evaluating whether or not to \r\nuse ESAT, or any NMF algorithm, on a dataset is to determine how to interpret factor profiles and contributions. How \r\nthis is done is fully dependent on the data and domain of the input dataset, such as units, types of features, temporal \r\nor spatial considerations, etc. \r\n\r\n\r\n### Notebooks\r\nJuypter notebooks are available that demonstrate the complete source apportionment and error estimation workflow found in PMF5, demonstrated in notebooks/epa_esat_workflow_01.ipynb\r\n\r\nThe simulator notebook provides examples for creating the synthetic profiles and contributions dataset and using the evaluation features to see how 'well' ESAT can recreate those profiles and contributions.\r\n\r\nOther notebooks are included which were used during development and verifying visualizations. \r\n\r\n## Development\r\n### Requirements\r\n* Core ESAT python package requirements can be found in the requirements.txt file.\r\n* The python requirements for creating the code documentation can be found in the doc-requirements.txt file.\r\n* Full development python package requirements can be found in the _dev-requirements.txt file (not actively maintained).\r\n\r\nThe ESAT python codebase includes github workflow actions which run:\r\n1. Run python build to compile the Rust code and create python packages for python 3.10, 3.11 and 3.12 on Linux, Windows and MacOS.\r\n2. Recreate code documentation from the README.md file, code docstrings for the Python API and CLI. Documentation is used to update the github documentation site for ESAT.\r\n\r\n### Rust Compiling\r\nThe python package includes a Rust module for running the algorithm update procedures, which requires local compiling to execute.\r\n\r\nTo run the Rust functions that is specified by the optimized parameter, requires that Rust is installed (https://www.rust-lang.org/tools/install) and the python package maturin (https://pypi.org/project/maturin/) is installed to the python development environment. \r\nThen from the python env and the project root, executing \u003ci\u003ematurin develop\u003c/i\u003e will compile the Rust code and place it in your python environment path. To compile optimized Rust code, include the \u003ci\u003e-r\u003c/i\u003e or \u003ci\u003e--release\u003c/i\u003e tags.\r\n\r\nThe Rust code can also be compiled to the target directory inside of project root using \u003ci\u003ematurin build\u003c/i\u003e.\r\n\r\nThe rust functions are imported as python functions, with the 'from esat_rust import esat_rust'.\r\n\r\nWhen creating the python package the pyproject.toml specifies that both setuptools and setuptool-rust are used. Setuptools-rust is required for compiling the Rust code during package build. \r\n\r\n### Creating Docs\r\nThe documentation is created using sphinx and several extensions.\r\n\r\nTo create or add new rst files run \u003ci\u003esphinx-apidoc -o docs esat\u003c/i\u003e.\r\n\r\nTo create, update or modify the existing documentation html, run \r\n\u003ci\u003esphinx-build -M html . docs\u003c/i\u003e from the command line at the project root directory running the python environment with the necessary sphinx packages.\r\n\r\n### Building Wheels and Compiling Rust\r\nThe ESAT python package and cli are built using setuptools and setuptools-rust, with configuration details defined in pyproject.toml and Cargo.toml. \r\n\r\nThe python package can be built with the standard \u003ci\u003epython -m build\u003c/i\u003e from the project root directory. \r\n\r\nBuild will compile the rust code and package up the python\r\ncode combining them into the wheel for distribution. The resulting wheel with the compiled code is available on github as a workflow artifact for the targeted architecture and python version.\r\n\r\nThe python package will be available on pypi.org in the near future.\r\n\r\n### Code Tests\r\nA collection of pytest tests have been created to test functionality, mostly as systems tests, which can be executed as \r\n\r\n\u003ci\u003ecoverage run -m pytest tests\u003c/i\u003e\r\n\r\nwith the coverage results displayed by \r\n\r\n\u003ci\u003ecoverage report\u003c/i\u003e\r\n\r\nWhile the overall coverage percentage is low, the majority of the untested code is for visualization functions with all \r\ncore functionality covered by tests.\r\n\r\n### Community Contributions\r\nFor those in the user community wishing to contribute to this project:\r\n * Code updates can be made through pull requests that will be reviewed by repository maintainers.\r\n * Software, code, or algorithm related bugs and issues can be submitted directly as issues on the GitHub repository.\r\n * Support can be requested through GitHub issues or through email at [esat@epa.gov](mailto:esat@epa.gov).\r\n\r\n## Disclaimer \r\nESAT development has been funded by U.S. EPA.  Mention of any trade names, products, or services does not convey, and \r\nshould not be interpreted as conveying, official EPA approval, endorsement, or recommendation. The views expressed in \r\nthis README are those of the authors and do not necessarily represent the views or policies of the US EPA.","funding_links":[],"readme_doi_urls":["https://doi.org/10.21105/joss.07316"],"works":{},"citation_counts":{},"total_citations":0,"keywords_from_contributors":[],"project_url":"https://ost.ecosyste.ms/api/v1/projects/328017","html_url":"https://ost.ecosyste.ms/projects/328017"}