diff --git a/.Rbuildignore b/.Rbuildignore index da54524e..51faea84 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,6 +1,5 @@ ^.*\.Rproj$ ^\.Rproj\.user$ -^\.travis\.yml$ ^codecov\.yml$ ^_pkgdown\.yml$ ^docs$ diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 00000000..ebec380e --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,89 @@ +name: test-coverage + +on: + push: + branches: [master, devel] + pull_request: + branches: [master, devel] + +permissions: read-all + +jobs: + test-coverage: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup R and Bioconductor + uses: grimbough/bioc-actions/setup-bioc@v1 + with: + bioc-version: devel + + - name: Install dependencies + run: | + # log4r (a dependency of our Imports:: MSstatsConvert) was archived + # (removed) from CRAN on 2026-07-14 for unresolved check issues -- + # see https://cran.r-project.org/web/packages/log4r/index.html. + # That's an upstream removal, not a repo-configuration problem, so + # no amount of options(repos=...) tinkering fixes it. Install the + # last released version straight from the CRAN Archive first, so + # it's already satisfied when we resolve the rest of the + # dependencies below. + # + # We also skip r-lib/actions/setup-r-dependencies@v2 (which + # delegates to pak). pak resolves dependencies in a persistent + # background subprocess (pak:::remote()) that only forwards options + # matching pkg.*/async_http_*/Ncpus/BioC_mirror and env vars + # matching PKG_*/R_BIOC_VERSION/PATH/PACKAGEMANAGER_* -- `repos` + # itself is never forwarded, so options(repos=...), ~/.Rprofile, + # and RENV_CONFIG_REPOS_OVERRIDE (an renv-only variable pak doesn't + # read at all) are all invisible to it, making it awkward to inject + # an already-installed/archived package like this. Installing + # directly with remotes::install_deps() runs in this same R + # session, so it just sees whatever we configure here directly. + repos <- BiocManager::repositories() + repos["CRAN"] <- "https://cloud.r-project.org" + options(repos = repos) + + install.packages(c("remotes", "sessioninfo", "xml2")) + install.packages( + "https://cran.r-project.org/src/contrib/Archive/log4r/log4r_0.4.4.tar.gz", + repos = NULL, + type = "source" + ) + remotes::install_deps(dependencies = TRUE, upgrade = "never") + shell: Rscript {0} + + - name: Test coverage + run: | + cov <- covr::package_coverage( + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + covr::to_cobertura(cov) + shell: Rscript {0} + + - uses: codecov/codecov-action@v4 + with: + file: ./cobertura.xml + plugin: noop + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + + - name: Show testthat output + if: always() + run: | + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/.github/workflows/update-citation-stats.yaml b/.github/workflows/update-citation-stats.yaml new file mode 100644 index 00000000..26008853 --- /dev/null +++ b/.github/workflows/update-citation-stats.yaml @@ -0,0 +1,37 @@ +name: update-citation-stats + +on: + schedule: + # 06:00 UTC on the 1st of every month + - cron: "0 6 1 * *" + workflow_dispatch: {} + +permissions: + contents: write + +jobs: + update-citation-stats: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: master + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Refresh citation-count table in README + run: python scripts/update_citation_counts.py + + - name: Commit and push if changed + run: | + if git diff --quiet -- README.md; then + echo "No change in citation counts." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add README.md + git commit -m "Update MSstats ecosystem citation counts" + git push origin HEAD:master diff --git a/.github/workflows/update-download-stats.yaml b/.github/workflows/update-download-stats.yaml new file mode 100644 index 00000000..f50bd830 --- /dev/null +++ b/.github/workflows/update-download-stats.yaml @@ -0,0 +1,37 @@ +name: update-download-stats + +on: + schedule: + # 06:00 UTC on the 1st of every month + - cron: "0 6 1 * *" + workflow_dispatch: {} + +permissions: + contents: write + +jobs: + update-download-stats: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: master + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Refresh download-statistics table in README + run: python scripts/update_download_stats.py + + - name: Commit and push if changed + run: | + if git diff --quiet -- README.md; then + echo "No change in download statistics." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add README.md + git commit -m "Update MSstats ecosystem download statistics" + git push origin HEAD:master diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4ae81311..00000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r - -language: r -os: linux -dist: xenial -cache: packages -warnings_are_errors: false - -after_success: - - Rscript -e 'covr::codecov()' -branches: - only: - - master - - develop -script: - - zip -r latest * - - mkdir -p deployment/msstats-dev - - mv latest.zip deployment/msstats-dev/latest.zip - -deploy: -- provider: s3 - access_key_id: $AWS_MASTER_ACCESS_ID - secret_access_key: $AWS_MASTER_SECRET_KEY - local_dir: deployment/msstats-dev - skip_cleanup: true - on: - branch: develop - repo: Vitek-Lab/MSstats-dev - bucket: $AWS_MASTER_S3_BUCKET - region: $AWS_REGION -- provider: codedeploy - access_key_id: $AWS_MASTER_ACCESS_ID - secret_access_key: $AWS_MASTER_SECRET_KEY - bucket: $AWS_MASTER_S3_BUCKET - key: latest.zip - bundle_type: zip - application: $AWS_MASTER_DEPLOYMENT_APPLICATION_NAME - deployment_group: $AWS_MASTER_DEPLOYMENT_GROUP_NAME - region: $AWS_REGION - on: - branch: develop diff --git a/README.md b/README.md index 2098239a..f6581a96 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,346 @@ -MSstats -======= +# MSstats - [![Travis build status](https://travis-ci.org/Vitek-Lab/MSstats-dev.svg?branch=master)](https://travis-ci.org/Vitek-Lab/MSstats-dev) -[![Codecov test coverage](https://codecov.io/gh/Vitek-Lab/MSstats-dev/branch/master/graph/badge.svg)](https://codecov.io/gh/Vitek-Lab/MSstats-dev?branch=master) +[![Bioconductor Release Build](https://bioconductor.org/shields/build/release/bioc/MSstats.svg)](https://bioconductor.org/checkResults/release/bioc-LATEST/MSstats/) +[![Bioconductor Devel Build](https://bioconductor.org/shields/build/devel/bioc/MSstats.svg)](https://bioconductor.org/checkResults/devel/bioc-LATEST/MSstats/) +[![Codecov test coverage](https://codecov.io/gh/Vitek-Lab/MSstats/branch/master/graph/badge.svg)](https://codecov.io/gh/Vitek-Lab/MSstats) +[![Bioconductor Downloads Rank](https://bioconductor.org/shields/downloads/release/MSstats.svg)](https://bioconductor.org/packages/stats/bioc/MSstats/) +[![Years in Bioconductor](https://bioconductor.org/shields/years-in-bioc/MSstats.svg)](https://bioconductor.org/packages/release/bioc/html/MSstats.html#since) +[![License: Artistic-2.0](https://img.shields.io/badge/license-Artistic--2.0-blue.svg)](https://opensource.org/licenses/Artistic-2.0) -MSstats is an R-based/Bioconductor package for statistical relative quantification of peptides and proteins in mass spectrometry-based proteomic experiments. It is applicable to multiple types of sample preparation, including label-free workflows, workflows that use stable isotope labeled reference proteins and peptides, and work-flows that use fractionation. It is applicable to targeted Selected Reactin Monitoring(SRM), Data-Dependent Acquisiton(DDA or shotgun), and Data-Independent Acquisition(DIA or SWATH-MS). This github page is for sharing source and testing. The official webpage is msstats.org +MSstats is an R/Bioconductor package for statistical relative quantification of +proteins and peptides in mass spectrometry-based proteomics experiments. It +supports label-free and label-based (isotope-labeled reference peptide/protein) +workflows, and is applicable to targeted Selected Reaction Monitoring (SRM), +Data-Dependent Acquisition (DDA/shotgun), and Data-Independent Acquisition +(DIA/SWATH-MS) experiments, including designs with fractionation. Given +identified and quantified peaks from upstream tools (Skyline, MaxQuant, +Proteome Discoverer, Spectronaut, DIA-NN, FragPipe, OpenSWATH, and others), +MSstats performs normalization, missing value imputation, feature selection, run-level +summarization, and model-based statistical testing to detect differentially +abundant proteins or peptides across conditions. Because the underlying +statistical framework operates on generic quantitative features, it can be easily extended to other quantitative signals, such as targeted metabolomics data. + +MSstats has been developed and maintained by the [Vitek Lab](https://olga-vitek-lab.khoury.northeastern.edu/) +at the Khoury College of Computer Sciences, Northeastern University, since +2012. The package and its documentation are also available at +[msstats.org](http://msstats.org). + +This repository is used for active development and testing of MSstats. The +package is released through [Bioconductor](https://bioconductor.org/packages/MSstats) +on its regular 6-month release cycle. + +## At a Glance + +- **13+ years** in Bioconductor (since release 2.13, 2013) +- **9 packages** in the MSstats ecosystem, covering DDA/DIA/SRM, TMT, PTMs, + LiP-MS, large-scale/out-of-memory data, network analysis, dose-response, and + a no-code GUI +- **13 peer-reviewed publications / preprints**, [~2,000+ citations](#citations) + combined (see [Citations](#citations)) +- **~6,0000 monthly downloads** across the ecosystem (see + [Download statistics](#download-statistics)), tracked automatically from + Bioconductor's own logs + +## The MSstats Ecosystem + +MSstats has grown into a family of packages that address distinct needs in MS-based proteomics analysis. Each package targets a different experiment type or stage +of the analysis pipeline: + +```mermaid +flowchart LR + A["Upstream search tools
Skyline · MaxQuant · Spectronaut
DIA-NN · FragPipe · OpenMS/OpenSWATH · ..."] --> B["MSstatsConvert
(format converters)"] + A --> G["MSstatsBig
(larger-than-memory converters)"] + B --> C["MSstats
DDA / DIA / SRM
label-free & label-based"] + G --> C + C --> D["MSstatsTMT
isobaric labeling"] + C --> E["MSstatsPTM
post-translational mods"] + C --> F["MSstatsLiP
limited proteolysis"] + C --> I["MSstatsResponse
dose-response"] + D --> H["MSstatsBioNet
network enrichment"] + E --> H + F --> H + I --> H + C --> H + C --> J["MSstatsShiny
point-and-click GUI"] + D --> J + E --> J + F --> J + I --> J + H --> J +``` + +| Package | Description | +| --- | --- | +| **[MSstats](https://bioconductor.org/packages/MSstats)** | Core package for DDA, SRM, and DIA label-free/label-based experiments. | +| **[MSstatsTMT](https://bioconductor.org/packages/MSstatsTMT)** ([GitHub](https://github.com/Vitek-Lab/MSstatsTMT)) | Statistical analysis of experiments with isobaric (TMT) labeling and multiple mixtures, including repeated-measures designs. | +| **[MSstatsPTM](https://bioconductor.org/packages/MSstatsPTM)** ([GitHub](https://github.com/Vitek-Lab/MSstatsPTM)) | Quantitative analysis of post-translational modifications (PTMs), jointly modeling PTM-site and protein-level abundance. | +| **[MSstatsLiP](https://bioconductor.org/packages/MSstatsLiP)** ([GitHub](https://github.com/Vitek-Lab/MSstatsLiP)) | Analysis of limited proteolysis mass spectrometry (LiP-MS) data to detect protein structural changes. | +| **[MSstatsBig](https://bioconductor.org/packages/MSstatsBig)** ([GitHub](https://github.com/Vitek-Lab/MSstatsBig)) | Converters and tooling for processing larger-than-memory quantitative datasets. | +| **[MSstatsShiny](https://bioconductor.org/packages/MSstatsShiny)** ([GitHub](https://github.com/Vitek-Lab/MSstatsShiny) · [web app](https://www.msstatsshiny.com)) | Point-and-click R-Shiny GUI integrating MSstats family of packages. | +| **[MSstatsBioNet](https://bioconductor.org/packages/MSstatsBioNet)** ([GitHub](https://github.com/Vitek-Lab/MSstatsBioNet)) | Network analysis and enrichment of MSstats differential abundance results using prior-knowledge networks (e.g., INDRA). | +| **[MSstatsResponse](https://bioconductor.org/packages/MSstatsResponse)** ([GitHub](https://github.com/Vitek-Lab/MSstatsResponse)) | Semi-parametric dose-response modeling for chemoproteomics experiments (drug-protein interaction / IC50 estimation). | +| **[MSstatsConvert](https://bioconductor.org/packages/MSstatsConvert)** ([GitHub](https://github.com/Vitek-Lab/MSstatsConvert)) | Shared converters that translate output from Skyline, MaxQuant, Proteome Discoverer, Spectronaut, DIA-NN, FragPipe, OpenSWATH, and more into MSstats format. | + +MSstatsResponse's semi-parametric curve-fitting approach to dose-response data +generalizes beyond drug-protein interaction/IC50 estimation: the same concept +applies to related experiment types such as Thermal Proteome Profiling (TPP) +and protein turnover kinetics, where abundance is likewise modeled as a smooth +function of a continuous variable (temperature or time) rather than a fixed +curve shape. + +## Developers + +MSstats is developed and maintained out of the [Vitek Lab](https://olga-vitek-lab.khoury.northeastern.edu/) +at Northeastern University. + +Current developers: + +- Devon Kohler +- Anthony Wu +- Mateusz Staniak +- Sarah Szvetecz + +Former developers include Meena Choi, Deril Raju, Tsung-Heng Tsai, and Ting Huang. See the full author list in [DESCRIPTION](DESCRIPTION) and the +lab's [publications page](https://olga-vitek-lab.khoury.northeastern.edu/publications/) +for the wider set of contributors across the MSstats ecosystem. + +The lab also organizes the annual [May Institute](https://computationalproteomics.khoury.northeastern.edu/), +a computational proteomics training program at Northeastern University +covering mass spectrometry, statistics, and bioinformatics, which regularly +features MSstats developers as instructors. + +## Installation + +```r +if (!requireNamespace("BiocManager", quietly = TRUE)) + install.packages("BiocManager") + +BiocManager::install("MSstats") +``` + +The development version can be installed directly from this repository: + +```r +BiocManager::install("Vitek-Lab/MSstats", ref = "devel") +``` + +## Quick Start + +```r +library(MSstats) + +# Use one of the datasets bundled with the package (label-based SRM example) +data("SRMRawData") + +# Pre-process: log-transform, normalize, and summarize to protein level +QuantData <- dataProcess(SRMRawData, use_log_file = FALSE) + +# Test all pairwise comparisons between conditions +testResults <- groupComparison(contrast.matrix = "pairwise", + data = QuantData, + use_log_file = FALSE) + +head(testResults$ComparisonResult) +``` + +Real experiments typically start by converting a search engine/tool's output +(e.g., Skyline, MaxQuant, Spectronaut, DIA-NN, FragPipe) into MSstats format +with a `*toMSstatsFormat` converter from `MSstatsConvert`, then proceeding with +`dataProcess()` and `groupComparison()` as above. See the vignettes below for +complete, tool-specific examples. + +## Supported Converters + +MSstats does not read raw search-tool output directly. Instead, a converter +translates each tool's report into MSstats format (one row per feature, run, +and condition) before `dataProcess()` is called. These converters are +implemented in [MSstatsConvert](https://bioconductor.org/packages/MSstatsConvert): + +| Search tool / format | Converter function | +| --- | --- | +| Skyline | `SkylinetoMSstatsFormat()` | +| MaxQuant | `MaxQtoMSstatsFormat()` | +| Progenesis | `ProgenesistoMSstatsFormat()` | +| Spectronaut | `SpectronauttoMSstatsFormat()` | +| Proteome Discoverer | `PDtoMSstatsFormat()` | +| DIA-NN | `DIANNtoMSstatsFormat()` | +| DIA-Umpire | `DIAUmpiretoMSstatsFormat()` | +| FragPipe | `FragPipetoMSstatsFormat()` | +| OpenMS | `OpenMStoMSstatsFormat()` | +| OpenSWATH | `OpenSWATHtoMSstatsFormat()` | +| Metamorpheus | `MetamorpheusToMSstatsFormat()` | + +MSstatsConvert also provides a set of `*toMSstatsTMTFormat()` converters +(MaxQuant, OpenMS, Proteome Discoverer, Philosopher/FragPipe, Protein +Prospector, SpectroMine) for isobaric-labeling experiments, used with +[MSstatsTMT](https://bioconductor.org/packages/MSstatsTMT) instead of MSstats. +See the [End to End Workflow vignette](vignettes/MSstatsWorkflow.Rmd) for the +required input files and options for each converter. + +## Documentation + +- [MSstats: Protein/Peptide significance analysis](vignettes/MSstats.Rmd) — overview of all functionality +- [MSstats: End to End Workflow](vignettes/MSstatsWorkflow.Rmd) — full worked example from raw data to results +- [MSstats+ vignette](vignettes/MSstatsPlus.Rmd) +- [Official website: msstats.org](http://msstats.org) +- [Bioconductor package page and reference manual](https://bioconductor.org/packages/MSstats) + +## Getting Help / Reporting Bugs + +- **Questions about usage, statistical methods, or troubleshooting:** please + post to the [MSstats Google Group](https://groups.google.com/forum/#!forum/msstats). + This is monitored by the development team and searchable, so it's the + fastest way to get help and to see if your question has already been + answered. +- **Bug reports and feature requests for this repository:** please open a + [GitHub issue](https://github.com/Vitek-Lab/MSstats/issues). + +## Usage & Impact + +MSstats has been part of Bioconductor since release 2.13 (2013) and is used +across the proteomics community, integrated as an external tool in Skyline and +underlying the MSstats family of packages and MSstatsShiny. + +### Citations + + + +_Citation counts from [OpenAlex](https://openalex.org), updated monthly. Google Scholar counts are typically higher, since Scholar indexes a broader range of sources (theses, gray literature, etc.); OpenAlex has a free, stable, official API. Last updated 2026-07-16 (UTC)._ + +| Paper | Citations | +| --- | --- | +| [Statistical design of MS proteomics experiments (2009)](https://doi.org/10.1021/pr8010099) | 265 | +| [MSstats (2014)](https://doi.org/10.1093/bioinformatics/btu305) | 1,161 | +| [MSstats feature selection (2020)](https://doi.org/10.1074/mcp.RA119.001792) | 37 | +| [MSstats 4.0 (2023)](https://doi.org/10.1021/acs.jproteome.2c00834) | 135 | +| [MSstats & FragPipe DIA workflow (2024)](https://doi.org/10.1038/s41596-024-01000-3) | 13 | +| [MSstats+ / longitudinal peak quality (2025, preprint)](https://doi.org/10.1101/2025.09.11.675573) | 1 | +| [MSstatsTMT (2020)](https://doi.org/10.1074/mcp.RA120.002105) | 204 | +| [MSstatsTMT repeated measures (2023)](https://doi.org/10.1021/acs.jproteome.3c00155) | 14 | +| [MSstatsTMT for Thermal Proteome Profiling (2025)](https://doi.org/10.1016/j.mcpro.2025.100999) | 1 | +| [MSstatsPTM (2022)](https://doi.org/10.1016/j.mcpro.2022.100477) | 53 | +| [MSstatsLiP / LiP-MS protocol (2023)](https://doi.org/10.1038/s41596-022-00771-x) | 127 | +| [MSstatsShiny (2023)](https://doi.org/10.1021/acs.jproteome.2c00603) | 23 | +| [MSstatsResponse (2026, preprint)](https://doi.org/10.64898/2026.03.09.710598) | 1 | +| **Total** | **2,035** | + + + +This table is regenerated automatically alongside the download statistics +below by +[`.github/workflows/update-citation-stats.yaml`](.github/workflows/update-citation-stats.yaml), +using [OpenAlex](https://openalex.org) rather than Google Scholar — Scholar +has no official API and blocks automated requests, which makes it unreliable +for an unattended scheduled job. If you'd like exact Google Scholar counts, +see the [MSstats citations search](https://scholar.google.com/scholar?q=MSstats+Vitek+proteomics) +directly. + +### Download statistics + + + +_Average monthly downloads over the last 6 complete months, computed directly from Bioconductor's download logs. Last updated 2026-07-15 (UTC)._ + +| Package | Avg. monthly downloads | +| --- | --- | +| [MSstats](https://bioconductor.org/packages/stats/bioc/MSstats/) | 1,566 | +| [MSstatsTMT](https://bioconductor.org/packages/stats/bioc/MSstatsTMT/) | 796 | +| [MSstatsPTM](https://bioconductor.org/packages/stats/bioc/MSstatsPTM/) | 632 | +| [MSstatsLiP](https://bioconductor.org/packages/stats/bioc/MSstatsLiP/) | 442 | +| [MSstatsBig](https://bioconductor.org/packages/stats/bioc/MSstatsBig/) | 384 | +| [MSstatsShiny](https://bioconductor.org/packages/stats/bioc/MSstatsShiny/) | 437 | +| [MSstatsBioNet](https://bioconductor.org/packages/stats/bioc/MSstatsBioNet/) | 311 | +| [MSstatsResponse](https://bioconductor.org/packages/stats/bioc/MSstatsResponse/) | 298 | +| [MSstatsConvert](https://bioconductor.org/packages/stats/bioc/MSstatsConvert/) | 1,100 | + + + +This table is regenerated automatically once a month by +[`.github/workflows/update-download-stats.yaml`](.github/workflows/update-download-stats.yaml), +which pulls the latest numbers from +[Bioconductor's download logs](https://bioconductor.org/packages/stats/bioc/MSstats/) +for every package in the ecosystem. + +## References + +If you use MSstats or a package from the MSstats ecosystem, please cite the +relevant publication(s): + +1. Oberg AL, Vitek O. **Statistical design of quantitative mass spectrometry-based + proteomic experiments.** *J Proteome Res*. 2009;8(5):2144-2156. + [DOI: 10.1021/pr8010099](https://doi.org/10.1021/pr8010099) +2. Choi M, Chang CY, Clough T, Broudy D, Killeen T, MacLean B, Vitek O. + **MSstats: an R package for statistical analysis of quantitative mass + spectrometry-based proteomic experiments.** *Bioinformatics*. 2014;30(17):2524-2526. + [DOI: 10.1093/bioinformatics/btu305](https://doi.org/10.1093/bioinformatics/btu305) +3. Tsai TH, Choi M, Banfai B, Liu Y, MacLean BX, Dunkley T, Vitek O. + **Selection of Features with Consistent Profiles Improves Relative Protein + Quantification in Mass Spectrometry Experiments.** *Mol Cell Proteomics*. + 2020;19(6):944-959. + [DOI: 10.1074/mcp.RA119.001792](https://doi.org/10.1074/mcp.RA119.001792) +4. Kohler D, Staniak M, Tsai TH, Huang T, Shulman N, Bernhardt OM, MacLean BX, + Nesvizhskii AI, Reiter L, Sabido E, Choi M, Vitek O. **MSstats Version 4.0: + Statistical Analyses of Quantitative Mass Spectrometry-Based Proteomic + Experiments with Chromatography-Based Quantification at Scale.** + *J Proteome Res*. 2023;22(5):1466-1482. + [DOI: 10.1021/acs.jproteome.2c00834](https://doi.org/10.1021/acs.jproteome.2c00834) +5. Kohler D, Vitek O, et al. **An MSstats workflow for detecting differentially + abundant proteins in large-scale data-independent acquisition mass + spectrometry experiments with FragPipe processing.** *Nat Protoc*. + 2024;19:2915-2938. + [DOI: 10.1038/s41596-024-01000-3](https://doi.org/10.1038/s41596-024-01000-3) +6. Kohler D, Dogu E, Bhattacharya M, Karayel O, Magana M, Wu A, Anania VG, + Vitek O. **Accounting for longitudinal peak quality metrics with MSstats+ + enhances differential analysis in proteomic experiments with + data-independent acquisition** (introduces MSstats+). *bioRxiv*. 2025. + [DOI: 10.1101/2025.09.11.675573](https://doi.org/10.1101/2025.09.11.675573) +7. Huang T, Choi M, Tzouros M, Golling S, Pandya NJ, Banfai B, Dunkley T, + Vitek O. **MSstatsTMT: Statistical Detection of Differentially Abundant + Proteins in Experiments with Isobaric Labeling and Multiple Mixtures.** + *Mol Cell Proteomics*. 2020;19(10):1706-1723. + [DOI: 10.1074/mcp.RA120.002105](https://doi.org/10.1074/mcp.RA120.002105) +8. Huang T, Staniak M, da Veiga Leprevost F, Figueroa-Navedo AM, Ivanov AR, + Nesvizhskii AI, Choi M, Vitek O. **Statistical Detection of Differentially + Abundant Proteins in Experiments with Repeated Measures Designs and + Isobaric Labeling.** *J Proteome Res*. 2023;22(8):2641-2659. + [DOI: 10.1021/acs.jproteome.3c00155](https://doi.org/10.1021/acs.jproteome.3c00155) +9. Figueroa-Navedo AM, Kapre R, Gupta T, Xu Y, Phaneuf CG, Jean Beltran PM, + Xue L, Ivanov AR, Vitek O. **MSstatsTMT Improves Accuracy of Thermal + Proteome Profiling.** *Mol Cell Proteomics*. 2025;24(8):100999. + [DOI: 10.1016/j.mcpro.2025.100999](https://doi.org/10.1016/j.mcpro.2025.100999) +10. Kohler D, Tsai TH, Verschueren E, Huang T, Hinkle T, Phu L, Choi M, Vitek O. + **MSstatsPTM: Statistical Relative Quantification of Posttranslational + Modifications in Bottom-Up Mass Spectrometry-Based Proteomics.** + *Mol Cell Proteomics*. 2022;22(1):100477. + [DOI: 10.1016/j.mcpro.2022.100477](https://doi.org/10.1016/j.mcpro.2022.100477) +11. Malinovska L, Cappelletti V, Kohler D, Piazza I, Tsai TH, Pepelnjak M, + Stalder P, Dörig C, Sesterhenn F, Elsässer F, Kralickova L, Beaton N, + Reiter L, de Souza N, Vitek O, Picotti P. **Proteome-wide structural + changes measured with limited proteolysis-mass spectrometry: an advanced + protocol for high-throughput applications** (introduces MSstatsLiP). + *Nat Protoc*. 2023;18(3):659-682. + [DOI: 10.1038/s41596-022-00771-x](https://doi.org/10.1038/s41596-022-00771-x) +12. Kohler D, Kaza M, Pasi C, Huang T, Staniak M, Mohandas D, Sabido E, Choi M, + Vitek O. **MSstatsShiny: A GUI for Versatile, Scalable, and Reproducible + Statistical Analyses of Quantitative Proteomic Experiments.** + *J Proteome Res*. 2023;22(2):551-556. + [DOI: 10.1021/acs.jproteome.2c00603](https://doi.org/10.1021/acs.jproteome.2c00603) +13. Szvetecz S, Kohler D, Vitek O. **MSstatsResponse: Semi-parametric + statistical model enhances detection of drug-protein interactions in + chemoproteomics experiments.** *bioRxiv*. 2026. + [DOI: 10.64898/2026.03.09.710598](https://doi.org/10.64898/2026.03.09.710598) + +MSstatsBioNet does not yet have a dedicated publication; see the +[Bioconductor package page](https://bioconductor.org/packages/MSstatsBioNet) +for details and how to cite the software directly. + +## Funding + +MSstats development has been supported by the Chan Zuckerberg Initiative's +[Essential Open Source Software for Science](https://chanzuckerberg.com/eoss/proposals/). + +## License + +MSstats is released under the [Artistic-2.0](https://opensource.org/licenses/Artistic-2.0) license. diff --git a/appspec.yml b/appspec.yml deleted file mode 100644 index a671e502..00000000 --- a/appspec.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 0.0 -os: linux -files: - - source: ./ - destination: /home/rstudio/code/deployment/msstats-dev -# Executes After install lifecycle during deployment -hooks: - AfterInstall: - - location: inst/scripts/after_install.sh - runas: ubuntu \ No newline at end of file diff --git a/man/figures/example_heatmap.png b/man/figures/example_heatmap.png new file mode 100644 index 00000000..8eef9cd1 Binary files /dev/null and b/man/figures/example_heatmap.png differ diff --git a/scripts/update_citation_counts.py b/scripts/update_citation_counts.py new file mode 100644 index 00000000..7b7f9e79 --- /dev/null +++ b/scripts/update_citation_counts.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Refresh the citation-count table in README.md using OpenAlex. + +OpenAlex (https://openalex.org) is used instead of Google Scholar because it +has a free, stable, official API. Google Scholar has no API and actively +blocks automated/datacenter-IP requests, which makes it unsuitable for an +unattended, scheduled job like this one. Counts will run a little lower than +Google Scholar's, since Scholar indexes a broader (and less curated) set of +sources. + +Uses only the standard library so it can run on a bare GitHub Actions runner +with no extra setup. +""" +import datetime +import json +import pathlib +import urllib.request + +README_PATH = pathlib.Path(__file__).resolve().parent.parent / "README.md" +START_MARKER = "" +END_MARKER = "" + +# (short label, doi) for every paper in the References section, in the same +# order they appear there. +PAPERS = [ + ("Statistical design of MS proteomics experiments (2009)", "10.1021/pr8010099"), + ("MSstats (2014)", "10.1093/bioinformatics/btu305"), + ("MSstats feature selection (2020)", "10.1074/mcp.RA119.001792"), + ("MSstats 4.0 (2023)", "10.1021/acs.jproteome.2c00834"), + ("MSstats + FragPipe DIA workflow (2024)", "10.1038/s41596-024-01000-3"), + ("MSstats+ / longitudinal peak quality (2025, preprint)", "10.1101/2025.09.11.675573"), + ("MSstatsTMT (2020)", "10.1074/mcp.RA120.002105"), + ("MSstatsTMT repeated measures (2023)", "10.1021/acs.jproteome.3c00155"), + ("MSstatsTMT for Thermal Proteome Profiling (2025)", "10.1016/j.mcpro.2025.100999"), + ("MSstatsPTM (2022)", "10.1016/j.mcpro.2022.100477"), + ("MSstatsLiP / LiP-MS protocol (2023)", "10.1038/s41596-022-00771-x"), + ("MSstatsShiny (2023)", "10.1021/acs.jproteome.2c00603"), + ("MSstatsResponse (2026, preprint)", "10.64898/2026.03.09.710598"), +] + +OPENALEX_URL = "https://api.openalex.org/works/https://doi.org/{doi}" + + +def fetch_cited_by_count(doi: str): + req = urllib.request.Request( + OPENALEX_URL.format(doi=doi), + headers={"User-Agent": "msstats-readme-stats-bot (mailto:none)"}, + ) + try: + with urllib.request.urlopen(req, timeout=30) as response: + data = json.load(response) + except Exception: + return None + return data.get("cited_by_count") + + +def format_table(today: datetime.date) -> str: + lines = [ + START_MARKER, + "", + "_Citation counts from [OpenAlex](https://openalex.org), updated " + "monthly. Google Scholar counts are typically higher, since Scholar " + "indexes a broader range of sources (theses, gray literature, etc.); " + f"OpenAlex has a free, stable, official API. Last updated {today.isoformat()} (UTC)._", + "", + "| Paper | Citations |", + "| --- | --- |", + ] + total = 0 + any_missing = False + for label, doi in PAPERS: + count = fetch_cited_by_count(doi) + if count is None: + any_missing = True + cell = "n/a" + else: + total += count + cell = f"{count:,}" + lines.append(f"| [{label}](https://doi.org/{doi}) | {cell} |") + + total_cell = f"{total:,}" + ("+" if any_missing else "") + lines.append(f"| **Total** | **{total_cell}** |") + lines.append("") + lines.append(END_MARKER) + return "\n".join(lines) + + +def main() -> None: + today = datetime.datetime.now(datetime.timezone.utc).date() + new_block = format_table(today) + + text = README_PATH.read_text() + if START_MARKER not in text or END_MARKER not in text: + raise SystemExit( + f"Could not find {START_MARKER} / {END_MARKER} markers in README.md" + ) + before, rest = text.split(START_MARKER, 1) + _old_block, after = rest.split(END_MARKER, 1) + updated_text = before + new_block + after + README_PATH.write_text(updated_text) + + +if __name__ == "__main__": + main() diff --git a/scripts/update_download_stats.py b/scripts/update_download_stats.py new file mode 100644 index 00000000..5f97a4f9 --- /dev/null +++ b/scripts/update_download_stats.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Refresh the download-statistics table in README.md from Bioconductor's +per-package download stats, and write it between the +DOWNLOAD-STATS:START/END markers. + +Uses only the standard library so it can run on a bare GitHub Actions +runner with no extra setup. +""" +import datetime +import pathlib +import urllib.request + +README_PATH = pathlib.Path(__file__).resolve().parent.parent / "README.md" +START_MARKER = "" +END_MARKER = "" +MONTHS_TO_AVERAGE = 6 + +# (package name, display label) in the order they should appear in the table. +PACKAGES = [ + ("MSstats", "MSstats"), + ("MSstatsTMT", "MSstatsTMT"), + ("MSstatsPTM", "MSstatsPTM"), + ("MSstatsLiP", "MSstatsLiP"), + ("MSstatsBig", "MSstatsBig"), + ("MSstatsShiny", "MSstatsShiny"), + ("MSstatsBioNet", "MSstatsBioNet"), + ("MSstatsResponse", "MSstatsResponse"), + ("MSstatsConvert", "MSstatsConvert"), +] + +MONTH_NUMBERS = { + "Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6, + "Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12, +} + +STATS_URL = "https://bioconductor.org/packages/stats/bioc/{pkg}/{pkg}_stats.tab" + + +def fetch_stats(pkg: str) -> str: + req = urllib.request.Request( + STATS_URL.format(pkg=pkg), + headers={"User-Agent": "msstats-readme-stats-bot/1.0"}, + ) + with urllib.request.urlopen(req, timeout=30) as response: + return response.read().decode("utf-8") + + +def completed_months(raw_tab: str, today: datetime.date): + """Yield (year, month_num, downloads) for months strictly before the + current, still-in-progress month, most recent first.""" + rows = [] + for line in raw_tab.splitlines()[1:]: + parts = line.split("\t") + if len(parts) != 4: + continue + year_str, month_str, _ips, downloads_str = parts + if month_str == "all": + continue + year = int(year_str) + month = MONTH_NUMBERS[month_str] + if (year, month) >= (today.year, today.month): + continue + rows.append((year, month, int(downloads_str))) + rows.sort(reverse=True) + return rows + + +def average_recent_downloads(pkg: str, today: datetime.date): + raw_tab = fetch_stats(pkg) + rows = completed_months(raw_tab, today)[:MONTHS_TO_AVERAGE] + if not rows: + return None, 0 + avg = round(sum(downloads for _, _, downloads in rows) / len(rows)) + return avg, len(rows) + + +def format_table(today: datetime.date) -> str: + lines = [ + START_MARKER, + "", + f"_Average monthly downloads over the last {MONTHS_TO_AVERAGE} complete " + f"months, computed directly from Bioconductor's download logs. Last " + f"updated {today.isoformat()} (UTC)._", + "", + "| Package | Avg. monthly downloads |", + "| --- | --- |", + ] + for pkg, label in PACKAGES: + avg, n_months = average_recent_downloads(pkg, today) + if avg is None: + cell = "n/a (no completed months yet)" + elif n_months < MONTHS_TO_AVERAGE: + cell = f"{avg:,} (avg. over {n_months} available month{'s' if n_months != 1 else ''})" + else: + cell = f"{avg:,}" + lines.append( + f"| [{label}](https://bioconductor.org/packages/stats/bioc/{pkg}/) | {cell} |" + ) + lines.append("") + lines.append(END_MARKER) + return "\n".join(lines) + + +def main() -> None: + today = datetime.datetime.now(datetime.timezone.utc).date() + new_block = format_table(today) + + text = README_PATH.read_text() + if START_MARKER not in text or END_MARKER not in text: + raise SystemExit( + f"Could not find {START_MARKER} / {END_MARKER} markers in README.md" + ) + before, rest = text.split(START_MARKER, 1) + _old_block, after = rest.split(END_MARKER, 1) + updated_text = before + new_block + after + README_PATH.write_text(updated_text) + + +if __name__ == "__main__": + main()