├── .coverage
├── .github
├── pull_request_template.md
└── workflows
│ └── CI.yml
├── .pre-commit-config.yaml
├── Dockerfile
├── LICENSE
├── README.md
├── codecov.yml
├── diffexpr
├── __init__.py
├── py_deseq.py
└── py_pathway.py
├── environment.yml
├── example
├── deseq_example.ipynb
└── jupyter.png
├── poetry.lock
├── pyproject.toml
├── requirements.txt
├── setup.R
├── setup.py
└── test
├── data
├── ercc.tsv
├── quant1
│ └── abundance.h5
└── quant2
│ └── abundance.h5
├── deseq.R
├── test_deseq.py
└── test_pathways.py
/.coverage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wckdouglas/diffexpr/c78aba6a8ec7b26331f7e258d5e2cf1c5d559eed/.coverage
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | Description of what this PR does
2 |
3 | ## Checklist
4 | - [ ] CI pass?
5 |
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3 |
4 | name: "CI"
5 |
6 | env:
7 | REGISTRY: ghcr.io
8 | IMAGE_NAME: ${{ github.repository }}
9 |
10 | on:
11 | push:
12 | branches: [ "*" ]
13 | pull_request:
14 | types: [opened, ready_for_review]
15 |
16 | concurrency:
17 | group: ${{ github.ref }}
18 | cancel-in-progress: true
19 |
20 |
21 | jobs:
22 | build-and-ci:
23 | name: miniconda build
24 | runs-on: ubuntu-latest
25 | strategy:
26 | matrix:
27 | python-version: [3.6, 3.7, 3.8]
28 | env:
29 | SETUPTOOLS_USE_DISTUTILS: stdlib
30 | defaults:
31 | run:
32 | shell: bash -l {0}
33 |
34 | steps:
35 | - uses: actions/checkout@v2
36 | - uses: conda-incubator/setup-miniconda@v2
37 | with:
38 | activate-environment: test-environment
39 | python-version: ${{ matrix.python-version }}
40 | channels: bioconda,default,anaconda,r,conda-forge
41 | allow-softlinks: true
42 | channel-priority: 'flexible'
43 | show-channel-urls: true
44 | use-only-tar-bz2: true
45 | auto-update-conda: true
46 |
47 | - name: setup conda
48 | run: |
49 | conda config --set always_yes yes --set changeps1 no
50 | conda info -a
51 | conda list
52 | conda config --show-sources
53 | conda config --show
54 |
55 | - name: Install dependencies
56 | run: |
57 | conda install mamba
58 | mamba install pandas tzlocal rpy2 biopython ReportLab pytest-cov codecov bioconductor-deseq2 gfortran_linux-64 bioconductor-apeglm
59 |
60 | - name: hack for missing sysconfigdata (py3.7 and py3.8, doing nothing really)
61 | if: matrix.python-version != '3.6'
62 | run: |
63 | ls $CONDA_PREFIX/lib/python${{ matrix.python-version }}/_sysconfigdata_x86_64_conda*.py
64 |
65 | - name: hack for missing sysconfigdata (py3.6)
66 | if: matrix.python-version == '3.6'
67 | run: |
68 | ls $CONDA_PREFIX/lib/python${{ matrix.python-version }}/_sysconfigdata_x86_64*.py
69 | cp $CONDA_PREFIX/lib/python${{ matrix.python-version }}/_sysconfigdata_x86_64_conda_cos6_linux_gnu.py $CONDA_PREFIX/lib/python${{ matrix.python-version }}/_sysconfigdata_x86_64_conda_linux_gnu.py
70 |
71 | - name: install package
72 | run: |
73 | conda activate test-environment
74 | pip install .
75 |
76 | - name: install R packages
77 | run: |
78 | Rscript setup.R
79 |
80 | - name: Test with pytest
81 | run: |
82 | coverage run -m pytest -vvv
83 |
84 | - name: codecov
85 | run: |
86 | bash <(curl -s https://codecov.io/bash)
87 |
88 | build-and-push-image:
89 | runs-on: ubuntu-latest
90 | needs: build-and-ci
91 | permissions:
92 | contents: read
93 | packages: write
94 |
95 | steps:
96 | - name: Checkout repository
97 | uses: actions/checkout@v3
98 |
99 | - name: Log in to the Container registry
100 | uses: docker/login-action@v2
101 | with:
102 | registry: ${{ env.REGISTRY }}
103 | username: ${{ github.actor }}
104 | password: ${{ secrets.GITHUB_TOKEN }}
105 |
106 | - name: Set up Docker Buildx
107 | uses: docker/setup-buildx-action@v2
108 |
109 | - name: Build and push Docker image
110 | if: github.ref_name == 'master'
111 | uses: docker/build-push-action@v3
112 | with:
113 | context: .
114 | push: true
115 | target: diffexpr
116 | cache-from: type=gha
117 | cache-to: type=gha,mode=max
118 | tags: |
119 | ${{ env.REGISTRY }}/${{ github.repository }}/diffexpr:${{ github.ref_name }}
120 | ${{ env.REGISTRY }}/${{ github.repository }}/diffexpr:${{ github.sha }}
121 |
122 | - name: Build and push Dev Docker image
123 | uses: docker/build-push-action@v3
124 | with:
125 | context: .
126 | push: true
127 | target: diffexpr_dev
128 | cache-from: type=gha
129 | cache-to: type=gha,mode=max
130 | tags: |
131 | ${{ env.REGISTRY }}/${{ github.repository }}/diffexpr-dev:${{ github.ref_name }}
132 | ${{ env.REGISTRY }}/${{ github.repository }}/diffexpr-dev:${{ github.sha }}
133 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/psf/black
3 | rev: 22.10.0
4 | hooks:
5 | - id: black
6 | args: ["-l", "120"]
7 | language_version: python3
8 | - repo: https://github.com/PyCQA/isort
9 | rev: 5.10.1
10 | hooks:
11 | - id: isort
12 | args: ["--profile", "black"]
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.9.16-bullseye AS base
2 |
3 | # installation of R and associated packages (DESeq2)
4 | RUN apt-get update \
5 | && apt-get install -y r-base r-base-dev r-bioc-deseq2 r-bioc-rhdf5 libcurl4-openssl-dev libxml2-dev libssl-dev \
6 | && apt-get clean \
7 | && rm -rf /var/lib/apt/lists/*
8 |
9 | # installation of python dependencies
10 | RUN pip install pandas tzlocal \
11 | biopython ReportLab pytest rpy2
12 |
13 |
14 | # At this stage, we will build the diffexpr package
15 | # and any R packages that are not installed by apt-get
16 | # e.g. apeglm
17 | FROM base AS diffexpr
18 |
19 | COPY . /opt/diffexpr
20 | RUN python /opt/diffexpr/setup.py install
21 | RUN /usr/bin/Rscript -e "BiocManager::install(c('apeglm'))"
22 |
23 | # make sure python kernel knows about the diffexpr package
24 | # TODO: do we really need this?
25 | ENV PYTHONPATH "${PYTHONPATH}:/opt/diffexpr"
26 |
27 | # run a test to make sure things are installed correctly
28 | WORKDIR /opt/diffexpr
29 | RUN pytest -vvv
30 |
31 | # for the dev stage, we will install packages that help
32 | # differential expression analyses
33 | FROM diffexpr AS diffexpr_dev
34 |
35 | # jupyter lab and plotting
36 | RUN pip install jupyterlab matplotlib seaborn
37 |
38 | # R kernel for debugging
39 | RUN /usr/bin/Rscript -e "install.packages('IRkernel'); IRkernel::installspec(user = FALSE)"
40 |
41 | # again, run a test to make sure things at this stage are installed correctly
42 | WORKDIR /opt/diffexpr
43 | RUN pytest -vvv
44 |
45 | # docker run will spin up a jupyter lab instance at port 1234
46 | # docker run -p 1234:1234 should be allow access from outside the
47 | # container
48 | WORKDIR /
49 | CMD ["jupyter-lab", "--allow-root", "--ip", "0.0.0.0", "--port", "1234"]
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Douglas Wu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # diffexpr #
2 | [](https://github.com/wckdouglas/diffexpr/actions) [](https://codecov.io/gh/wckdouglas/diffexpr)
3 |
4 | A python package using `rpy2` to port [DESeq2](https://bioconductor.org/packages/release/bioc/html/DESeq2.html) into python.
5 |
6 | ## Installation ##
7 | Dependencies are `pandas` (python), `rpy2` (python), and `DESeq2` (R)
8 | Best way to setup the environment should be via [docker](#docker),
9 | but it should also be possible to install the dependency packages using
10 | [conda](https://docs.conda.io/en/latest/):
11 |
12 | ```
13 | conda config --add channels defaults
14 | conda config --add channels bioconda
15 | conda config --add channels conda-forge
16 | conda create -q -n diffexpr python=3.6 \
17 | pandas tzlocal rpy2 biopython ReportLab pytest-cov \
18 | bioconductor-deseq2 codecov
19 | conda activate diffexpr # activate diffexpr environment
20 | Rscript setup.R #to install DESeq2 correctly
21 | python setup.py install
22 | ```
23 |
24 | ## Docker ##
25 |
26 | We build two docker images in our [CI workflow](https://github.com/wckdouglas/diffexpr/blob/98166d9ee7c078520dfb55535634a5cdeaf477cf/.github/workflows/CI.yml#L106-L128):
27 | 1. diffexpr (`ghcr.io/wckdouglas/diffexpr/diffexpr`): contains minimal dependencies for executing code in this package
28 | 2. *diffexpr-dev* (`ghcr.io/wckdouglas/diffexpr/diffexpr-dev`): is the same as `diffexpr`, but with additional python packages (`matplotlib`, `seaborn`, and `jupyterlab`) for using this package in jupyter notebook analysis (see [below](#example) for how to spin up the jupyterlab instance from within the container), feel free to file an issue or put a PR to include your favorite packages!
29 |
30 | ## Example ##
31 | An example of running DESeq2 in *python* using `diffexpr` package is provided [here](https://github.com/wckdouglas/diffexp/blob/master/example/deseq_example.ipynb).
32 |
33 | This should be reproducible by:
34 |
35 | ```bash
36 | git clone https://github.com/wckdouglas/diffexpr.git
37 | cd diffexpr
38 | docker run \
39 | -p 1234:1234 \
40 | --mount type=bind,source="$(pwd)",target=/jupyter \
41 | ghcr.io/wckdouglas/diffexpr/diffexpr-dev:master
42 | ```
43 |
44 | and go to http://localhost:1234 to access the jupyter lab instance
45 |
46 | The `--mount type=bind,source="$(pwd)",target=/jupyter` option will mount the local filesystem (current directory) at `/jupyter`, such that the container has access to all the files under the current directory, the example notebook is under `/jupyter/example/deseq_example.ipynb`:
47 |
48 | 
49 |
50 |
51 | ## Citation ##
52 | :bangbang: Please cite the original [DESeq2 paper](https://genomebiology.biomedcentral.com/articles/10.1186/s13059-014-0550-8) if you used this package in your work:
53 |
54 | ```
55 | @article{Love2014,
56 | doi = {10.1186/s13059-014-0550-8},
57 | url = {https://doi.org/10.1186/s13059-014-0550-8},
58 | year = {2014},
59 | month = dec,
60 | publisher = {Springer Science and Business Media {LLC}},
61 | volume = {15},
62 | number = {12},
63 | author = {Michael I Love and Wolfgang Huber and Simon Anders},
64 | title = {Moderated estimation of fold change and dispersion for {RNA}-seq data with {DESeq}2},
65 | journal = {Genome Biology}
66 | }
67 | ```
68 |
69 | ## Alternatives ##
70 |
71 | [pyDESeq2](https://github.com/owkin/PyDESeq2) is a pure python implementation of DESeq2.
72 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | require_ci_to_pass: yes
3 |
4 | coverage:
5 | precision: 2
6 | round: down
7 | range: "70...100"
8 |
9 | parsers:
10 | gcov:
11 | branch_detection:
12 | conditional: yes
13 | loop: yes
14 | method: no
15 | macro: no
16 |
17 | comment:
18 | layout: "reach,diff,flags,tree"
19 | behavior: default
20 | require_changes: no
21 |
--------------------------------------------------------------------------------
/diffexpr/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wckdouglas/diffexpr/c78aba6a8ec7b26331f7e258d5e2cf1c5d559eed/diffexpr/__init__.py
--------------------------------------------------------------------------------
/diffexpr/py_deseq.py:
--------------------------------------------------------------------------------
1 | """
2 | Running DESeq2 from python via rpy2
3 |
4 | Adopted from: https://stackoverflow.com/questions/41821100/running-deseq2-through-rpy2
5 |
6 | If there are any functions that is missing for your need, feel free to file an
7 | [issue](https://github.com/wckdouglas/diffexpr/issues) or even better, make a
8 | [PR](https://github.com/wckdouglas/diffexpr/pulls)!
9 |
10 | A jupyter notebook with examples can be found at:
11 | https://github.com/wckdouglas/diffexpr/blob/master/example/deseq_example.ipynb
12 | """
13 |
14 | import logging
15 | from typing import Dict
16 |
17 | import numpy as np
18 | import pandas as pd
19 | import rpy2.robjects as robjects
20 | from rpy2.robjects import Formula, pandas2ri
21 | from rpy2.robjects.conversion import localconverter
22 | from rpy2.robjects.packages import importr
23 |
24 | # setup logger
25 | logging.basicConfig(level=logging.INFO)
26 | logger = logging.getLogger("DESeq2")
27 |
28 | # R packages as python objects
29 | r_utils = importr("utils")
30 | deseq = importr("DESeq2")
31 | tximport = importr("tximport")
32 | multicore = importr("BiocParallel")
33 | summarized_experiment = importr("SummarizedExperiment")
34 |
35 | # get version of deseq2
36 | _DESEQ2_VERSION_INT = r_utils.packageVersion("DESeq2")
37 | DESEQ2_VERSION = ".".join(map(str, robjects.conversion.rpy2py(_DESEQ2_VERSION_INT)[0]))
38 |
39 | # a R function to make matrix into dataframe
40 | to_dataframe = robjects.r("function(x) data.frame(x)")
41 |
42 |
43 | class py_DESeq2:
44 | """
45 | DESeq2 object through rpy2
46 |
47 | Args:
48 | count_matrix (Union[pd.DataFrame, Dict[str,str]): should be a pandas dataframe with each column as count, and a id column for gene id,
49 | unless kallisto=True, then this is expected to be a dictionary of key: sample name, value: abundance.h5 file
50 | design_matrix (pd.DataFrame): an design matrix in the form of pandas dataframe, see DESeq2 manual, samplenames as rownames
51 | design_formula (str): see DESeq2 manual, example: "~ treatment""
52 | gene_column (str): column name of gene id columns (default: "id")
53 | threads (int): how many threads to used in running deseq, if threads > 1 is provided,
54 | `parallel=True` will be used in `DESeq2::DESeq`, `DESeq2::results`, and `DESeq2::lfcShrink`
55 | (default: 1)
56 |
57 |
58 | count_matrix example::
59 |
60 | id sampleA sampleB
61 | geneA 5 1
62 | geneB 4 5
63 | geneC 1 2
64 |
65 | Design matrix example::
66 |
67 | treatment
68 | sampleA1 A
69 | sampleA2 A
70 | sampleB1 B
71 | sampleB2 B
72 |
73 | """
74 |
75 | def __init__(
76 | self, count_matrix, design_matrix, design_formula, gene_column="id", threads=1, kallisto=False, tx2gene=None
77 | ):
78 | if not isinstance(threads, int):
79 | raise ValueError("threads must be an integer")
80 | multicore.register(multicore.MulticoreParam(threads))
81 |
82 | # set up the deseq2 object
83 | self.dds = None
84 | self.result = None
85 | self.deseq_result = None
86 | self.resLFC = None
87 | self.comparison = None
88 | self.normalized_count_df = None
89 | self.parallel = threads > 1
90 | self.gene_id = None
91 | self.gene_column = None
92 |
93 | if kallisto:
94 | if tx2gene is None:
95 | raise ValueError("tx2gene must be specified")
96 | self.from_kallisto(count_matrix, design_matrix, design_formula, tx2gene)
97 | else:
98 | self.init_matrix(count_matrix, design_matrix, design_formula, gene_column)
99 |
100 | def init_matrix(self, count_matrix, design_matrix, design_formula, gene_column):
101 | """
102 | Initialize deseq from count matrix
103 |
104 | Args:
105 | count_matrix (pd.DataFrame):
106 | design_matrix (pd.DataFrame):
107 | design_formula (str):
108 | gene_column (str):
109 | """
110 | # input validation
111 | for df in [count_matrix, design_matrix]:
112 | if not isinstance(df, pd.DataFrame):
113 | raise ValueError("count_matrix and design_matrix should be pd.DataFrame type")
114 |
115 | if gene_column not in count_matrix.columns:
116 | raise ValueError("The given gene_column name is not a column in count_matrix dataframe")
117 |
118 | self.gene_column = gene_column
119 | self.gene_id = count_matrix[self.gene_column]
120 | self.samplenames = count_matrix.columns[count_matrix.columns != self.gene_column]
121 | with localconverter(robjects.default_converter + pandas2ri.converter):
122 | self.count_matrix = robjects.conversion.py2rpy(count_matrix.set_index(self.gene_column))
123 | self.design_matrix = robjects.conversion.py2rpy(design_matrix)
124 | self.design_formula = Formula(design_formula)
125 | self.dds = deseq.DESeqDataSetFromMatrix(
126 | countData=self.count_matrix, colData=self.design_matrix, design=self.design_formula
127 | )
128 |
129 | def from_kallisto(
130 | self, h5_file_list: Dict[str, str], design_matrix: pd.DataFrame, design_formula: str, tx2gene: pd.DataFrame
131 | ):
132 | """
133 | Initialize deseq from Tximport kallisto files
134 |
135 | :param h5_file_list: dictionary of key: sample name, value: abundance.h5 file
136 | :param design_matrix: an design matrix in the form of pandas dataframe, see DESeq2 manual, samplenames as rownames
137 | :param str design_formula: see DESeq2 manual, example: "~ treatment""
138 | """
139 | files = robjects.StrVector(list(h5_file_list.values()))
140 | files.names = list(h5_file_list.keys())
141 | self.design_formula = Formula(design_formula)
142 | with localconverter(robjects.default_converter + pandas2ri.converter):
143 | self.design_matrix = robjects.conversion.py2rpy(design_matrix)
144 | tx2gene = robjects.conversion.py2rpy(tx2gene)
145 | self.txi = tximport.tximport(
146 | files, type="kallisto", txOut=False, tx2gene=tx2gene, countsFromAbundance="scaledTPM"
147 | )
148 | logger.info(f"Read kallisto files: {files}")
149 | self.dds = deseq.DESeqDataSetFromTximport(self.txi, colData=self.design_matrix, design=self.design_formula)
150 |
151 | def run_deseq(self, **kwargs):
152 | """
153 | actually running deseq2 and setup the dds and comparison
154 | fields in the object
155 |
156 | From DESeq2 manual:
157 |
158 | DESeq(
159 | object,
160 | test = c("Wald", "LRT"),
161 | fitType = c("parametric", "local", "mean", "glmGamPoi"),
162 | sfType = c("ratio", "poscounts", "iterate"),
163 | betaPrior,
164 | full = design(object),
165 | reduced,
166 | quiet = FALSE,
167 | minReplicatesForReplace = 7,
168 | modelMatrixType,
169 | useT = FALSE,
170 | minmu = if (fitType == "glmGamPoi") 1e-06 else 0.5,
171 | parallel = FALSE,
172 | BPPARAM = bpparam()
173 | )
174 |
175 | Args:
176 | **kwargs: Any keyword arguments for DESeq
177 | Returns:
178 | NoneType
179 |
180 | """
181 |
182 | for key, value in kwargs.items():
183 | if key == "reduced":
184 | kwargs[key] = Formula(value)
185 | if key == "parallel":
186 | raise ValueError("parallel is inferred from the provided thread count")
187 | self.dds = deseq.DESeq(self.dds, parallel=self.parallel, **kwargs)
188 | self.comparison = list(deseq.resultsNames(self.dds))
189 |
190 | def get_deseq_result(self, contrast=None, **kwargs):
191 | """
192 | DESeq2: result(dds, contrast)
193 | making a dds.deseq_result pandas dataframe
194 |
195 | Args:
196 | contrast (list[str]): list of string annotating the contrast to compute
197 | (see DESeq2 manual http://bioconductor.org/packages/devel/bioc/vignettes/DESeq2/inst/doc/DESeq2.html#contrasts)
198 | **kwargs (Dict[str,Any]): any other parameters to pass into DESeq2::results function
199 | Returns:
200 | NoneType
201 | """
202 |
203 | if contrast:
204 | if len(contrast) == 3:
205 | r_contrast = robjects.vectors.StrVector(np.array(contrast))
206 | else:
207 | if len(contrast) != 2:
208 | raise ValueError("Contrast must be length of 3 or 2")
209 | r_contrast = robjects.ListVector({None: con for con in contrast})
210 | logger.info("Using contrast: %s" % contrast)
211 | self.result = deseq.results(self.dds, contrast=r_contrast, parallel=self.parallel, **kwargs) # Robject
212 | else:
213 | self.result = deseq.results(self.dds, **kwargs) # R object
214 | self.deseq_result = to_dataframe(self.result) # R dataframe
215 | with localconverter(robjects.default_converter + pandas2ri.converter):
216 | self.deseq_result = robjects.conversion.rpy2py(self.deseq_result) ## back to pandas dataframe
217 |
218 | if self.gene_column is not None:
219 | self.deseq_result[self.gene_column] = self.gene_id.values
220 |
221 | def normalized_count(self):
222 | """
223 | Returns a normalized count data frame
224 |
225 | Returns:
226 | pd.DataFrame: a dataframe in the format of DESeq2::Counts(dds, normalized=TRUE) in R
227 | """
228 | normalized_count_matrix = deseq.counts_DESeqDataSet(self.dds, normalized=True)
229 | normalized_count_matrix = to_dataframe(normalized_count_matrix)
230 | # switch back to python
231 | with localconverter(robjects.default_converter + pandas2ri.converter):
232 | self.normalized_count_df = robjects.conversion.rpy2py(normalized_count_matrix)
233 |
234 | if self.gene_column is not None:
235 | self.normalized_count_df[self.gene_column] = self.gene_id.values
236 | logger.info("Normalizing counts")
237 | return self.normalized_count_df
238 |
239 | def lfcShrink(self, coef, method="apeglm", **kwargs):
240 | """
241 | Perform LFC shrinkage on the DDS object
242 | see: http://bioconductor.org/packages/devel/bioc/vignettes/DESeq2/inst/doc/DESeq2.html
243 |
244 | Be sure to check dds.comparison to see which coef (1-base because it's passing into the R code)
245 | to use
246 |
247 | Args:
248 | coef (int): 1-based index for selecting which of dds.comparison to show
249 | method (str): DESeq2 lfcshrink method ("apeglm", "ashr", "normal")
250 |
251 | Returns:
252 | pandas.DataFrame: a deseq2 result table
253 | """
254 | lfc = deseq.lfcShrink(self.dds, res=self.result, coef=coef, type=method, parallel=self.parallel, **kwargs)
255 | with localconverter(robjects.default_converter + pandas2ri.converter):
256 | lfc = robjects.conversion.rpy2py(to_dataframe(lfc))
257 |
258 | return lfc.reset_index().rename(columns={"index": self.gene_column})
259 |
260 | def vst(self, blind=True, fit_type="parametric"):
261 | """
262 | deseq varianceStabilizingTransformation
263 | see: https://rdrr.io/bioc/DESeq2/man/varianceStabilizingTransformation.html
264 |
265 | essentially running R code:
266 |
267 | >>> vsd = DESeq2::varianceStabilizingTransformation(dds, blind=True, fitType="parametric")
268 | >>> SummarizedExperiment::assay(vsd)
269 |
270 | Example:
271 |
272 | >>> dds = py_DESeq2(
273 | count_matrix=df,
274 | design_matrix=sample_df,
275 | design_formula="~ batch + sample",
276 | gene_column="id",
277 | )
278 | >>> dds.vst(blind=True, fit_type="parametric")
279 |
280 |
281 | Args:
282 | blind (bool): whether to blind the transformation to the experimental design
283 | fit_type (str): should be either "parametric", "local", "mean"
284 | Returns:
285 | pandas.DataFrame: a vst transformed count table
286 | """
287 | if self.dds is None:
288 | raise ValueError("Empty DESeq object")
289 |
290 | acceptable_fit_types = set(["parametric", "local", "mean"])
291 | if fit_type not in acceptable_fit_types:
292 | raise ValueError(f"fit_type must be {acceptable_fit_types}")
293 |
294 | vst_matrix = summarized_experiment.assay(
295 | deseq.varianceStabilizingTransformation(self.dds, blind=blind, fitType=fit_type)
296 | )
297 | vst_df = to_dataframe(vst_matrix)
298 | with localconverter(robjects.default_converter + pandas2ri.converter):
299 | vst_counts = robjects.conversion.rpy2py(vst_df)
300 |
301 | logger.info("Processed variance stablizing transformation")
302 | return vst_counts.reset_index().rename(columns={"index": self.gene_column})
303 |
304 | def rlog(self, blind=True, fit_type="parametric"):
305 | """
306 | deseq rlog
307 | see: https://rdrr.io/bioc/DESeq2/man/rlog.html
308 |
309 | TODO: DESeq2 version of this function accepts two additional optional arguments
310 | 'intercept' and 'betaPriorVar' that have not been explicitly ported here.
311 |
312 | essentially running R code:
313 |
314 | >>> rld = DESeq2::rlog(dds, blind=True, fitType="parametric")
315 | >>> SummarizedExperiment::assay(rld)
316 |
317 | Example:
318 |
319 | >>> dds = py_DESeq2(
320 | count_matrix=df,
321 | design_matrix=sample_df,
322 | design_formula="~ batch + sample",
323 | gene_column="id",
324 | )
325 | >>> dds.rlog(blind=True, fit_type="parametric")
326 |
327 |
328 | Args:
329 | blind (bool): whether to blind the transformation to the experimental design
330 | fit_type (str): should be either "parametric", "local", "mean"
331 | Returns:
332 | pandas.DataFrame: a rlog transformed count table
333 | """
334 | if self.dds is None:
335 | raise ValueError("Empty DESeq object")
336 |
337 | acceptable_fit_types = set(["parametric", "local", "mean"])
338 | if fit_type not in acceptable_fit_types:
339 | raise ValueError(f"fit_type must be {acceptable_fit_types}")
340 |
341 | rlog_matrix = summarized_experiment.assay(deseq.rlog(self.dds, blind=blind, fitType=fit_type))
342 | rlog_df = to_dataframe(rlog_matrix)
343 | with localconverter(robjects.default_converter + pandas2ri.converter):
344 | rlog_counts = robjects.conversion.rpy2py(rlog_df)
345 |
346 | logger.info("Processed rlog transformation")
347 | return rlog_counts.reset_index().rename(columns={"index": self.gene_column})
348 |
349 | @property
350 | def deseq2_version(self):
351 | """
352 | Return DESeq2 version number
353 |
354 | :return: string
355 | """
356 | return DESEQ2_VERSION
357 |
--------------------------------------------------------------------------------
/diffexpr/py_pathway.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from Bio.Graphics.KGML_vis import KGMLCanvas
3 | from Bio.KEGG.KGML import KGML_parser
4 | from Bio.KEGG.REST import *
5 |
6 |
7 | def gene_is_enriched(enriched_genes, possible_names):
8 | """
9 | check if any gene name in the box matched the enriched genes
10 | return the first matched gene name
11 | """
12 | gene_names = possible_names.replace(" ", "").split(",")
13 | for gn in gene_names:
14 | if gn in enriched_genes:
15 | return gn
16 |
17 |
18 | class pathwayview:
19 | """
20 | Like pathwayview in R, plotting a pathway and labeling the changed genes
21 |
22 | Usage:
23 | args:
24 | enriched_genes: a list of enriched genes that are in the pathway
25 | pathway_id: KEGG pathway ID (e.g. hsa04110)
26 | figurename: Must be pdf file
27 |
28 | returns:
29 | pathway: a biopython KEGG KGML object
30 |
31 | Example:
32 |
33 | enriched_genes = 'GADD45A,PLK1,TTK,CDC6,CDC25C,CDC25A'.split(',')
34 | pv = pathwayview()
35 | pathway = pv.plot_pathway(enriched_genes = enriched_genes,
36 | pathway_id = 'hsa04110',
37 | figurename = 'pathway.pdf')
38 |
39 |
40 | # changing labeling colors:
41 | pv.enriched_color = '#00000' #must be hex codes
42 |
43 |
44 | """
45 |
46 | def __init__(self, fontsize=12):
47 | self.enriched_box_color = "#FFB600" # oragne
48 | self.non_enriched_box_color = "#FFFFFF" # white
49 | self.enriched_text_color = "#000000" # black
50 | self.non_enriched_text_color = "#000000"
51 | self.fontsize = fontsize
52 |
53 | def set_color(
54 | self,
55 | enriched_box_color="#FFB600",
56 | non_enriched_box_color="#FFFFFF",
57 | enriched_text_color="#000000",
58 | non_enriched_text_color="#000000",
59 | ):
60 | """
61 | Color control, inputs must be HEX color code
62 | """
63 | self.enriched_box_color = enriched_box_color # oragne
64 | self.non_enriched_box_color = non_enriched_box_color # white
65 | self.enriched_text_color = enriched_text_color # black
66 | self.non_enriched_text_color = non_enriched_text_color
67 |
68 | def plot_pathway(self, enriched_genes, pathway_id="hsa05322", figurename=None):
69 |
70 | # config figure name
71 | if not figurename:
72 | figurename = "%s.pdf" % pathway_id
73 | assert figurename.endswith(".pdf")
74 |
75 | # fetch pathway
76 | pathway = KGML_parser.read(kegg_get(pathway_id, "kgml"))
77 |
78 | # change color for pathway elements
79 | for entry in pathway.entries.values():
80 | possible_gene_names = entry.graphics[0].name
81 | matched_name = gene_is_enriched(enriched_genes, possible_gene_names)
82 | if matched_name:
83 | entry.graphics[0].bgcolor = self.enriched_box_color # set box color
84 | entry.graphics[0].fgcolor = self.enriched_text_color # set text color
85 | entry.graphics[0].name = matched_name
86 | else:
87 | entry.graphics[0].bgcolor = self.non_enriched_box_color
88 | entry.graphics[0].fgcolor = self.non_enriched_text_color
89 | entry.graphics[0].name = entry.graphics[0].name.split(",")[0]
90 |
91 | canvas = KGMLCanvas(pathway, import_imagemap=True, fontsize=self.fontsize)
92 | canvas.draw(figurename)
93 | print("Drawn: ", figurename)
94 | return pathway
95 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: base
2 | channels:
3 | - conda-forge
4 | - r
5 | - anaconda
6 | - default
7 | - bioconda
8 | - defaults
9 | dependencies:
10 | - _libgcc_mutex=0.1=conda_forge
11 | - _openmp_mutex=4.5=2_gnu
12 | - _r-mutex=1.0.1=anacondar_1
13 | - argcomplete=2.0.0=pyhd8ed1ab_0
14 | - attrs=22.1.0=pyh71513ae_1
15 | - binutils_impl_linux-64=2.36.1=h193b22a_2
16 | - binutils_linux-64=2.36=hf3e587d_10
17 | - bioconductor-annotate=1.76.0=r42hdfd78af_0
18 | - bioconductor-annotationdbi=1.60.0=r42hdfd78af_0
19 | - bioconductor-biobase=2.58.0=r42hc0cfd56_0
20 | - bioconductor-biocgenerics=0.44.0=r42hdfd78af_0
21 | - bioconductor-biocparallel=1.32.0=r42hc247a5b_0
22 | - bioconductor-biostrings=2.66.0=r42hc0cfd56_0
23 | - bioconductor-data-packages=20221112=hdfd78af_0
24 | - bioconductor-delayedarray=0.24.0=r42hc0cfd56_0
25 | - bioconductor-deseq2=1.38.0=r42hc247a5b_0
26 | - bioconductor-genefilter=1.80.0=r42h38f54d8_0
27 | - bioconductor-geneplotter=1.76.0=r42hdfd78af_0
28 | - bioconductor-genomeinfodb=1.34.1=r42hdfd78af_0
29 | - bioconductor-genomeinfodbdata=1.2.9=r42hdfd78af_0
30 | - bioconductor-genomicranges=1.50.0=r42hc0cfd56_0
31 | - bioconductor-iranges=2.32.0=r42hc0cfd56_0
32 | - bioconductor-keggrest=1.38.0=r42hdfd78af_0
33 | - bioconductor-matrixgenerics=1.10.0=r42hdfd78af_0
34 | - bioconductor-s4vectors=0.36.0=r42hc0cfd56_0
35 | - bioconductor-summarizedexperiment=1.28.0=r42hdfd78af_0
36 | - bioconductor-xvector=0.38.0=r42hc0cfd56_0
37 | - bioconductor-zlibbioc=1.44.0=r42hc0cfd56_0
38 | - biopython=1.80=py39hb9d737c_0
39 | - blas=1.1=openblas
40 | - brotlipy=0.7.0=py39h27cfd23_1003
41 | - bwidget=1.9.14=ha770c72_1
42 | - bzip2=1.0.8=h7f98852_4
43 | - c-ares=1.18.1=h7f98852_0
44 | - ca-certificates=2022.9.24=ha878542_0
45 | - cairo=1.16.0=h18b612c_1001
46 | - certifi=2022.9.24=pyhd8ed1ab_0
47 | - cffi=1.15.0=py39hd667e15_1
48 | - charset-normalizer=2.0.4=pyhd3eb1b0_0
49 | - codecov=2.1.12=pyhd8ed1ab_0
50 | - colorama=0.4.4=pyhd3eb1b0_0
51 | - conda=4.12.0=py39h06a4308_0
52 | - conda-content-trust=0.1.1=pyhd3eb1b0_0
53 | - conda-package-handling=1.8.1=py39h7f8727e_0
54 | - coverage=6.5.0=py39hb9d737c_1
55 | - cryptography=36.0.0=py39h9ce1e76_0
56 | - curl=7.86.0=h2283fc2_1
57 | - exceptiongroup=1.0.4=pyhd8ed1ab_0
58 | - expat=2.5.0=h27087fc_0
59 | - fmt=9.1.0=h924138e_0
60 | - font-ttf-dejavu-sans-mono=2.37=hab24e00_0
61 | - font-ttf-inconsolata=3.000=h77eed37_0
62 | - font-ttf-source-code-pro=2.038=h77eed37_0
63 | - font-ttf-ubuntu=0.83=hab24e00_0
64 | - fontconfig=2.14.1=hc2a2eb6_0
65 | - fonts-conda-ecosystem=1=0
66 | - fonts-conda-forge=1=0
67 | - freetype=2.12.1=hca18f0e_1
68 | - fribidi=1.0.10=h36c2ea0_0
69 | - gcc_impl_linux-64=11.2.0=h82a94d6_16
70 | - gcc_linux-64=11.2.0=h39a9532_10
71 | - gettext=0.21.1=h27087fc_0
72 | - gfortran_impl_linux-64=11.2.0=h7a446d4_16
73 | - gfortran_linux-64=11.2.0=h777b47f_10
74 | - glib=2.74.1=h6239696_0
75 | - glib-tools=2.74.1=h6239696_0
76 | - graphite2=1.3.14=h295c915_1
77 | - gxx_impl_linux-64=11.2.0=h82a94d6_16
78 | - gxx_linux-64=11.2.0=hacbe6df_10
79 | - harfbuzz=4.3.0=hd55b92a_0
80 | - icu=58.2=hf484d3e_1000
81 | - idna=3.3=pyhd3eb1b0_0
82 | - importlib-metadata=4.11.4=py39hf3d152e_0
83 | - importlib_metadata=4.11.4=hd8ed1ab_0
84 | - iniconfig=1.1.1=pyh9f0ad1d_0
85 | - jinja2=3.1.2=pyhd8ed1ab_1
86 | - jpeg=9e=h166bdaf_2
87 | - jq=1.6=h36c2ea0_1000
88 | - kernel-headers_linux-64=2.6.32=he073ed8_15
89 | - keyutils=1.6.1=h166bdaf_0
90 | - krb5=1.19.3=h08a2579_0
91 | - lcms2=2.14=h6ed2654_0
92 | - ld_impl_linux-64=2.36.1=hea4e1c9_2
93 | - lerc=4.0.0=h27087fc_0
94 | - libarchive=3.5.2=hada088e_3
95 | - libblas=3.9.0=16_linux64_openblas
96 | - libcblas=3.9.0=16_linux64_openblas
97 | - libcurl=7.86.0=h2283fc2_1
98 | - libdeflate=1.14=h166bdaf_0
99 | - libedit=3.1.20191231=he28a2e2_2
100 | - libev=4.33=h516909a_1
101 | - libffi=3.4.2=h7f98852_5
102 | - libgcc-devel_linux-64=11.2.0=h0952999_16
103 | - libgcc-ng=12.2.0=h65d4601_19
104 | - libgfortran-ng=12.2.0=h69a702a_19
105 | - libgfortran5=12.2.0=h337968e_19
106 | - libglib=2.74.1=h7a41b64_0
107 | - libgomp=12.2.0=h65d4601_19
108 | - libiconv=1.17=h166bdaf_0
109 | - liblapack=3.9.0=16_linux64_openblas
110 | - libmamba=1.0.0=h9eff5f0_2
111 | - libmambapy=1.0.0=py39h37259de_2
112 | - libnghttp2=1.47.0=hff17c54_1
113 | - libnsl=2.0.0=h7f98852_0
114 | - libopenblas=0.3.21=pthreads_h78a6416_3
115 | - libpng=1.6.39=h753d276_0
116 | - libsanitizer=11.2.0=he4da1e4_16
117 | - libsolv=0.7.22=h6239696_0
118 | - libsqlite=3.40.0=h753d276_0
119 | - libssh2=1.10.0=hf14f497_3
120 | - libstdcxx-devel_linux-64=11.2.0=h0952999_16
121 | - libstdcxx-ng=12.2.0=h46fd767_19
122 | - libtiff=4.4.0=h55922b4_4
123 | - libuuid=2.32.1=h7f98852_1000
124 | - libwebp-base=1.2.4=h166bdaf_0
125 | - libxcb=1.13=h7f98852_1004
126 | - libxml2=2.9.14=h74e7548_0
127 | - libzlib=1.2.13=h166bdaf_4
128 | - lz4-c=1.9.3=h9c3ff4c_1
129 | - lzo=2.10=h516909a_1000
130 | - make=4.3=hd18ef5c_1
131 | - mamba=1.0.0=py39hc5d2bb1_2
132 | - markupsafe=2.1.1=py39hb9d737c_2
133 | - ncurses=6.3=h7f8727e_2
134 | - numpy=1.23.5=py39h3d75532_0
135 | - oniguruma=6.9.8=h166bdaf_0
136 | - openblas=0.3.21=pthreads_h320a7e8_3
137 | - openjpeg=2.5.0=h7d73246_1
138 | - openssl=3.0.7=h166bdaf_0
139 | - packaging=21.3=pyhd8ed1ab_0
140 | - pandas=1.5.2=py39h4661b88_0
141 | - pango=1.50.7=hbd2fdc8_0
142 | - pcre2=10.37=hc3806b6_1
143 | - pillow=9.2.0=py39hf3a2cdf_3
144 | - pip=21.2.4=py39h06a4308_0
145 | - pixman=0.38.0=h516909a_1003
146 | - pluggy=1.0.0=py39hf3d152e_4
147 | - pthread-stubs=0.4=h36c2ea0_1001
148 | - pybind11-abi=4=hd8ed1ab_3
149 | - pycosat=0.6.3=py39h27cfd23_0
150 | - pycparser=2.21=pyhd3eb1b0_0
151 | - pyopenssl=22.0.0=pyhd3eb1b0_0
152 | - pyparsing=3.0.9=pyhd8ed1ab_0
153 | - pysocks=1.7.1=py39h06a4308_0
154 | - pytest=7.2.0=py39hf3d152e_1
155 | - pytest-cov=4.0.0=pyhd8ed1ab_0
156 | - python=3.9.15=hba424b6_0_cpython
157 | - python-dateutil=2.8.2=pyhd8ed1ab_0
158 | - python-tzdata=2022.6=pyhd8ed1ab_0
159 | - python_abi=3.9=2_cp39
160 | - pytz=2022.6=pyhd8ed1ab_0
161 | - pytz-deprecation-shim=0.1.0.post0=py39hf3d152e_3
162 | - pyyaml=6.0=py39hb9d737c_5
163 | - r-askpass=1.1=r42h06615bd_3
164 | - r-backports=1.4.1=r42h06615bd_1
165 | - r-base=4.2.0=h1ae530e_0
166 | - r-bh=1.78.0_0=r42hc72bb7e_1
167 | - r-bit=4.0.5=r42h06615bd_0
168 | - r-bit64=4.0.5=r42h06615bd_1
169 | - r-bitops=1.0_7=r42h06615bd_1
170 | - r-blob=1.2.3=r42hc72bb7e_1
171 | - r-brio=1.1.3=r42h06615bd_1
172 | - r-cachem=1.0.6=r42h06615bd_1
173 | - r-callr=3.7.3=r42hc72bb7e_0
174 | - r-cli=3.4.1=r42h7525677_1
175 | - r-codetools=0.2_18=r42hc72bb7e_1
176 | - r-colorspace=2.0_3=r42h06615bd_1
177 | - r-cpp11=0.4.3=r42hc72bb7e_0
178 | - r-crayon=1.5.2=r42hc72bb7e_1
179 | - r-curl=4.3.3=r42h06615bd_1
180 | - r-dbi=1.1.3=r42hc72bb7e_1
181 | - r-desc=1.4.2=r42hc72bb7e_1
182 | - r-diffobj=0.3.5=r42h06615bd_1
183 | - r-digest=0.6.30=r42h7525677_0
184 | - r-ellipsis=0.3.2=r42h06615bd_1
185 | - r-evaluate=0.18=r42hc72bb7e_0
186 | - r-fansi=1.0.3=r42h06615bd_1
187 | - r-farver=2.1.1=r42h7525677_1
188 | - r-fastmap=1.1.0=r42h7525677_1
189 | - r-formatr=1.12=r42hc72bb7e_1
190 | - r-fs=1.5.2=r42h7525677_2
191 | - r-futile.logger=1.4.3=r42hc72bb7e_1004
192 | - r-futile.options=1.0.1=r42hc72bb7e_1003
193 | - r-ggplot2=3.4.0=r42hc72bb7e_0
194 | - r-glue=1.6.2=r42h06615bd_1
195 | - r-gtable=0.3.1=r42hc72bb7e_1
196 | - r-httr=1.4.4=r42hc72bb7e_1
197 | - r-isoband=0.2.6=r42h7525677_1
198 | - r-jsonlite=1.8.3=r42h06615bd_0
199 | - r-labeling=0.4.2=r42hc72bb7e_2
200 | - r-lambda.r=1.2.4=r42hc72bb7e_2
201 | - r-lattice=0.20_45=r42h06615bd_1
202 | - r-lifecycle=1.0.3=r42hc72bb7e_1
203 | - r-locfit=1.5_9.6=r42h06615bd_1
204 | - r-magrittr=2.0.3=r42h06615bd_1
205 | - r-mass=7.3_58.1=r42h06615bd_1
206 | - r-matrix=1.5_3=r42h5f7b363_0
207 | - r-matrixstats=0.63.0=r42h06615bd_0
208 | - r-memoise=2.0.1=r42hc72bb7e_1
209 | - r-mgcv=1.8_41=r42h5f7b363_0
210 | - r-mime=0.12=r42h06615bd_1
211 | - r-munsell=0.5.0=r42hc72bb7e_1005
212 | - r-nlme=3.1_160=r42h8da6f51_0
213 | - r-openssl=2.0.4=r42h1f3e0c5_0
214 | - r-pillar=1.8.1=r42hc72bb7e_1
215 | - r-pkgconfig=2.0.3=r42hc72bb7e_2
216 | - r-pkgload=1.3.2=r42hc72bb7e_0
217 | - r-plogr=0.2.0=r42hc72bb7e_1004
218 | - r-png=0.1_7=r42h06615bd_1006
219 | - r-praise=1.0.0=r42hc72bb7e_1006
220 | - r-processx=3.8.0=r42h06615bd_0
221 | - r-ps=1.7.2=r42h06615bd_0
222 | - r-r6=2.5.1=r42hc72bb7e_1
223 | - r-rcolorbrewer=1.1_3=r42h785f33e_1
224 | - r-rcpp=1.0.9=r42h7525677_2
225 | - r-rcpparmadillo=0.11.4.2.1=r42h9f5de39_0
226 | - r-rcurl=1.98_1.9=r42h06615bd_1
227 | - r-rematch2=2.1.2=r42hc72bb7e_2
228 | - r-rlang=1.0.6=r42h7525677_1
229 | - r-rprojroot=2.0.3=r42hc72bb7e_1
230 | - r-rsqlite=2.2.19=r42h7525677_0
231 | - r-scales=1.2.1=r42hc72bb7e_1
232 | - r-snow=0.4_4=r42hc72bb7e_1
233 | - r-survival=3.4_0=r42h06615bd_1
234 | - r-sys=3.4.1=r42h06615bd_0
235 | - r-testthat=3.1.5=r42h7525677_1
236 | - r-tibble=3.1.8=r42h06615bd_1
237 | - r-utf8=1.2.2=r42h06615bd_1
238 | - r-vctrs=0.5.1=r42h7525677_0
239 | - r-viridislite=0.4.1=r42hc72bb7e_1
240 | - r-waldo=0.4.0=r42hc72bb7e_1
241 | - r-withr=2.5.0=r42hc72bb7e_1
242 | - r-xml=3.99_0.11=r42h2b86b34_2
243 | - r-xtable=1.8_4=r42hc72bb7e_4
244 | - readline=8.1.2=h7f8727e_1
245 | - reportlab=3.5.68=py39he59360d_1
246 | - reproc=14.2.3=h7f98852_0
247 | - reproc-cpp=14.2.3=h9c3ff4c_0
248 | - requests=2.27.1=pyhd3eb1b0_0
249 | - rpy2=3.5.6=py39r42h2ae25f5_0
250 | - ruamel_yaml=0.15.100=py39h27cfd23_0
251 | - setuptools=61.2.0=py39h06a4308_0
252 | - simplegeneric=0.8.1=py_1
253 | - six=1.16.0=pyhd3eb1b0_1
254 | - sqlite=3.38.2=hc218d9a_0
255 | - sysroot_linux-64=2.12=he073ed8_15
256 | - tk=8.6.12=h27826a3_0
257 | - tktable=2.10=hb7b940f_3
258 | - toml=0.10.2=pyhd8ed1ab_0
259 | - tomli=2.0.1=pyhd8ed1ab_0
260 | - tqdm=4.63.0=pyhd3eb1b0_0
261 | - tzdata=2022a=hda174b7_0
262 | - tzlocal=4.2=py39hf3d152e_2
263 | - urllib3=1.26.8=pyhd3eb1b0_0
264 | - wheel=0.37.1=pyhd3eb1b0_0
265 | - xmltodict=0.13.0=pyhd8ed1ab_0
266 | - xorg-kbproto=1.0.7=h7f98852_1002
267 | - xorg-libice=1.0.10=h7f98852_0
268 | - xorg-libsm=1.2.3=hd9c2040_1000
269 | - xorg-libx11=1.7.2=h7f98852_0
270 | - xorg-libxau=1.0.9=h7f98852_0
271 | - xorg-libxdmcp=1.1.3=h7f98852_0
272 | - xorg-libxext=1.3.4=h7f98852_1
273 | - xorg-libxrender=0.9.10=h7f98852_1003
274 | - xorg-renderproto=0.11.1=h7f98852_1002
275 | - xorg-xextproto=7.3.0=h7f98852_1002
276 | - xorg-xproto=7.0.31=h7f98852_1007
277 | - xz=5.2.6=h166bdaf_0
278 | - yaml=0.2.5=h7b6447c_0
279 | - yaml-cpp=0.7.0=h27087fc_2
280 | - yq=2.13.0=pyhd8ed1ab_0
281 | - zipp=3.11.0=pyhd8ed1ab_0
282 | - zlib=1.2.13=h166bdaf_4
283 | - zstd=1.5.2=ha4553b6_0
284 | - pip:
285 | - diffexp==0.1
286 | prefix: /opt/conda
287 |
--------------------------------------------------------------------------------
/example/deseq_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Porting DESeq into python using rpy2#\n",
8 | "\n",
9 | "I will use a small example of [ERCC transcript](https://www.thermofisher.com/order/catalog/product/4456740) from [samples A and B in MAQC data](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3272078/)."
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": 1,
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "%load_ext autoreload\n",
19 | "%autoreload 2\n",
20 | "import pandas as pd \n",
21 | "import numpy as np"
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "metadata": {},
27 | "source": [
28 | "We will read the table and it should only contains count data of ERCC spikeins (rows) and 3 replicates from each of samples A and B (columns)."
29 | ]
30 | },
31 | {
32 | "cell_type": "code",
33 | "execution_count": 2,
34 | "metadata": {},
35 | "outputs": [
36 | {
37 | "data": {
38 | "text/html": [
39 | "
\n",
40 | "\n",
53 | "
\n",
54 | " \n",
55 | " \n",
56 | " | \n",
57 | " id | \n",
58 | " A_1 | \n",
59 | " A_2 | \n",
60 | " A_3 | \n",
61 | " B_1 | \n",
62 | " B_2 | \n",
63 | " B_3 | \n",
64 | "
\n",
65 | " \n",
66 | " \n",
67 | " \n",
68 | " 0 | \n",
69 | " ERCC-00002 | \n",
70 | " 111461 | \n",
71 | " 106261 | \n",
72 | " 107547 | \n",
73 | " 333944 | \n",
74 | " 199252 | \n",
75 | " 186947 | \n",
76 | "
\n",
77 | " \n",
78 | " 1 | \n",
79 | " ERCC-00003 | \n",
80 | " 6735 | \n",
81 | " 5387 | \n",
82 | " 5265 | \n",
83 | " 13937 | \n",
84 | " 8584 | \n",
85 | " 8596 | \n",
86 | "
\n",
87 | " \n",
88 | " 2 | \n",
89 | " ERCC-00004 | \n",
90 | " 17673 | \n",
91 | " 13983 | \n",
92 | " 15462 | \n",
93 | " 5065 | \n",
94 | " 3222 | \n",
95 | " 3353 | \n",
96 | "
\n",
97 | " \n",
98 | " 3 | \n",
99 | " ERCC-00009 | \n",
100 | " 4669 | \n",
101 | " 4431 | \n",
102 | " 4211 | \n",
103 | " 6939 | \n",
104 | " 4155 | \n",
105 | " 3647 | \n",
106 | "
\n",
107 | " \n",
108 | " 4 | \n",
109 | " ERCC-00012 | \n",
110 | " 0 | \n",
111 | " 2 | \n",
112 | " 0 | \n",
113 | " 0 | \n",
114 | " 0 | \n",
115 | " 0 | \n",
116 | "
\n",
117 | " \n",
118 | "
\n",
119 | "
"
120 | ],
121 | "text/plain": [
122 | " id A_1 A_2 A_3 B_1 B_2 B_3\n",
123 | "0 ERCC-00002 111461 106261 107547 333944 199252 186947\n",
124 | "1 ERCC-00003 6735 5387 5265 13937 8584 8596\n",
125 | "2 ERCC-00004 17673 13983 15462 5065 3222 3353\n",
126 | "3 ERCC-00009 4669 4431 4211 6939 4155 3647\n",
127 | "4 ERCC-00012 0 2 0 0 0 0"
128 | ]
129 | },
130 | "execution_count": 2,
131 | "metadata": {},
132 | "output_type": "execute_result"
133 | }
134 | ],
135 | "source": [
136 | "df = pd.read_table('../test/data/ercc.tsv')\n",
137 | "df.head()"
138 | ]
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "metadata": {},
143 | "source": [
144 | "And here, we will create a design matrix based on the samples in the count table. Note that the sample name has to be used as the ```pd.DataFrame``` index"
145 | ]
146 | },
147 | {
148 | "cell_type": "code",
149 | "execution_count": 3,
150 | "metadata": {},
151 | "outputs": [
152 | {
153 | "data": {
154 | "text/html": [
155 | "\n",
156 | "\n",
169 | "
\n",
170 | " \n",
171 | " \n",
172 | " | \n",
173 | " samplename | \n",
174 | " sample | \n",
175 | " replicate | \n",
176 | "
\n",
177 | " \n",
178 | " samplename | \n",
179 | " | \n",
180 | " | \n",
181 | " | \n",
182 | "
\n",
183 | " \n",
184 | " \n",
185 | " \n",
186 | " A_1 | \n",
187 | " A_1 | \n",
188 | " A | \n",
189 | " 1 | \n",
190 | "
\n",
191 | " \n",
192 | " A_2 | \n",
193 | " A_2 | \n",
194 | " A | \n",
195 | " 2 | \n",
196 | "
\n",
197 | " \n",
198 | " A_3 | \n",
199 | " A_3 | \n",
200 | " A | \n",
201 | " 3 | \n",
202 | "
\n",
203 | " \n",
204 | " B_1 | \n",
205 | " B_1 | \n",
206 | " B | \n",
207 | " 1 | \n",
208 | "
\n",
209 | " \n",
210 | " B_2 | \n",
211 | " B_2 | \n",
212 | " B | \n",
213 | " 2 | \n",
214 | "
\n",
215 | " \n",
216 | " B_3 | \n",
217 | " B_3 | \n",
218 | " B | \n",
219 | " 3 | \n",
220 | "
\n",
221 | " \n",
222 | "
\n",
223 | "
"
224 | ],
225 | "text/plain": [
226 | " samplename sample replicate\n",
227 | "samplename \n",
228 | "A_1 A_1 A 1\n",
229 | "A_2 A_2 A 2\n",
230 | "A_3 A_3 A 3\n",
231 | "B_1 B_1 B 1\n",
232 | "B_2 B_2 B 2\n",
233 | "B_3 B_3 B 3"
234 | ]
235 | },
236 | "execution_count": 3,
237 | "metadata": {},
238 | "output_type": "execute_result"
239 | }
240 | ],
241 | "source": [
242 | "sample_df = pd.DataFrame({'samplename': df.columns}) \\\n",
243 | " .query('samplename != \"id\"')\\\n",
244 | " .assign(sample = lambda d: d.samplename.str.extract('([AB])_', expand=False)) \\\n",
245 | " .assign(replicate = lambda d: d.samplename.str.extract('_([123])', expand=False)) \n",
246 | "sample_df.index = sample_df.samplename\n",
247 | "sample_df"
248 | ]
249 | },
250 | {
251 | "cell_type": "markdown",
252 | "metadata": {},
253 | "source": [
254 | "Running DESeq2 is jsut like how it is run in ```R```, but instead of the row.name being gene ID for the count table, we can jsut tell the function which column is the gene ID:"
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": 4,
260 | "metadata": {},
261 | "outputs": [
262 | {
263 | "name": "stderr",
264 | "output_type": "stream",
265 | "text": [
266 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: estimating size factors\n",
267 | "\n",
268 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: estimating dispersions\n",
269 | "\n",
270 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: gene-wise dispersion estimates\n",
271 | "\n",
272 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: mean-dispersion relationship\n",
273 | "\n",
274 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: final dispersion estimates\n",
275 | "\n",
276 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: fitting model and testing\n",
277 | "\n",
278 | "INFO:DESeq2:Using contrast: ['sample', 'B', 'A']\n"
279 | ]
280 | },
281 | {
282 | "data": {
283 | "text/html": [
284 | "\n",
285 | "\n",
298 | "
\n",
299 | " \n",
300 | " \n",
301 | " | \n",
302 | " baseMean | \n",
303 | " log2FoldChange | \n",
304 | " lfcSE | \n",
305 | " stat | \n",
306 | " pvalue | \n",
307 | " padj | \n",
308 | " id | \n",
309 | "
\n",
310 | " \n",
311 | " \n",
312 | " \n",
313 | " ERCC-00002 | \n",
314 | " 167917.342729 | \n",
315 | " 0.808857 | \n",
316 | " 0.047606 | \n",
317 | " 16.990537 | \n",
318 | " 9.650176e-65 | \n",
319 | " 1.102877e-63 | \n",
320 | " ERCC-00002 | \n",
321 | "
\n",
322 | " \n",
323 | " ERCC-00003 | \n",
324 | " 7902.634073 | \n",
325 | " 0.521731 | \n",
326 | " 0.058878 | \n",
327 | " 8.861252 | \n",
328 | " 7.912104e-19 | \n",
329 | " 4.868987e-18 | \n",
330 | " ERCC-00003 | \n",
331 | "
\n",
332 | " \n",
333 | " ERCC-00004 | \n",
334 | " 10567.048228 | \n",
335 | " -2.330122 | \n",
336 | " 0.055754 | \n",
337 | " -41.792764 | \n",
338 | " 0.000000e+00 | \n",
339 | " 0.000000e+00 | \n",
340 | " ERCC-00004 | \n",
341 | "
\n",
342 | " \n",
343 | " ERCC-00009 | \n",
344 | " 4672.573043 | \n",
345 | " -0.195660 | \n",
346 | " 0.061600 | \n",
347 | " -3.176286 | \n",
348 | " 1.491736e-03 | \n",
349 | " 3.616329e-03 | \n",
350 | " ERCC-00009 | \n",
351 | "
\n",
352 | " \n",
353 | " ERCC-00012 | \n",
354 | " 0.384257 | \n",
355 | " -1.565491 | \n",
356 | " 4.047562 | \n",
357 | " -0.386774 | \n",
358 | " 6.989237e-01 | \n",
359 | " NaN | \n",
360 | " ERCC-00012 | \n",
361 | "
\n",
362 | " \n",
363 | "
\n",
364 | "
"
365 | ],
366 | "text/plain": [
367 | " baseMean log2FoldChange lfcSE stat pvalue \\\n",
368 | "ERCC-00002 167917.342729 0.808857 0.047606 16.990537 9.650176e-65 \n",
369 | "ERCC-00003 7902.634073 0.521731 0.058878 8.861252 7.912104e-19 \n",
370 | "ERCC-00004 10567.048228 -2.330122 0.055754 -41.792764 0.000000e+00 \n",
371 | "ERCC-00009 4672.573043 -0.195660 0.061600 -3.176286 1.491736e-03 \n",
372 | "ERCC-00012 0.384257 -1.565491 4.047562 -0.386774 6.989237e-01 \n",
373 | "\n",
374 | " padj id \n",
375 | "ERCC-00002 1.102877e-63 ERCC-00002 \n",
376 | "ERCC-00003 4.868987e-18 ERCC-00003 \n",
377 | "ERCC-00004 0.000000e+00 ERCC-00004 \n",
378 | "ERCC-00009 3.616329e-03 ERCC-00009 \n",
379 | "ERCC-00012 NaN ERCC-00012 "
380 | ]
381 | },
382 | "execution_count": 4,
383 | "metadata": {},
384 | "output_type": "execute_result"
385 | }
386 | ],
387 | "source": [
388 | "from diffexpr.py_deseq import py_DESeq2\n",
389 | "\n",
390 | "dds = py_DESeq2(count_matrix = df,\n",
391 | " design_matrix = sample_df,\n",
392 | " design_formula = '~ replicate + sample',\n",
393 | " gene_column = 'id') # <- telling DESeq2 this should be the gene ID column\n",
394 | " \n",
395 | "dds.run_deseq() \n",
396 | "dds.get_deseq_result(contrast = ['sample','B','A'])\n",
397 | "res = dds.deseq_result \n",
398 | "res.head()"
399 | ]
400 | },
401 | {
402 | "cell_type": "code",
403 | "execution_count": 5,
404 | "metadata": {},
405 | "outputs": [
406 | {
407 | "name": "stderr",
408 | "output_type": "stream",
409 | "text": [
410 | "INFO:DESeq2:Normalizing counts\n"
411 | ]
412 | },
413 | {
414 | "data": {
415 | "text/html": [
416 | "\n",
417 | "\n",
430 | "
\n",
431 | " \n",
432 | " \n",
433 | " | \n",
434 | " A_1 | \n",
435 | " A_2 | \n",
436 | " A_3 | \n",
437 | " B_1 | \n",
438 | " B_2 | \n",
439 | " B_3 | \n",
440 | " id | \n",
441 | "
\n",
442 | " \n",
443 | " \n",
444 | " \n",
445 | " ERCC-00002 | \n",
446 | " 115018.353297 | \n",
447 | " 122494.471246 | \n",
448 | " 128809.545168 | \n",
449 | " 218857.357008 | \n",
450 | " 207880.854689 | \n",
451 | " 214443.474968 | \n",
452 | " ERCC-00002 | \n",
453 | "
\n",
454 | " \n",
455 | " ERCC-00003 | \n",
456 | " 6949.952086 | \n",
457 | " 6209.970889 | \n",
458 | " 6305.915138 | \n",
459 | " 9133.911628 | \n",
460 | " 8955.740754 | \n",
461 | " 9860.313944 | \n",
462 | " ERCC-00003 | \n",
463 | "
\n",
464 | " \n",
465 | " ERCC-00004 | \n",
466 | " 18237.045763 | \n",
467 | " 16119.180051 | \n",
468 | " 18518.909755 | \n",
469 | " 3319.456296 | \n",
470 | " 3361.532701 | \n",
471 | " 3846.164804 | \n",
472 | " ERCC-00004 | \n",
473 | "
\n",
474 | " \n",
475 | " ERCC-00009 | \n",
476 | " 4818.014297 | \n",
477 | " 5107.922964 | \n",
478 | " 5043.534405 | \n",
479 | " 4547.622357 | \n",
480 | " 4334.937422 | \n",
481 | " 4183.406812 | \n",
482 | " ERCC-00009 | \n",
483 | "
\n",
484 | " \n",
485 | " ERCC-00012 | \n",
486 | " 0.000000 | \n",
487 | " 2.305540 | \n",
488 | " 0.000000 | \n",
489 | " 0.000000 | \n",
490 | " 0.000000 | \n",
491 | " 0.000000 | \n",
492 | " ERCC-00012 | \n",
493 | "
\n",
494 | " \n",
495 | " ... | \n",
496 | " ... | \n",
497 | " ... | \n",
498 | " ... | \n",
499 | " ... | \n",
500 | " ... | \n",
501 | " ... | \n",
502 | " ... | \n",
503 | "
\n",
504 | " \n",
505 | " ERCC-00164 | \n",
506 | " 2.063831 | \n",
507 | " 1.152770 | \n",
508 | " 5.988523 | \n",
509 | " 3.276857 | \n",
510 | " 1.043306 | \n",
511 | " 2.294163 | \n",
512 | " ERCC-00164 | \n",
513 | "
\n",
514 | " \n",
515 | " ERCC-00165 | \n",
516 | " 269.329992 | \n",
517 | " 246.692736 | \n",
518 | " 287.449123 | \n",
519 | " 513.811202 | \n",
520 | " 484.094095 | \n",
521 | " 489.803869 | \n",
522 | " ERCC-00165 | \n",
523 | "
\n",
524 | " \n",
525 | " ERCC-00168 | \n",
526 | " 1.031916 | \n",
527 | " 3.458309 | \n",
528 | " 0.000000 | \n",
529 | " 4.587600 | \n",
530 | " 4.173225 | \n",
531 | " 1.147082 | \n",
532 | " ERCC-00168 | \n",
533 | "
\n",
534 | " \n",
535 | " ERCC-00170 | \n",
536 | " 137.244785 | \n",
537 | " 148.707304 | \n",
538 | " 135.340629 | \n",
539 | " 26.870229 | \n",
540 | " 10.433062 | \n",
541 | " 32.118286 | \n",
542 | " ERCC-00170 | \n",
543 | "
\n",
544 | " \n",
545 | " ERCC-00171 | \n",
546 | " 8707.304484 | \n",
547 | " 9622.169484 | \n",
548 | " 8818.699555 | \n",
549 | " 7691.439109 | \n",
550 | " 7691.253592 | \n",
551 | " 6892.813691 | \n",
552 | " ERCC-00171 | \n",
553 | "
\n",
554 | " \n",
555 | "
\n",
556 | "
92 rows × 7 columns
\n",
557 | "
"
558 | ],
559 | "text/plain": [
560 | " A_1 A_2 A_3 B_1 \\\n",
561 | "ERCC-00002 115018.353297 122494.471246 128809.545168 218857.357008 \n",
562 | "ERCC-00003 6949.952086 6209.970889 6305.915138 9133.911628 \n",
563 | "ERCC-00004 18237.045763 16119.180051 18518.909755 3319.456296 \n",
564 | "ERCC-00009 4818.014297 5107.922964 5043.534405 4547.622357 \n",
565 | "ERCC-00012 0.000000 2.305540 0.000000 0.000000 \n",
566 | "... ... ... ... ... \n",
567 | "ERCC-00164 2.063831 1.152770 5.988523 3.276857 \n",
568 | "ERCC-00165 269.329992 246.692736 287.449123 513.811202 \n",
569 | "ERCC-00168 1.031916 3.458309 0.000000 4.587600 \n",
570 | "ERCC-00170 137.244785 148.707304 135.340629 26.870229 \n",
571 | "ERCC-00171 8707.304484 9622.169484 8818.699555 7691.439109 \n",
572 | "\n",
573 | " B_2 B_3 id \n",
574 | "ERCC-00002 207880.854689 214443.474968 ERCC-00002 \n",
575 | "ERCC-00003 8955.740754 9860.313944 ERCC-00003 \n",
576 | "ERCC-00004 3361.532701 3846.164804 ERCC-00004 \n",
577 | "ERCC-00009 4334.937422 4183.406812 ERCC-00009 \n",
578 | "ERCC-00012 0.000000 0.000000 ERCC-00012 \n",
579 | "... ... ... ... \n",
580 | "ERCC-00164 1.043306 2.294163 ERCC-00164 \n",
581 | "ERCC-00165 484.094095 489.803869 ERCC-00165 \n",
582 | "ERCC-00168 4.173225 1.147082 ERCC-00168 \n",
583 | "ERCC-00170 10.433062 32.118286 ERCC-00170 \n",
584 | "ERCC-00171 7691.253592 6892.813691 ERCC-00171 \n",
585 | "\n",
586 | "[92 rows x 7 columns]"
587 | ]
588 | },
589 | "execution_count": 5,
590 | "metadata": {},
591 | "output_type": "execute_result"
592 | }
593 | ],
594 | "source": [
595 | "dds.normalized_count() #DESeq2 normalized count"
596 | ]
597 | },
598 | {
599 | "cell_type": "code",
600 | "execution_count": 6,
601 | "metadata": {},
602 | "outputs": [
603 | {
604 | "data": {
605 | "text/plain": [
606 | "['Intercept', 'replicate_2_vs_1', 'replicate_3_vs_1', 'sample_B_vs_A']"
607 | ]
608 | },
609 | "execution_count": 6,
610 | "metadata": {},
611 | "output_type": "execute_result"
612 | }
613 | ],
614 | "source": [
615 | "dds.comparison # show coefficients for GLM"
616 | ]
617 | },
618 | {
619 | "cell_type": "code",
620 | "execution_count": 7,
621 | "metadata": {},
622 | "outputs": [
623 | {
624 | "name": "stderr",
625 | "output_type": "stream",
626 | "text": [
627 | "WARNING:rpy2.rinterface_lib.callbacks:R[write to console]: using 'apeglm' for LFC shrinkage. If used in published research, please cite:\n",
628 | " Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for\n",
629 | " sequence count data: removing the noise and preserving large differences.\n",
630 | " Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895\n",
631 | "\n"
632 | ]
633 | },
634 | {
635 | "data": {
636 | "text/html": [
637 | "\n",
638 | "\n",
651 | "
\n",
652 | " \n",
653 | " \n",
654 | " | \n",
655 | " id | \n",
656 | " baseMean | \n",
657 | " log2FoldChange | \n",
658 | " lfcSE | \n",
659 | " pvalue | \n",
660 | " padj | \n",
661 | "
\n",
662 | " \n",
663 | " \n",
664 | " \n",
665 | " 0 | \n",
666 | " ERCC-00002 | \n",
667 | " 167917.342729 | \n",
668 | " 0.807316 | \n",
669 | " 0.047609 | \n",
670 | " 9.650176e-65 | \n",
671 | " 1.102877e-63 | \n",
672 | "
\n",
673 | " \n",
674 | " 1 | \n",
675 | " ERCC-00003 | \n",
676 | " 7902.634073 | \n",
677 | " 0.519944 | \n",
678 | " 0.058823 | \n",
679 | " 7.912104e-19 | \n",
680 | " 4.868987e-18 | \n",
681 | "
\n",
682 | " \n",
683 | " 2 | \n",
684 | " ERCC-00004 | \n",
685 | " 10567.048228 | \n",
686 | " -2.328037 | \n",
687 | " 0.055783 | \n",
688 | " 0.000000e+00 | \n",
689 | " 0.000000e+00 | \n",
690 | "
\n",
691 | " \n",
692 | " 3 | \n",
693 | " ERCC-00009 | \n",
694 | " 4672.573043 | \n",
695 | " -0.194594 | \n",
696 | " 0.061466 | \n",
697 | " 1.491736e-03 | \n",
698 | " 3.616329e-03 | \n",
699 | "
\n",
700 | " \n",
701 | " 4 | \n",
702 | " ERCC-00012 | \n",
703 | " 0.384257 | \n",
704 | " -0.052326 | \n",
705 | " 0.820696 | \n",
706 | " 6.989237e-01 | \n",
707 | " NaN | \n",
708 | "
\n",
709 | " \n",
710 | "
\n",
711 | "
"
712 | ],
713 | "text/plain": [
714 | " id baseMean log2FoldChange lfcSE pvalue \\\n",
715 | "0 ERCC-00002 167917.342729 0.807316 0.047609 9.650176e-65 \n",
716 | "1 ERCC-00003 7902.634073 0.519944 0.058823 7.912104e-19 \n",
717 | "2 ERCC-00004 10567.048228 -2.328037 0.055783 0.000000e+00 \n",
718 | "3 ERCC-00009 4672.573043 -0.194594 0.061466 1.491736e-03 \n",
719 | "4 ERCC-00012 0.384257 -0.052326 0.820696 6.989237e-01 \n",
720 | "\n",
721 | " padj \n",
722 | "0 1.102877e-63 \n",
723 | "1 4.868987e-18 \n",
724 | "2 0.000000e+00 \n",
725 | "3 3.616329e-03 \n",
726 | "4 NaN "
727 | ]
728 | },
729 | "execution_count": 7,
730 | "metadata": {},
731 | "output_type": "execute_result"
732 | }
733 | ],
734 | "source": [
735 | "# from the last cell, we see the arrangement of coefficients, \n",
736 | "# so that we can now use \"coef\" for lfcShrink\n",
737 | "# the comparison we want to focus on is 'sample_B_vs_A', so coef = 4 will be used\n",
738 | "lfc_res = dds.lfcShrink(coef=4, method='apeglm')\n",
739 | "lfc_res.head()"
740 | ]
741 | }
742 | ],
743 | "metadata": {
744 | "kernelspec": {
745 | "display_name": "Python 3 (ipykernel)",
746 | "language": "python",
747 | "name": "python3"
748 | },
749 | "language_info": {
750 | "codemirror_mode": {
751 | "name": "ipython",
752 | "version": 3
753 | },
754 | "file_extension": ".py",
755 | "mimetype": "text/x-python",
756 | "name": "python",
757 | "nbconvert_exporter": "python",
758 | "pygments_lexer": "ipython3",
759 | "version": "3.9.15"
760 | }
761 | },
762 | "nbformat": 4,
763 | "nbformat_minor": 4
764 | }
765 |
--------------------------------------------------------------------------------
/example/jupyter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wckdouglas/diffexpr/c78aba6a8ec7b26331f7e258d5e2cf1c5d559eed/example/jupyter.png
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "atomicwrites"
3 | version = "1.4.0"
4 | description = "Atomic file writes."
5 | category = "main"
6 | optional = false
7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
8 |
9 | [[package]]
10 | name = "attrs"
11 | version = "20.3.0"
12 | description = "Classes Without Boilerplate"
13 | category = "main"
14 | optional = false
15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
16 |
17 | [package.extras]
18 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
19 | docs = ["furo", "sphinx", "zope.interface"]
20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
22 |
23 | [[package]]
24 | name = "biopython"
25 | version = "1.78"
26 | description = "Freely available tools for computational molecular biology."
27 | category = "main"
28 | optional = false
29 | python-versions = ">=3.6"
30 |
31 | [package.dependencies]
32 | numpy = "*"
33 |
34 | [[package]]
35 | name = "certifi"
36 | version = "2020.12.5"
37 | description = "Python package for providing Mozilla's CA Bundle."
38 | category = "main"
39 | optional = false
40 | python-versions = "*"
41 |
42 | [[package]]
43 | name = "cffi"
44 | version = "1.14.5"
45 | description = "Foreign Function Interface for Python calling C code."
46 | category = "main"
47 | optional = false
48 | python-versions = "*"
49 |
50 | [package.dependencies]
51 | pycparser = "*"
52 |
53 | [[package]]
54 | name = "chardet"
55 | version = "4.0.0"
56 | description = "Universal encoding detector for Python 2 and 3"
57 | category = "main"
58 | optional = false
59 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
60 |
61 | [[package]]
62 | name = "codecov"
63 | version = "2.1.11"
64 | description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab"
65 | category = "main"
66 | optional = false
67 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
68 |
69 | [package.dependencies]
70 | coverage = "*"
71 | requests = ">=2.7.9"
72 |
73 | [[package]]
74 | name = "colorama"
75 | version = "0.4.4"
76 | description = "Cross-platform colored terminal text."
77 | category = "main"
78 | optional = false
79 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
80 |
81 | [[package]]
82 | name = "coverage"
83 | version = "5.5"
84 | description = "Code coverage measurement for Python"
85 | category = "main"
86 | optional = false
87 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
88 |
89 | [package.extras]
90 | toml = ["toml"]
91 |
92 | [[package]]
93 | name = "idna"
94 | version = "2.10"
95 | description = "Internationalized Domain Names in Applications (IDNA)"
96 | category = "main"
97 | optional = false
98 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
99 |
100 | [[package]]
101 | name = "importlib-metadata"
102 | version = "3.7.2"
103 | description = "Read metadata from Python packages"
104 | category = "main"
105 | optional = false
106 | python-versions = ">=3.6"
107 |
108 | [package.dependencies]
109 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
110 | zipp = ">=0.5"
111 |
112 | [package.extras]
113 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
114 | testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
115 |
116 | [[package]]
117 | name = "iniconfig"
118 | version = "1.1.1"
119 | description = "iniconfig: brain-dead simple config-ini parsing"
120 | category = "main"
121 | optional = false
122 | python-versions = "*"
123 |
124 | [[package]]
125 | name = "jinja2"
126 | version = "2.11.3"
127 | description = "A very fast and expressive template engine."
128 | category = "main"
129 | optional = false
130 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
131 |
132 | [package.dependencies]
133 | MarkupSafe = ">=0.23"
134 |
135 | [package.extras]
136 | i18n = ["Babel (>=0.8)"]
137 |
138 | [[package]]
139 | name = "markupsafe"
140 | version = "1.1.1"
141 | description = "Safely add untrusted strings to HTML/XML markup."
142 | category = "main"
143 | optional = false
144 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
145 |
146 | [[package]]
147 | name = "numpy"
148 | version = "1.19.5"
149 | description = "NumPy is the fundamental package for array computing with Python."
150 | category = "main"
151 | optional = false
152 | python-versions = ">=3.6"
153 |
154 | [[package]]
155 | name = "packaging"
156 | version = "20.9"
157 | description = "Core utilities for Python packages"
158 | category = "main"
159 | optional = false
160 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
161 |
162 | [package.dependencies]
163 | pyparsing = ">=2.0.2"
164 |
165 | [[package]]
166 | name = "pandas"
167 | version = "1.0.1"
168 | description = "Powerful data structures for data analysis, time series, and statistics"
169 | category = "main"
170 | optional = false
171 | python-versions = ">=3.6.1"
172 |
173 | [package.dependencies]
174 | numpy = ">=1.13.3"
175 | python-dateutil = ">=2.6.1"
176 | pytz = ">=2017.2"
177 |
178 | [package.extras]
179 | test = ["pytest (>=4.0.2)", "pytest-xdist", "hypothesis (>=3.58)"]
180 |
181 | [[package]]
182 | name = "pillow"
183 | version = "8.1.2"
184 | description = "Python Imaging Library (Fork)"
185 | category = "main"
186 | optional = false
187 | python-versions = ">=3.6"
188 |
189 | [[package]]
190 | name = "pluggy"
191 | version = "0.13.1"
192 | description = "plugin and hook calling mechanisms for python"
193 | category = "main"
194 | optional = false
195 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
196 |
197 | [package.dependencies]
198 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
199 |
200 | [package.extras]
201 | dev = ["pre-commit", "tox"]
202 |
203 | [[package]]
204 | name = "py"
205 | version = "1.10.0"
206 | description = "library with cross-python path, ini-parsing, io, code, log facilities"
207 | category = "main"
208 | optional = false
209 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
210 |
211 | [[package]]
212 | name = "pycparser"
213 | version = "2.20"
214 | description = "C parser in Python"
215 | category = "main"
216 | optional = false
217 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
218 |
219 | [[package]]
220 | name = "pyparsing"
221 | version = "2.4.7"
222 | description = "Python parsing module"
223 | category = "main"
224 | optional = false
225 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
226 |
227 | [[package]]
228 | name = "pytest"
229 | version = "6.2.2"
230 | description = "pytest: simple powerful testing with Python"
231 | category = "main"
232 | optional = false
233 | python-versions = ">=3.6"
234 |
235 | [package.dependencies]
236 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
237 | attrs = ">=19.2.0"
238 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
239 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
240 | iniconfig = "*"
241 | packaging = "*"
242 | pluggy = ">=0.12,<1.0.0a1"
243 | py = ">=1.8.2"
244 | toml = "*"
245 |
246 | [package.extras]
247 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
248 |
249 | [[package]]
250 | name = "pytest-cov"
251 | version = "2.11.1"
252 | description = "Pytest plugin for measuring coverage."
253 | category = "dev"
254 | optional = false
255 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
256 |
257 | [package.dependencies]
258 | coverage = ">=5.2.1"
259 | pytest = ">=4.6"
260 |
261 | [package.extras]
262 | testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"]
263 |
264 | [[package]]
265 | name = "python-dateutil"
266 | version = "2.8.1"
267 | description = "Extensions to the standard Python datetime module"
268 | category = "main"
269 | optional = false
270 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
271 |
272 | [package.dependencies]
273 | six = ">=1.5"
274 |
275 | [[package]]
276 | name = "pytz"
277 | version = "2021.1"
278 | description = "World timezone definitions, modern and historical"
279 | category = "main"
280 | optional = false
281 | python-versions = "*"
282 |
283 | [[package]]
284 | name = "reportlab"
285 | version = "3.5.65"
286 | description = "The Reportlab Toolkit"
287 | category = "main"
288 | optional = false
289 | python-versions = ">=2.7, >=3.6, <4"
290 |
291 | [package.dependencies]
292 | pillow = ">=4.0.0"
293 |
294 | [package.extras]
295 | rlpycairo = ["rlPyCairo (>=0.0.5)"]
296 |
297 | [[package]]
298 | name = "requests"
299 | version = "2.25.1"
300 | description = "Python HTTP for Humans."
301 | category = "main"
302 | optional = false
303 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
304 |
305 | [package.dependencies]
306 | certifi = ">=2017.4.17"
307 | chardet = ">=3.0.2,<5"
308 | idna = ">=2.5,<3"
309 | urllib3 = ">=1.21.1,<1.27"
310 |
311 | [package.extras]
312 | security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
313 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
314 |
315 | [[package]]
316 | name = "rpy2"
317 | version = "3.0.0"
318 | description = "Python interface to the R language (embedded R)"
319 | category = "main"
320 | optional = false
321 | python-versions = "*"
322 |
323 | [package.dependencies]
324 | cffi = ">=1.0.0"
325 | jinja2 = "*"
326 | pytest = "*"
327 | simplegeneric = "*"
328 |
329 | [[package]]
330 | name = "simplegeneric"
331 | version = "0.8.1"
332 | description = "Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)"
333 | category = "main"
334 | optional = false
335 | python-versions = "*"
336 |
337 | [[package]]
338 | name = "six"
339 | version = "1.15.0"
340 | description = "Python 2 and 3 compatibility utilities"
341 | category = "main"
342 | optional = false
343 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
344 |
345 | [[package]]
346 | name = "toml"
347 | version = "0.10.2"
348 | description = "Python Library for Tom's Obvious, Minimal Language"
349 | category = "main"
350 | optional = false
351 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
352 |
353 | [[package]]
354 | name = "typing-extensions"
355 | version = "3.7.4.3"
356 | description = "Backported and Experimental Type Hints for Python 3.5+"
357 | category = "main"
358 | optional = false
359 | python-versions = "*"
360 |
361 | [[package]]
362 | name = "tzlocal"
363 | version = "2.1"
364 | description = "tzinfo object for the local timezone"
365 | category = "main"
366 | optional = false
367 | python-versions = "*"
368 |
369 | [package.dependencies]
370 | pytz = "*"
371 |
372 | [[package]]
373 | name = "urllib3"
374 | version = "1.26.3"
375 | description = "HTTP library with thread-safe connection pooling, file post, and more."
376 | category = "main"
377 | optional = false
378 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
379 |
380 | [package.extras]
381 | brotli = ["brotlipy (>=0.6.0)"]
382 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
383 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
384 |
385 | [[package]]
386 | name = "zipp"
387 | version = "3.4.1"
388 | description = "Backport of pathlib-compatible object wrapper for zip files"
389 | category = "main"
390 | optional = false
391 | python-versions = ">=3.6"
392 |
393 | [package.extras]
394 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
395 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
396 |
397 | [metadata]
398 | lock-version = "1.1"
399 | python-versions = "^3.6.1"
400 | content-hash = "c9d6fe96949af4e87868a6c5cbe8b93e411e9439bd6213fccc138694a65be86a"
401 |
402 | [metadata.files]
403 | atomicwrites = [
404 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
405 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
406 | ]
407 | attrs = [
408 | {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
409 | {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
410 | ]
411 | biopython = [
412 | {file = "biopython-1.78-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0b9fbb0d3022dc22716da108b8a81b80d952cd97ac1f106de491dce850f92f62"},
413 | {file = "biopython-1.78-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f5021a398c898b9cf6815cc5171c146a601b935b55364c53e6516a2545ab740c"},
414 | {file = "biopython-1.78-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:195f099c2c0c39518b6df921ab2b3cc43a601896018fc61909ac8385d5878866"},
415 | {file = "biopython-1.78-cp36-cp36m-win32.whl", hash = "sha256:75b55000793f6b76334b8e80dc7e6d8cd2b019af917aa431cea6646e8e696c7f"},
416 | {file = "biopython-1.78-cp36-cp36m-win_amd64.whl", hash = "sha256:f1076653937947773768455556b1d24acad9575759e9089082f32636b09add54"},
417 | {file = "biopython-1.78-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e0af107cc62a905d13d35dd7b38f335a37752ede45e4617139e84409a6a88dc4"},
418 | {file = "biopython-1.78-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4565c97fab16c5697d067b821b6a1da0ec3ef36a9c96cf103ac7b4a94eb9f9ba"},
419 | {file = "biopython-1.78-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0df5cddef2819c975e6508adf5d85aa046e449df5420d02b04871c7836b41273"},
420 | {file = "biopython-1.78-cp37-cp37m-win32.whl", hash = "sha256:5c0b369f91a76b8e5e36624d075585c3f0f088ea4a6e3d015c48f08e48ce0114"},
421 | {file = "biopython-1.78-cp37-cp37m-win_amd64.whl", hash = "sha256:cc3b0b78022d14f11d508038a288a189d03c97c476d6636c7b6f98bd8bc8462b"},
422 | {file = "biopython-1.78-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:010142a8ec2549ff0649edd497658964ef1a18eefdb9fd942ec1e81b292ce2d9"},
423 | {file = "biopython-1.78-cp38-cp38-manylinux1_i686.whl", hash = "sha256:194528eda6856a4c68f840ca0bcc9b544a5edee3548b97521084e7ac38c833ca"},
424 | {file = "biopython-1.78-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ada611f12ee3b0bef7308ef41ee7b94898613b369ab44e0268d74bd1d6a06920"},
425 | {file = "biopython-1.78-cp38-cp38-win32.whl", hash = "sha256:48d424453a5512a1d1d41a4acabdfe5291da1f491a2d3606f2b0e4fbd63aeda6"},
426 | {file = "biopython-1.78-cp38-cp38-win_amd64.whl", hash = "sha256:2bd5a630be2a8e593094f7b1717fc962eda8931b68542b97fbf9bd8e2ac1e08d"},
427 | {file = "biopython-1.78.tar.gz", hash = "sha256:1ee0a0b6c2376680fea6642d5080baa419fd73df104a62d58a8baf7a8bbe4564"},
428 | ]
429 | certifi = [
430 | {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
431 | {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
432 | ]
433 | cffi = [
434 | {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
435 | {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
436 | {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
437 | {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
438 | {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
439 | {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
440 | {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
441 | {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
442 | {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
443 | {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
444 | {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
445 | {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
446 | {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
447 | {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
448 | {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
449 | {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
450 | {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
451 | {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
452 | {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
453 | {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
454 | {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
455 | {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
456 | {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
457 | {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
458 | {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
459 | {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
460 | {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
461 | {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
462 | {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
463 | {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
464 | {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
465 | {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
466 | {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
467 | {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
468 | {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
469 | {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
470 | {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
471 | ]
472 | chardet = [
473 | {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
474 | {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
475 | ]
476 | codecov = [
477 | {file = "codecov-2.1.11-py2.py3-none-any.whl", hash = "sha256:ba8553a82942ce37d4da92b70ffd6d54cf635fc1793ab0a7dc3fecd6ebfb3df8"},
478 | {file = "codecov-2.1.11-py3.8.egg", hash = "sha256:e95901d4350e99fc39c8353efa450050d2446c55bac91d90fcfd2354e19a6aef"},
479 | {file = "codecov-2.1.11.tar.gz", hash = "sha256:6cde272454009d27355f9434f4e49f238c0273b216beda8472a65dc4957f473b"},
480 | ]
481 | colorama = [
482 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
483 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
484 | ]
485 | coverage = [
486 | {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
487 | {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
488 | {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
489 | {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
490 | {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
491 | {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
492 | {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
493 | {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
494 | {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
495 | {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
496 | {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
497 | {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
498 | {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
499 | {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
500 | {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
501 | {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
502 | {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
503 | {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
504 | {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
505 | {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
506 | {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
507 | {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
508 | {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
509 | {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
510 | {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
511 | {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
512 | {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
513 | {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
514 | {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
515 | {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
516 | {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
517 | {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
518 | {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
519 | {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
520 | {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
521 | {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
522 | {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
523 | {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
524 | {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
525 | {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
526 | {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
527 | {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
528 | {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
529 | {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
530 | {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
531 | {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
532 | {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
533 | {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
534 | {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
535 | {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
536 | {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
537 | {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
538 | ]
539 | idna = [
540 | {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
541 | {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
542 | ]
543 | importlib-metadata = [
544 | {file = "importlib_metadata-3.7.2-py3-none-any.whl", hash = "sha256:407d13f55dc6f2a844e62325d18ad7019a436c4bfcaee34cda35f2be6e7c3e34"},
545 | {file = "importlib_metadata-3.7.2.tar.gz", hash = "sha256:18d5ff601069f98d5d605b6a4b50c18a34811d655c55548adc833e687289acde"},
546 | ]
547 | iniconfig = [
548 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
549 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
550 | ]
551 | jinja2 = [
552 | {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
553 | {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
554 | ]
555 | markupsafe = [
556 | {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
557 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
558 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
559 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
560 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
561 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
562 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
563 | {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
564 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
565 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
566 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
567 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
568 | {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
569 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
570 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
571 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
572 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
573 | {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
574 | {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"},
575 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
576 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
577 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"},
578 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"},
579 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"},
580 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
581 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
582 | {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
583 | {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"},
584 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
585 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
586 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"},
587 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"},
588 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"},
589 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
590 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
591 | {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
592 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
593 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
594 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"},
595 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"},
596 | {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"},
597 | {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
598 | {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
599 | {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"},
600 | {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"},
601 | {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"},
602 | {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"},
603 | {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"},
604 | {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},
605 | {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"},
606 | {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"},
607 | {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
608 | ]
609 | numpy = [
610 | {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"},
611 | {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"},
612 | {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"},
613 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"},
614 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"},
615 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"},
616 | {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"},
617 | {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"},
618 | {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"},
619 | {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"},
620 | {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"},
621 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"},
622 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"},
623 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"},
624 | {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"},
625 | {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"},
626 | {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"},
627 | {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"},
628 | {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"},
629 | {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"},
630 | {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"},
631 | {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"},
632 | {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"},
633 | {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"},
634 | {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"},
635 | {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"},
636 | {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"},
637 | {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"},
638 | {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"},
639 | {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"},
640 | {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"},
641 | {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"},
642 | {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"},
643 | {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"},
644 | ]
645 | packaging = [
646 | {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
647 | {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
648 | ]
649 | pandas = [
650 | {file = "pandas-1.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74a470d349d52b9d00a2ba192ae1ee22155bb0a300fd1ccb2961006c3fa98ed3"},
651 | {file = "pandas-1.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e2140e1bbf9c46db9936ee70f4be6584d15ff8dc3dfff1da022d71227d53bad3"},
652 | {file = "pandas-1.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d10e83866b48c0cdb83281f786564e2a2b51a7ae7b8a950c3442ad3c9e36b48c"},
653 | {file = "pandas-1.0.1-cp36-cp36m-win32.whl", hash = "sha256:303827f0bb40ff610fbada5b12d50014811efcc37aaf6ef03202dc3054bfdda1"},
654 | {file = "pandas-1.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6f38969e2325056f9959efbe06c27aa2e94dd35382265ad0703681d993036052"},
655 | {file = "pandas-1.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2530aea4fe46e8df7829c3f05e0a0f821c893885d53cb8ac9b89cc67c143448c"},
656 | {file = "pandas-1.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3b019e3ea9f5d0cfee0efabae2cfd3976874e90bcc3e97b29600e5a9b345ae3d"},
657 | {file = "pandas-1.0.1-cp37-cp37m-win32.whl", hash = "sha256:23e177d43e4bf68950b0f8788b6a2fef2f478f4ec94883acb627b9264522a98a"},
658 | {file = "pandas-1.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a9fbe41663416bb70ed05f4e16c5f377519c0dc292ba9aa45f5356e37df03a38"},
659 | {file = "pandas-1.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f9a509f6f11fa8b9313002ebdf6f690a7aa1dd91efd95d90185371a0d68220e"},
660 | {file = "pandas-1.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:942b5d04762feb0e55b2ad97ce2b254a0ffdd344b56493b04a627266e24f2d82"},
661 | {file = "pandas-1.0.1-cp38-cp38-win32.whl", hash = "sha256:7d77034e402165b947f43050a8a415aa3205abfed38d127ea66e57a2b7b5a9e0"},
662 | {file = "pandas-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:5036d4009012a44aa3e50173e482b664c1fae36decd277c49e453463798eca4e"},
663 | {file = "pandas-1.0.1.tar.gz", hash = "sha256:3c07765308f091d81b6735d4f2242bb43c332cc3461cae60543df6b10967fe27"},
664 | ]
665 | pillow = [
666 | {file = "Pillow-8.1.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:5cf03b9534aca63b192856aa601c68d0764810857786ea5da652581f3a44c2b0"},
667 | {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f91b50ad88048d795c0ad004abbe1390aa1882073b1dca10bfd55d0b8cf18ec5"},
668 | {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5762ebb4436f46b566fc6351d67a9b5386b5e5de4e58fdaa18a1c83e0e20f1a8"},
669 | {file = "Pillow-8.1.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e2cd8ac157c1e5ae88b6dd790648ee5d2777e76f1e5c7d184eaddb2938594f34"},
670 | {file = "Pillow-8.1.2-cp36-cp36m-win32.whl", hash = "sha256:72027ebf682abc9bafd93b43edc44279f641e8996fb2945104471419113cfc71"},
671 | {file = "Pillow-8.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d1d6bca39bb6dd94fba23cdb3eeaea5e30c7717c5343004d900e2a63b132c341"},
672 | {file = "Pillow-8.1.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:90882c6f084ef68b71bba190209a734bf90abb82ab5e8f64444c71d5974008c6"},
673 | {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:89e4c757a91b8c55d97c91fa09c69b3677c227b942fa749e9a66eef602f59c28"},
674 | {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8c4e32218c764bc27fe49b7328195579581aa419920edcc321c4cb877c65258d"},
675 | {file = "Pillow-8.1.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a01da2c266d9868c4f91a9c6faf47a251f23b9a862dce81d2ff583135206f5be"},
676 | {file = "Pillow-8.1.2-cp37-cp37m-win32.whl", hash = "sha256:30d33a1a6400132e6f521640dd3f64578ac9bfb79a619416d7e8802b4ce1dd55"},
677 | {file = "Pillow-8.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:71b01ee69e7df527439d7752a2ce8fb89e19a32df484a308eca3e81f673d3a03"},
678 | {file = "Pillow-8.1.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:5a2d957eb4aba9d48170b8fe6538ec1fbc2119ffe6373782c03d8acad3323f2e"},
679 | {file = "Pillow-8.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:87f42c976f91ca2fc21a3293e25bd3cd895918597db1b95b93cbd949f7d019ce"},
680 | {file = "Pillow-8.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:15306d71a1e96d7e271fd2a0737038b5a92ca2978d2e38b6ced7966583e3d5af"},
681 | {file = "Pillow-8.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:71f31ee4df3d5e0b366dd362007740106d3210fb6a56ec4b581a5324ba254f06"},
682 | {file = "Pillow-8.1.2-cp38-cp38-win32.whl", hash = "sha256:98afcac3205d31ab6a10c5006b0cf040d0026a68ec051edd3517b776c1d78b09"},
683 | {file = "Pillow-8.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:328240f7dddf77783e72d5ed79899a6b48bc6681f8d1f6001f55933cb4905060"},
684 | {file = "Pillow-8.1.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bead24c0ae3f1f6afcb915a057943ccf65fc755d11a1410a909c1fefb6c06ad1"},
685 | {file = "Pillow-8.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81b3716cc9744ffdf76b39afb6247eae754186838cedad0b0ac63b2571253fe6"},
686 | {file = "Pillow-8.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:63cd413ac52ee3f67057223d363f4f82ce966e64906aea046daf46695e3c8238"},
687 | {file = "Pillow-8.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8565355a29655b28fdc2c666fd9a3890fe5edc6639d128814fafecfae2d70910"},
688 | {file = "Pillow-8.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1940fc4d361f9cc7e558d6f56ff38d7351b53052fd7911f4b60cd7bc091ea3b1"},
689 | {file = "Pillow-8.1.2-cp39-cp39-win32.whl", hash = "sha256:46c2bcf8e1e75d154e78417b3e3c64e96def738c2a25435e74909e127a8cba5e"},
690 | {file = "Pillow-8.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:aeab4cd016e11e7aa5cfc49dcff8e51561fa64818a0be86efa82c7038e9369d0"},
691 | {file = "Pillow-8.1.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:74cd9aa648ed6dd25e572453eb09b08817a1e3d9f8d1bd4d8403d99e42ea790b"},
692 | {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:e5739ae63636a52b706a0facec77b2b58e485637e1638202556156e424a02dc2"},
693 | {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:903293320efe2466c1ab3509a33d6b866dc850cfd0c5d9cc92632014cec185fb"},
694 | {file = "Pillow-8.1.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5daba2b40782c1c5157a788ec4454067c6616f5a0c1b70e26ac326a880c2d328"},
695 | {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:1f93f2fe211f1ef75e6f589327f4d4f8545d5c8e826231b042b483d8383e8a7c"},
696 | {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6efac40344d8f668b6c4533ae02a48d52fd852ef0654cc6f19f6ac146399c733"},
697 | {file = "Pillow-8.1.2-pp37-pypy37_pp73-win32.whl", hash = "sha256:f36c3ff63d6fc509ce599a2f5b0d0732189eed653420e7294c039d342c6e204a"},
698 | {file = "Pillow-8.1.2.tar.gz", hash = "sha256:b07c660e014852d98a00a91adfbe25033898a9d90a8f39beb2437d22a203fc44"},
699 | ]
700 | pluggy = [
701 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
702 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
703 | ]
704 | py = [
705 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
706 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
707 | ]
708 | pycparser = [
709 | {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
710 | {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
711 | ]
712 | pyparsing = [
713 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
714 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
715 | ]
716 | pytest = [
717 | {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
718 | {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
719 | ]
720 | pytest-cov = [
721 | {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
722 | {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
723 | ]
724 | python-dateutil = [
725 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
726 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
727 | ]
728 | pytz = [
729 | {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
730 | {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
731 | ]
732 | reportlab = [
733 | {file = "reportlab-3.5.65-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:fd6712a8a6dca12181a3a12316f97810927861e77f2a98029efd2c5cfc8546dc"},
734 | {file = "reportlab-3.5.65-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:ee711804acdaf3ea7f0f2cd27f19478af993e730df8c8d923a678eb0e2572fba"},
735 | {file = "reportlab-3.5.65-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4c42e85851f969e21fa4d6414587b7544e877ce685e2495d7d422589c70b6281"},
736 | {file = "reportlab-3.5.65-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d8fefd07072bfae2715283a821fb1acf8fc4946cf925509d5cc2af791c611809"},
737 | {file = "reportlab-3.5.65-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bdf751289efee4891f4f354ce9122da8de8258a40f328b3f11540c4888363337"},
738 | {file = "reportlab-3.5.65-cp36-cp36m-win32.whl", hash = "sha256:f0634740b099b69caed081acd89692996b5504c59f86f39781b6bebc82b267f5"},
739 | {file = "reportlab-3.5.65-cp36-cp36m-win_amd64.whl", hash = "sha256:d810bffd4bcd50fdcb2bab0d1fe9ea4e6187ed5237687e41c6ade6c884b00c1e"},
740 | {file = "reportlab-3.5.65-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:46745826657d35f86843487f4bc6f6f805f61260428f8ee13642bf6372f9df55"},
741 | {file = "reportlab-3.5.65-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bc62187181582772688d65c557ad6a40a4c3bb8d1f74de463d35ea81983e9b75"},
742 | {file = "reportlab-3.5.65-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:58bec163f727c1c60515fc4704a961b3b4ccf2c76b4e6ec1a457ea7ed0c2d756"},
743 | {file = "reportlab-3.5.65-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d92834993bf998853a04946729266a3276965e7b13f7423212f1c1abdfc4a1c7"},
744 | {file = "reportlab-3.5.65-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9ec95808b742ce70c1dab28b2c5bef9093816b92315b948419c2c6968658f9cc"},
745 | {file = "reportlab-3.5.65-cp37-cp37m-win32.whl", hash = "sha256:b9494986f35d82350b0ce0c29704a49a3945421b789dff92e93fbd3de554fa34"},
746 | {file = "reportlab-3.5.65-cp37-cp37m-win_amd64.whl", hash = "sha256:07f9d9c0360cb8fc780ca05264faa68b90583cd28dbdf2cda6bda34379b6e66c"},
747 | {file = "reportlab-3.5.65-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:81898de0a0be2c8318468ae0ae1590f828805e9b7fd68e5a50667dce8b942171"},
748 | {file = "reportlab-3.5.65-cp38-cp38-manylinux1_i686.whl", hash = "sha256:99aeee49a61c85f1af1087e9e418f3d0c2352c4dd0f0abbfac17ae6c467185aa"},
749 | {file = "reportlab-3.5.65-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3ec70873d99c14570e2a9c44b86c8c01526871e7af5ee4b2855246db15cb0c9f"},
750 | {file = "reportlab-3.5.65-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c12432575c793b8cd8552fddc219bbf2813541c64d02854ae345a108fb875b9d"},
751 | {file = "reportlab-3.5.65-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2cf692ae7af995b499a31a3f58f2001d98e310e03f74812bcb97a08078239c0"},
752 | {file = "reportlab-3.5.65-cp38-cp38-win32.whl", hash = "sha256:f92388e30bf6b5d2eceb3d7b05ee2df856635f74ce7d950a8f45d2b70c685a5b"},
753 | {file = "reportlab-3.5.65-cp38-cp38-win_amd64.whl", hash = "sha256:6f007142f2b166f52cbb3e5d23319e3e496c429831e53b904e6db28c3370f279"},
754 | {file = "reportlab-3.5.65-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:8707cc21a769150154bf4634dca6e9581ae24a05f0fb81a84fcc1143b1cbbfde"},
755 | {file = "reportlab-3.5.65-cp39-cp39-manylinux1_i686.whl", hash = "sha256:27a831da0d17153e33c985bd7a88307e206c5a28778cddb755d5372598d12637"},
756 | {file = "reportlab-3.5.65-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fe5d98cdac07dd702bcd49f5723aacdd0af8c84d70fc82a5cc3781e52aedad52"},
757 | {file = "reportlab-3.5.65-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:ba2d10f368c9ea1e76c84b3bb6b9982eb5a8f243c434e821c505b75ca8d85852"},
758 | {file = "reportlab-3.5.65-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:289539f7888239343ef7ebcd30c55e6204ef78d5f70e1547fdeb854a2da8bfa1"},
759 | {file = "reportlab-3.5.65-cp39-cp39-win32.whl", hash = "sha256:cdf8ff72cd6fa9303744c8409fb81ef7720da2e034c369762c2fdf496462179e"},
760 | {file = "reportlab-3.5.65-cp39-cp39-win_amd64.whl", hash = "sha256:4a784ecdf3008f533e5a032b96c395e8592ed5e679baaf5ef4dcc136b01c72e9"},
761 | {file = "reportlab-3.5.65.tar.gz", hash = "sha256:b2c7eedb4d19db63301c27ad1076086a099fd4c8ca0a6f62f6e9ed749fa5908f"},
762 | ]
763 | requests = [
764 | {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
765 | {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
766 | ]
767 | rpy2 = [
768 | {file = "rpy2-3.0.0.tar.gz", hash = "sha256:34efc2935d9015527837d6b1de29641863d184b19d39ad415d5384be8a015bce"},
769 | ]
770 | simplegeneric = [
771 | {file = "simplegeneric-0.8.1.zip", hash = "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"},
772 | ]
773 | six = [
774 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
775 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
776 | ]
777 | toml = [
778 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
779 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
780 | ]
781 | typing-extensions = [
782 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
783 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
784 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
785 | ]
786 | tzlocal = [
787 | {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"},
788 | {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"},
789 | ]
790 | urllib3 = [
791 | {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
792 | {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
793 | ]
794 | zipp = [
795 | {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"},
796 | {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"},
797 | ]
798 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "diffexpr"
3 | version = "0.1.0"
4 | description = "DESeq2 port in python via rpy2"
5 | authors = ["Douglas Wu "]
6 | license = "MIT"
7 |
8 | [tool.poetry.dependencies]
9 | python = "^3.6.1"
10 | rpy2 = "3.0.0"
11 | pandas = "1.0.1"
12 | tzlocal = "^2.1"
13 | biopython = "^1.78"
14 | reportlab = "^3.5.65"
15 | codecov = "^2.1.11"
16 |
17 | [tool.poetry.dev-dependencies]
18 | pytest = "6.2.2"
19 | pytest-cov = "2.11.1"
20 |
21 | [build-system]
22 | requires = ["poetry-core>=1.0.0"]
23 | build-backend = "poetry.core.masonry.api"
24 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file may be used to create an environment using:
2 | # $ conda create --name --file
3 | # platform: osx-64
4 | appnope=0.1.0=py38h32f6830_1001
5 | backcall=0.1.0=py_0
6 | ca-certificates=2020.4.5.1=hecc5488_0
7 | certifi=2020.4.5.1=py38h32f6830_0
8 | decorator=4.4.2=py_0
9 | entrypoints=0.3=py38h32f6830_1001
10 | ipykernel=5.3.0=py38h23f93f0_0
11 | ipython=7.15.0=py38h32f6830_0
12 | ipython_genutils=0.2.0=py_1
13 | jedi=0.17.0=py38h32f6830_0
14 | jupyter_client=6.1.3=py_0
15 | jupyter_core=4.6.3=py38h32f6830_1
16 | libcxx=10.0.0=h1af66ff_2
17 | libffi=3.2.1=h4a8c4bd_1007
18 | libsodium=1.0.17=h01d97ff_0
19 | ncurses=6.1=h0a44026_1002
20 | openssl=1.1.1g=h0b31af3_0
21 | parso=0.7.0=pyh9f0ad1d_0
22 | pexpect=4.8.0=py38h32f6830_1
23 | pickleshare=0.7.5=py38h32f6830_1001
24 | pip=20.1.1=py_1
25 | prompt-toolkit=3.0.5=py_0
26 | ptyprocess=0.6.0=py_1001
27 | pygments=2.6.1=py_0
28 | python=3.8.2=hd5f0129_7_cpython
29 | python-dateutil=2.8.1=py_0
30 | python_abi=3.8=1_cp38
31 | pyzmq=19.0.1=py38h1fcdcd6_0
32 | readline=8.0=hcfe32e1_0
33 | setuptools=47.1.1=py38h32f6830_0
34 | six=1.15.0=pyh9f0ad1d_0
35 | sqlite=3.30.1=h93121df_0
36 | tk=8.6.10=hbbe82c9_0
37 | tornado=6.0.4=py38h64e0658_1
38 | traitlets=4.3.3=py38h32f6830_1
39 | wcwidth=0.2.2=pyh9f0ad1d_0
40 | wheel=0.34.2=py_1
41 | xz=5.2.5=h0b31af3_0
42 | zeromq=4.3.2=h6de7cb9_2
43 | zlib=1.2.11=h0b31af3_1006
44 |
--------------------------------------------------------------------------------
/setup.R:
--------------------------------------------------------------------------------
1 | install.packages(c('BiocManager','Hmisc', 'RcppEigen','RcppNumerical'),
2 | dependencies='Depends',
3 | repo = "http://cran.us.r-project.org")
4 | BiocManager::install(c('DESeq2','apeglm', 'rhdf5', 'tximport'))
5 |
6 |
7 | library(DESeq2)
8 | library(apeglm)
9 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages, setup, Extension
2 |
3 |
4 | def check_package(package = 'rpy2'):
5 | import importlib
6 | try:
7 | importlib.import_module(package)
8 | except ImportError as e:
9 | raise ImportError("Requires {package} to "
10 | "be installed before running setup.py (pip install {package})"\
11 | .format(package = package))
12 | finally:
13 | pass
14 |
15 | _ = [check_package(p) for p in ['rpy2','numpy','pandas']]
16 |
17 |
18 | #try:
19 | # import rpy2
20 | #except ImportError:
21 | # raise ImportError("Requires rpy2 to "
22 | # "be installed before running setup.py (pip install cython)")
23 | #try:
24 | # import numpy as np
25 | #except ImportError:
26 | # raise ImportError("Requires numpy to "
27 | # "be installed before running setup.py (pip install numpy)")
28 | #try:
29 | # import pandas
30 | #except ImportError:
31 | # raise ImportError("Requires pandas to "
32 | # "be installed before running setup.py (pip install pandas)")
33 |
34 | setup(
35 | name = 'diffexp',
36 | version = '0.1',
37 | description = 'Tools for porting differential expression DESeq to python',
38 | url = '',
39 | author = 'Douglas C. Wu',
40 | author_email = 'wckdouglas@gmail.com',
41 | license = 'MIT',
42 | packages = find_packages(),
43 | zip_safe = False,
44 | install_requires=[
45 | 'pandas',
46 | 'rpy2']
47 | )
48 |
--------------------------------------------------------------------------------
/test/data/ercc.tsv:
--------------------------------------------------------------------------------
1 | id A_1 A_2 A_3 B_1 B_2 B_3
2 | ERCC-00002 111461 106261 107547 333944 199252 186947
3 | ERCC-00003 6735 5387 5265 13937 8584 8596
4 | ERCC-00004 17673 13983 15462 5065 3222 3353
5 | ERCC-00009 4669 4431 4211 6939 4155 3647
6 | ERCC-00012 0 2 0 0 0 0
7 | ERCC-00013 3 0 3 14 6 5
8 | ERCC-00014 36 39 34 93 78 68
9 | ERCC-00016 0 3 1 2 1 1
10 | ERCC-00017 1 0 0 0 0 0
11 | ERCC-00019 115 95 103 28 33 20
12 | ERCC-00022 631 633 712 1933 1208 1180
13 | ERCC-00024 1 0 0 3 1 1
14 | ERCC-00025 635 587 581 1000 632 559
15 | ERCC-00028 18 10 14 10 4 8
16 | ERCC-00031 17 9 13 14 9 5
17 | ERCC-00033 31 31 45 11 4 2
18 | ERCC-00034 73 66 64 128 73 67
19 | ERCC-00035 553 530 450 846 527 475
20 | ERCC-00039 20 18 17 49 10 14
21 | ERCC-00040 1 0 1 12 4 2
22 | ERCC-00041 1 0 1 0 1 0
23 | ERCC-00042 2205 1999 1943 2828 1675 1737
24 | ERCC-00043 4350 4039 3831 9568 6165 5766
25 | ERCC-00044 616 635 575 1462 861 815
26 | ERCC-00046 12373 11902 12537 29554 18817 17744
27 | ERCC-00048 0 0 1 1 1 0
28 | ERCC-00051 68 48 50 56 35 28
29 | ERCC-00053 191 153 155 211 115 116
30 | ERCC-00054 11 15 10 14 23 8
31 | ERCC-00057 6 0 0 2 0 0
32 | ERCC-00058 8 9 13 26 15 9
33 | ERCC-00059 54 31 27 134 91 81
34 | ERCC-00060 1057 949 1064 1350 803 682
35 | ERCC-00061 0 0 0 3 0 0
36 | ERCC-00062 443 369 346 100 70 64
37 | ERCC-00067 10 6 7 11 8 5
38 | ERCC-00069 16 9 12 33 23 16
39 | ERCC-00071 210 170 223 418 262 241
40 | ERCC-00073 8 5 3 6 3 2
41 | ERCC-00074 45268 41189 38646 87012 53603 50788
42 | ERCC-00075 0 0 0 0 0 0
43 | ERCC-00076 543 533 561 1375 818 807
44 | ERCC-00077 9 7 7 30 5 8
45 | ERCC-00078 252 179 169 597 344 339
46 | ERCC-00079 235 201 199 592 403 388
47 | ERCC-00081 0 1 1 0 0 1
48 | ERCC-00083 0 0 0 0 0 0
49 | ERCC-00084 181 197 182 475 264 223
50 | ERCC-00085 30 11 26 19 8 12
51 | ERCC-00086 1 3 0 3 2 1
52 | ERCC-00092 1649 1507 1698 581 358 391
53 | ERCC-00095 368 301 303 113 63 57
54 | ERCC-00096 92836 89607 89178 144482 86830 85932
55 | ERCC-00097 5 3 1 1 2 1
56 | ERCC-00098 0 0 1 8 0 0
57 | ERCC-00099 124 130 107 232 172 123
58 | ERCC-00104 6 1 1 1 1 2
59 | ERCC-00108 4936 4349 4727 1954 1019 1080
60 | ERCC-00109 3 2 1 6 1 0
61 | ERCC-00111 1804 1626 1854 4007 2349 2461
62 | ERCC-00112 711 705 773 2326 1223 1321
63 | ERCC-00113 19212 18555 17141 41027 25217 23896
64 | ERCC-00116 5947 5823 5669 2300 1424 1256
65 | ERCC-00117 0 0 0 2 0 1
66 | ERCC-00120 0 1 0 3 2 1
67 | ERCC-00123 0 0 0 0 0 0
68 | ERCC-00126 41 35 32 49 49 45
69 | ERCC-00130 174360 163701 149265 58753 36667 31609
70 | ERCC-00131 475 422 460 149 118 116
71 | ERCC-00134 4 5 4 1 0 0
72 | ERCC-00136 8585 8136 7795 3065 1826 1601
73 | ERCC-00137 1 0 2 4 3 3
74 | ERCC-00138 1 4 1 0 1 2
75 | ERCC-00142 2 0 0 0 4 0
76 | ERCC-00143 15 20 14 28 24 29
77 | ERCC-00144 79 64 76 38 10 19
78 | ERCC-00145 4210 4116 3844 8384 5364 4846
79 | ERCC-00147 7 2 9 0 1 1
80 | ERCC-00148 45 33 19 53 28 20
81 | ERCC-00150 12 11 15 15 10 17
82 | ERCC-00154 10 21 19 7 9 7
83 | ERCC-00156 1 0 0 0 1 0
84 | ERCC-00157 65 65 59 142 100 88
85 | ERCC-00158 6 2 1 2 6 3
86 | ERCC-00160 68 65 62 210 131 113
87 | ERCC-00162 113 71 116 195 111 143
88 | ERCC-00163 38 56 33 130 65 72
89 | ERCC-00164 2 1 5 5 1 2
90 | ERCC-00165 261 214 240 784 464 427
91 | ERCC-00168 1 3 0 7 4 1
92 | ERCC-00170 133 129 113 41 10 28
93 | ERCC-00171 8438 8347 7363 11736 7372 6009
94 |
--------------------------------------------------------------------------------
/test/data/quant1/abundance.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wckdouglas/diffexpr/c78aba6a8ec7b26331f7e258d5e2cf1c5d559eed/test/data/quant1/abundance.h5
--------------------------------------------------------------------------------
/test/data/quant2/abundance.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wckdouglas/diffexpr/c78aba6a8ec7b26331f7e258d5e2cf1c5d559eed/test/data/quant2/abundance.h5
--------------------------------------------------------------------------------
/test/deseq.R:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env Rscript
2 |
3 | library(DESeq2)
4 |
5 | df <- read.table('data/ercc.tsv', header=T)
6 |
7 | sample_df <- data.frame(samplename = names(df)[2:ncol(df)])
8 | sample_df$sample <- substr(sample_df$samplename, 1,1)
9 | sample_df$batch <- substr(sample_df$samplename, 3,3)
10 | row.names(sample_df) <- sample_df$samplename
11 | sample_df$sample <- factor(sample_df$sample)
12 |
13 |
14 | count_matrix <- subset(df, , -id)
15 | row.names(count_matrix) <- df$id
16 | dds <- DESeqDataSetFromMatrix(countData = count_matrix,
17 | colData = sample_df,
18 | design = ~ batch + sample)
19 | dds <- DESeq(dds)
20 | res <- results(dds)
21 | res$id = row.names(res)
22 | write.table(data.frame(res),
23 | file = 'data/R_deseq.tsv',
24 | sep='\t',
25 | row.names=F,
26 | quote=F)
27 |
28 | dds <- DESeq(dds, test="LRT", reduced=~ batch)
29 | res <- results(dds)
30 | res$id = row.names(res)
31 | write.table(data.frame(res),
32 | file = 'data/R_deseq_reduced.tsv',
33 | sep='\t',
34 | row.names=F,
35 | quote=F)
--------------------------------------------------------------------------------
/test/test_deseq.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import re
4 | import shlex
5 | import subprocess
6 | import warnings
7 |
8 | import numpy as np
9 | import pandas as pd
10 | import pytest
11 |
12 | from diffexpr.py_deseq import py_DESeq2
13 |
14 | warnings.filterwarnings("ignore")
15 | test_data_path = os.path.dirname(os.path.realpath(__file__)) + "/data"
16 |
17 |
18 | @pytest.fixture(scope="module", name="run_r")
19 | def fixture_run_r():
20 | os.chdir(os.path.dirname(test_data_path))
21 | os.system("Rscript deseq.R")
22 |
23 |
24 | def count_matrix():
25 | """
26 | id A_1 A_2 A_3 B_1 B_2 B_3
27 | 0 ERCC-00002 111461 106261 107547 333944 199252 186947
28 | 1 ERCC-00003 6735 5387 5265 13937 8584 8596
29 | 2 ERCC-00004 17673 13983 15462 5065 3222 3353
30 | 3 ERCC-00009 4669 4431 4211 6939 4155 3647
31 | 4 ERCC-00012 0 2 0 0 0 0
32 | """
33 | return pd.read_csv(test_data_path + "/ercc.tsv", sep="\t")
34 |
35 |
36 | def sample_metadata():
37 | df = count_matrix()
38 | sample_df = (
39 | pd.DataFrame({"samplename": df.columns})
40 | .query('samplename != "id"')
41 | .assign(sample=lambda d: d["samplename"].str.extract("([AB])_", expand=False))
42 | .assign(batch=lambda d: d["samplename"].str.extract("_([123])", expand=False))
43 | .pipe(lambda d: d.set_index(d["samplename"]))
44 | # this duplicates the "samplename" column into index, needed for `setup_deseq`
45 | )
46 | return sample_df
47 |
48 |
49 | @pytest.fixture(scope="module")
50 | def setup_deseq():
51 | df = count_matrix()
52 | sample_df = sample_metadata()
53 |
54 | dds = py_DESeq2(
55 | count_matrix=df,
56 | design_matrix=sample_df,
57 | design_formula="~ batch + sample",
58 | gene_column="id",
59 | threads=2,
60 | )
61 |
62 | return df, dds
63 |
64 |
65 | @pytest.mark.parametrize("matrix", [("count"), ("metadata")])
66 | def test_deseq_not_dataframe_exception(matrix):
67 | df = pd.read_csv(test_data_path + "/ercc.tsv", sep="\t")
68 | sample_df = sample_metadata()
69 | if matrix == "count":
70 | df = df.values
71 | elif matrix == "metadata":
72 | sample_df = sample_df.values
73 |
74 | with pytest.raises(ValueError, match="should be pd.DataFrame type"):
75 | py_DESeq2(
76 | count_matrix=df,
77 | design_matrix=sample_df,
78 | design_formula="~ batch + sample",
79 | gene_column="id",
80 | )
81 |
82 |
83 | def test_deseq_no_id_exception():
84 | df = pd.read_csv(test_data_path + "/ercc.tsv", sep="\t")
85 | sample_df = sample_metadata()
86 | with pytest.raises(ValueError, match="The given gene_column name is not a column"):
87 | py_DESeq2(
88 | count_matrix=df,
89 | design_matrix=sample_df,
90 | design_formula="~ batch + sample",
91 | gene_column="id1",
92 | )
93 |
94 |
95 | def test_deseq(setup_deseq):
96 | df, dds = setup_deseq
97 | dds.run_deseq()
98 | dds.get_deseq_result()
99 | res = dds.deseq_result
100 | assert res.query("padj < 0.05").shape == (35, 7)
101 |
102 | dds.get_deseq_result(contrast=["sample", "B", "A"])
103 | res = dds.deseq_result
104 | assert res.query("padj < 0.05").shape == (35, 7)
105 |
106 | lfc_res = dds.lfcShrink(coef=4, method="apeglm")
107 | assert lfc_res[lfc_res.padj < 0.05].shape[0] == 35
108 |
109 | res.to_csv(test_data_path + "/py_deseq.tsv", index=False, sep="\t")
110 |
111 | dds.run_deseq(test="LRT", reduced="~ batch")
112 | dds.get_deseq_result()
113 | res = dds.deseq_result
114 | res.to_csv(test_data_path + "/py_deseq_reduced.tsv", index=False, sep="\t")
115 |
116 |
117 | def test_normalized_count(setup_deseq):
118 | df, dds = setup_deseq
119 | norm_count_df = dds.normalized_count()
120 | assert norm_count_df.shape == df.shape
121 |
122 |
123 | @pytest.mark.parametrize(
124 | "blind,fit_type",
125 | [(True, "parametric"), (True, "local"), (True, "mean"), (False, "parametric"), (False, "local"), (False, "mean")],
126 | )
127 | def test_vst(setup_deseq, blind, fit_type):
128 | df, dds = setup_deseq
129 | vst = dds.vst(blind=blind, fit_type=fit_type)
130 | assert vst.shape == df.shape
131 |
132 |
133 | @pytest.mark.parametrize(
134 | "blind,fit_type",
135 | [(True, "parametric"), (True, "local"), (True, "mean"), (False, "parametric"), (False, "local"), (False, "mean")],
136 | )
137 | def test_rlog(setup_deseq, blind, fit_type):
138 | df, dds = setup_deseq
139 | rlog = dds.rlog(blind=blind, fit_type=fit_type)
140 | assert rlog.shape == df.shape
141 |
142 |
143 | @pytest.mark.parametrize(
144 | "case,r_table,py_table",
145 | [
146 | ("deseq", "R_deseq.tsv", "py_deseq.tsv"),
147 | ("deseq reduced", "R_deseq_reduced.tsv", "py_deseq_reduced.tsv"),
148 | ],
149 | )
150 | def test_result(run_r, case, r_table, py_table):
151 | os.chdir(os.path.dirname(test_data_path))
152 |
153 | py_tab = pd.read_table(os.path.join(test_data_path, py_table))
154 | r_tab = pd.read_table(os.path.join(test_data_path, r_table))
155 |
156 | for col in py_tab.columns:
157 | if py_tab.columns.dtype == "float64":
158 | assert np.all(np.isclose(py_tab[col].fillna(0), r_tab[col].fillna(0))), f"{case} failed"
159 |
160 |
161 | def test_deseq2_version(setup_deseq):
162 | _, dds = setup_deseq
163 |
164 | # extract R deseq2 version
165 | re_ver = re.compile("\d+.\d+.\d+")
166 | cmd = """
167 | Rscript -e "packageVersion('DESeq2')"
168 | """
169 | version_output = subprocess.check_output(shlex.split(cmd)).decode()
170 | version = re_ver.findall(version_output)[0]
171 |
172 | # let's see if we extracted the correct version
173 | assert dds.deseq2_version == version
174 |
175 |
176 | def test_kallisto():
177 |
178 | import pandas as pd
179 |
180 | from diffexpr import py_deseq
181 |
182 | h5_list = {
183 | "quant1": f"{test_data_path}/quant1/abundance.h5",
184 | "quant2": f"{test_data_path}/quant2/abundance.h5",
185 | }
186 | sample_df = pd.DataFrame(
187 | {
188 | "sample": ["quant1", "quant2"],
189 | "condition": ["1", "2"],
190 | }
191 | ).set_index("sample")
192 | design = "~ condition"
193 |
194 | transcripts = """
195 | ENST00000513300.5
196 | ENST00000282507.7
197 | ENST00000504685.5
198 | ENST00000243108.4
199 | ENST00000303450.4
200 | ENST00000243082.4
201 | ENST00000303406.4
202 | ENST00000303460.4
203 | ENST00000243056.4
204 | ENST00000312492.2
205 | ENST00000040584.5
206 | ENST00000430889.2
207 | ENST00000394331.3
208 | ENST00000243103.3
209 | """
210 | tx = transcripts.strip().split("\n")
211 | gene = list(map(lambda x: x.split(".")[-1], tx))
212 |
213 | tx2gene = pd.DataFrame({"TXNAME": tx, "GENEID": gene})
214 |
215 | dds = py_deseq.py_DESeq2(
216 | count_matrix=h5_list, design_matrix=sample_df, design_formula=design, tx2gene=tx2gene, kallisto=True
217 | )
218 |
--------------------------------------------------------------------------------
/test/test_pathways.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from Bio.KEGG.KGML.KGML_pathway import Pathway
4 |
5 | from diffexpr.py_pathway import pathwayview
6 |
7 |
8 | def test_plotpathway():
9 | enriched_genes = "GADD45A,PLK1,TTK,CDC6,CDC25C,CDC25A".split(",")
10 | pv = pathwayview()
11 | pathway = pv.plot_pathway(enriched_genes=enriched_genes, pathway_id="hsa04110", figurename="pathway.pdf")
12 | assert os.path.isfile("pathway.pdf")
13 | assert isinstance(pathway, Pathway)
14 |
--------------------------------------------------------------------------------