flippr 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flippr/__about__.py +1 -0
- flippr/__init__.py +172 -0
- flippr/combine.py +107 -0
- flippr/functions.py +289 -0
- flippr/graph/__init__.py +0 -0
- flippr/parameters.py +154 -0
- flippr/py.typed +0 -0
- flippr/spr.py +237 -0
- flippr/uniprot/__init__.py +0 -0
- flippr/uniprot/fasta.py +100 -0
- flippr/uniprot/metadata.py +45 -0
- flippr/validate.py +109 -0
- flippr-0.1.1.dist-info/METADATA +168 -0
- flippr-0.1.1.dist-info/RECORD +16 -0
- flippr-0.1.1.dist-info/WHEEL +4 -0
- flippr-0.1.1.dist-info/licenses/LICENSE +0 -0
flippr/__about__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.1"
|
flippr/__init__.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import polars as pl
|
|
5
|
+
|
|
6
|
+
from . import spr as _spr
|
|
7
|
+
from . import validate as _validate
|
|
8
|
+
from .parameters import rcParams
|
|
9
|
+
from .uniprot import fasta as _fasta
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Study:
|
|
13
|
+
"""
|
|
14
|
+
A FLiPPR Study is used to coordinate Limited Proteolysis (LiP) data processing for a single experiment analyzed with FragPipe.
|
|
15
|
+
Studies can contain LiP data alone or LiP and TrP data; with the inclusion of a FASTA file for automatic metadata generation.
|
|
16
|
+
TrP data should result from the same experimental conditions as the LiP data, but without including a broad-spectrum protease like Proteinase K (PK).
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
lip (str | Path): Directory or path to the FragPipe LFQ output data for Limited Proteolysis (LiP) experiment data.
|
|
20
|
+
trp (str | Path, optional): Directory or path to the FragPipe LFQ output data for Trypsin Only (TrP) experiment data. Defaults to None.
|
|
21
|
+
fasta (str | Path, optional): Path to the FASTA file containing the same protein sequences used in the LiP/TrP experiments. Defaults to None.
|
|
22
|
+
method (str): Method used to quantify ion intensities in FragPipe. `lfq` - Label-Free Quantification, `silac` - Stable Isotope Labelling (currently not incorporated). Defaults to `lfq`.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
Analysis of a single dataset
|
|
26
|
+
>>> flippr.Study(lip = "PXD025926/FragPipe/LiP_LFQ")
|
|
27
|
+
|
|
28
|
+
Analysis of a single dataset with metadata integration
|
|
29
|
+
>>> flippr.Study(lip = "PXD025926/FragPipe/LiP_LFQ", fasta = "UP000000625.fasta")
|
|
30
|
+
|
|
31
|
+
Including protein-level normalization from trypsin-only data
|
|
32
|
+
>>> flippr.Study(lip = "PXD025926/FragPipe/LiP_LFQ", trp = "PXD025926/FragPipe/TrP_LFQ")
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, lip: str | Path, trp: str | Path | None = None, fasta: str | Path | None = None, method: str = "lfq") -> None:
|
|
37
|
+
|
|
38
|
+
_lip, _trp, _fasta_path, _method = _validate._validate_study(lip, trp, fasta, method)
|
|
39
|
+
|
|
40
|
+
self.lip: Path = _lip
|
|
41
|
+
self.trp: Path | None = _trp
|
|
42
|
+
self._fasta_path: Path | None = _fasta_path
|
|
43
|
+
self.method: str = _method
|
|
44
|
+
|
|
45
|
+
self._pid: int = 0
|
|
46
|
+
self.processes: dict = dict()
|
|
47
|
+
self.results: dict = dict()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def samples(self) -> dict:
|
|
51
|
+
"""
|
|
52
|
+
Returns the names of the samples or experimental annotations found in the FragPipe outputs for LiP and TrP (if include) datasets.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
Including only LiP data
|
|
56
|
+
>>> e_coli_refolding = flippr.Study(lip = "PXD025926/FragPipe/LiP_LFQ")
|
|
57
|
+
>>> print(e_coli_refolding.samples)
|
|
58
|
+
{"LiP": ("Native", "Refolded_01_min", "Refolded_05_min", "Refolded_30_min")}
|
|
59
|
+
|
|
60
|
+
Including LiP and TrP data
|
|
61
|
+
>>> e_coli_refolding = flippr.Study(lip = "PXD025926/FragPipe/LiP_LFQ", trp = "PXD025926/FragPipe/TrP_LFQ")
|
|
62
|
+
>>> print(e_coli_refolding.samples)
|
|
63
|
+
{"LiP": ("Native", "Refolded_01_min", "Refolded_05_min", "Refolded_30_min"),
|
|
64
|
+
"TrP": ("Native", "Refolded")}
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
if self.trp is not None:
|
|
69
|
+
return {"LiP": self.lip_samples, "TrP": self.trp_samples}
|
|
70
|
+
|
|
71
|
+
return {"LiP": self.lip_samples}
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def lip_samples(self) -> set:
|
|
75
|
+
"""
|
|
76
|
+
Returns the names of the samples or experimental annotations found in the FragPipe outputs for LiP datasets.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
return self.__read_annotation(self.lip)
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def trp_samples(self) -> set:
|
|
84
|
+
"""
|
|
85
|
+
Returns the names of the samples or experimental annotations found in the FragPipe outputs for TrP datasets.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
return self.__read_annotation(self.trp)
|
|
90
|
+
|
|
91
|
+
@cached_property
|
|
92
|
+
def fasta(self) -> pl.DataFrame | None:
|
|
93
|
+
"""
|
|
94
|
+
Returns a three column dataframe containing Protein IDs, Sequences, and Valid ID if the Protein ID is interpretable as an official Uniprot ID.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
if self._fasta_path is not None:
|
|
98
|
+
return _fasta._read_fasta(self._fasta_path)
|
|
99
|
+
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def add_process(
|
|
103
|
+
self, pid: object | None, lip_ctrl: str, lip_test: str, n_rep: int, trp_ctrl: str | None = None, trp_test: str | None = None, trp_n_rep: int | None = None) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Adding a process to calculate the fold-change between test and control conditions.
|
|
106
|
+
Normalization is optional and must include a TrP experiment in the `Study` set-up.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
pid (Any): Process ID used to index the `Study().results` dictionary after running `Study().run()`.
|
|
110
|
+
lip_ctrl (str): Control condition sample name from the LiP experiment.
|
|
111
|
+
lip_test (str): Test condition sample name from the LiP experiment.
|
|
112
|
+
n_rep (int): Number of replicates in the LiP experiment.
|
|
113
|
+
trp_ctrl (str, optional): Control condition sample name from the TrP experiment.
|
|
114
|
+
trp_test (str, optional): Test condition sample name from the TrP experiment.
|
|
115
|
+
trp_n_rep (int, optional): Number of replicates in the TrP experiment.
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
Simple fold-change calculation between two conditions
|
|
119
|
+
>>> study.add_process("sample", "WT", "Drug", 3)
|
|
120
|
+
|
|
121
|
+
Fold-change calculation with normalization
|
|
122
|
+
>>> study.add_process("sample", "WT", "Drug", 3, "WT_TrP", "DMSO_TrP", 3)
|
|
123
|
+
|
|
124
|
+
Add multiple processes to the same study
|
|
125
|
+
>>> study.add_process("cond_1", "WT", "Cond1", 5)
|
|
126
|
+
>>> study.add_process("cond_2", "WT", "Cond2", 5, "WT_TrP", "DMSO_TrP", 5)
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
if pid is None:
|
|
130
|
+
pid = self._pid
|
|
131
|
+
self._pid += 1
|
|
132
|
+
|
|
133
|
+
self.processes.update(
|
|
134
|
+
{
|
|
135
|
+
pid: _spr.Process(
|
|
136
|
+
self.fasta,
|
|
137
|
+
self.lip,
|
|
138
|
+
self.trp,
|
|
139
|
+
pid,
|
|
140
|
+
lip_ctrl,
|
|
141
|
+
lip_test,
|
|
142
|
+
n_rep,
|
|
143
|
+
trp_ctrl,
|
|
144
|
+
trp_test,
|
|
145
|
+
trp_n_rep,
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def run(self) -> dict[object: _spr.Result]:
|
|
151
|
+
"""
|
|
152
|
+
Run the processes added to the study.
|
|
153
|
+
Study parameters can be changed by editing the `flippr.rcParams` dictionary.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
self.results = {pid: proc.run(rcParams) for pid, proc in self.processes.items()}
|
|
157
|
+
|
|
158
|
+
return self.results
|
|
159
|
+
|
|
160
|
+
def __read_annotation(self, liptrp: Path) -> set:
|
|
161
|
+
def __strip_fragpipe_repilicate(sample: str) -> str:
|
|
162
|
+
return "_".join([_ for _ in sample.split("_")[:-1]])
|
|
163
|
+
|
|
164
|
+
annotation = liptrp.joinpath("experiment_annotation.tsv")
|
|
165
|
+
|
|
166
|
+
annotation_df = pl.read_csv(annotation, separator="\t")
|
|
167
|
+
|
|
168
|
+
annotation_dict = annotation_df.select(pl.col("sample")).to_dict(
|
|
169
|
+
as_series=False
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return set(__strip_fragpipe_repilicate(sample) for sample in annotation_dict["sample"])
|
flippr/combine.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import polars as pl
|
|
3
|
+
import scipy as sp
|
|
4
|
+
|
|
5
|
+
from .functions import _log2, _log10
|
|
6
|
+
from .parameters import _FLIPPR_COMBINE_KEY
|
|
7
|
+
|
|
8
|
+
COMB_NAME_COLUMN = {
|
|
9
|
+
"CUT SITE": "Cut Site ID",
|
|
10
|
+
"PEPTIDE": "Peptide Sequence",
|
|
11
|
+
"MODIFIED PEPTIDE": "Modified Sequence",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
def combine_by(df: pl.DataFrame, by: str, fc: str) -> pl.DataFrame:
|
|
15
|
+
|
|
16
|
+
combined = (
|
|
17
|
+
df.group_by(by=["Protein ID", COMB_NAME_COLUMN[by]], maintain_order=True)
|
|
18
|
+
.agg(
|
|
19
|
+
pl.col(_FLIPPR_COMBINE_KEY[by]).first(),
|
|
20
|
+
pl.col(["P-value", "Adj. P-value", "CV", fc])
|
|
21
|
+
.filter(
|
|
22
|
+
pl.col("T-test").sign() == pl.col("T-test").sign().sum().sign()
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
.with_columns(
|
|
26
|
+
pl.when(pl.col("P-value").list.len() > 0)
|
|
27
|
+
.then(pl.col("P-value"))
|
|
28
|
+
.otherwise([1.0])
|
|
29
|
+
.alias("P-value"),
|
|
30
|
+
pl.when(pl.col("Adj. P-value").list.len() > 0)
|
|
31
|
+
.then(pl.col("Adj. P-value"))
|
|
32
|
+
.otherwise([1.0])
|
|
33
|
+
.alias("Adj. P-value"),
|
|
34
|
+
pl.when(pl.col("CV").list.len() > 0)
|
|
35
|
+
.then(pl.col("CV"))
|
|
36
|
+
.otherwise([0.0])
|
|
37
|
+
.alias("CV"),
|
|
38
|
+
pl.when(pl.col(fc).list.len() > 0)
|
|
39
|
+
.then(pl.col(fc))
|
|
40
|
+
.otherwise([0.0])
|
|
41
|
+
.alias(fc)
|
|
42
|
+
)
|
|
43
|
+
.with_columns(
|
|
44
|
+
pl.col("P-value")
|
|
45
|
+
.map_elements(lambda x: sp.stats.combine_pvalues(x)[1], return_dtype=pl.Float64)
|
|
46
|
+
.alias("P-value"),
|
|
47
|
+
pl.col("Adj. P-value")
|
|
48
|
+
.map_elements(lambda x: sp.stats.combine_pvalues(x)[1], return_dtype=pl.Float64)
|
|
49
|
+
.alias("Adj. P-value"),
|
|
50
|
+
pl.col("CV")
|
|
51
|
+
.map_elements(lambda x: np.nanmax(x), return_dtype=pl.Float64)
|
|
52
|
+
.alias("CV"),
|
|
53
|
+
pl.col(fc)
|
|
54
|
+
.map_elements(lambda x: np.nanmedian(x), return_dtype=pl.Float64)
|
|
55
|
+
.alias(fc)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
combined = _log2(combined, fc)
|
|
60
|
+
combined = _log10(combined, "P-value")
|
|
61
|
+
combined = _log10(combined, "Adj. P-value")
|
|
62
|
+
|
|
63
|
+
return combined
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def summary_by(df: pl.DataFrame, by: str, fc: str) -> pl.DataFrame:
|
|
67
|
+
val = (
|
|
68
|
+
df.group_by(by="Protein ID", maintain_order=True)
|
|
69
|
+
.agg(pl.col("CV").filter(pl.col("-Log10 P-value") > 0).len())
|
|
70
|
+
.rename({"CV": f"No. of Valid {by}"})
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
sig = (
|
|
74
|
+
df.group_by(by="Protein ID", maintain_order=True)
|
|
75
|
+
.agg(
|
|
76
|
+
pl.col("CV")
|
|
77
|
+
.filter(
|
|
78
|
+
(
|
|
79
|
+
(pl.col(f"Log2 {fc}").abs() >= 1)
|
|
80
|
+
& (pl.col("-Log10 P-value") >= -np.log10(0.01))
|
|
81
|
+
)
|
|
82
|
+
| (
|
|
83
|
+
(pl.col(f"Log2 {fc}").abs() >= 6)
|
|
84
|
+
& (pl.col("-Log10 P-value") >= 1.8)
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
.len()
|
|
88
|
+
)
|
|
89
|
+
.rename({"CV": f"No. of Significant {by} (P-value)"})
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
sigsig = (
|
|
93
|
+
df.group_by(by="Protein ID", maintain_order=True)
|
|
94
|
+
.agg(
|
|
95
|
+
pl.col("CV")
|
|
96
|
+
.filter(
|
|
97
|
+
|
|
98
|
+
(pl.col(f"Log2 {fc}").abs() >= 1)
|
|
99
|
+
& (pl.col("-Log10 Adj. P-value") >= -np.log10(0.05))
|
|
100
|
+
|
|
101
|
+
)
|
|
102
|
+
.len()
|
|
103
|
+
)
|
|
104
|
+
.rename({"CV": f"No. of Significant {by} (Adj. P-value)"})
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return val.join(sig, on="Protein ID").join(sigsig, on="Protein ID")
|
flippr/functions.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import polars as pl
|
|
3
|
+
import scipy as sp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _cull_intensities(df: pl.DataFrame,
|
|
7
|
+
ctrl_name: str, test_name: str,
|
|
8
|
+
ctrl_ints: list[str], test_ints: list[str],
|
|
9
|
+
n_rep: int,
|
|
10
|
+
rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
11
|
+
|
|
12
|
+
max_missing = rcParams.get("ion.missing_intensity_thresh", 1)
|
|
13
|
+
|
|
14
|
+
df = \
|
|
15
|
+
df.with_columns(# Count the number of zeros intensity replicates
|
|
16
|
+
pl.concat_list(ctrl_ints).list.count_matches(0).alias(f"{ctrl_name} ZC"),
|
|
17
|
+
pl.concat_list(test_ints).list.count_matches(0).alias(f"{test_name} ZC"),
|
|
18
|
+
).filter(# Cull based on zero count (ZC)
|
|
19
|
+
(pl.col(f"{ctrl_name} ZC").le(max_missing) & pl.col(f"{test_name} ZC").eq(0))
|
|
20
|
+
| (pl.col(f"{ctrl_name} ZC").eq(0) & pl.col(f"{test_name} ZC").le(max_missing))
|
|
21
|
+
| (pl.col(f"{ctrl_name} ZC").eq(n_rep) & pl.col(f"{test_name} ZC").eq(0))
|
|
22
|
+
| (pl.col(f"{ctrl_name} ZC").eq(0) & pl.col(f"{test_name} ZC").eq(n_rep))
|
|
23
|
+
).with_columns(# Replace zero values remaining with NaN
|
|
24
|
+
pl.col(ctrl_ints).replace(0.0, float("nan")),
|
|
25
|
+
pl.col(test_ints).replace(0.0, float("nan"))
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
return df
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _add_alt_hypothesis(df: pl.DataFrame,
|
|
32
|
+
ctrl_name: str, test_name: str,
|
|
33
|
+
n_rep: int,
|
|
34
|
+
rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
35
|
+
|
|
36
|
+
df = \
|
|
37
|
+
df.with_columns(# Add alternative hypothesis
|
|
38
|
+
pl.when(pl.col(f"{ctrl_name} ZC").eq(n_rep) & pl.col(f"{test_name} ZC").eq(0))
|
|
39
|
+
.then(pl.lit("greater"))
|
|
40
|
+
.when(pl.col(f"{ctrl_name} ZC").eq(0) & pl.col(f"{test_name} ZC").eq(n_rep))
|
|
41
|
+
.then(pl.lit("less"))
|
|
42
|
+
.otherwise(pl.lit("two-sided"))
|
|
43
|
+
.alias("Alternative Hypothesis")
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return df
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _impute_aon_intensities(df: pl.DataFrame,
|
|
50
|
+
ctrl_name: str, test_name: str,
|
|
51
|
+
ctrl_ints: list[str], test_ints: list[str],
|
|
52
|
+
n_rep: int,
|
|
53
|
+
rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Add imputation variables
|
|
57
|
+
loc = rcParams.get("ion.aon_impute_loc", 1e4)
|
|
58
|
+
scale = rcParams.get("ion.aon_impute_scale", 1e4)
|
|
59
|
+
imp_df = pl.from_numpy(np.random.normal(loc=loc, scale=scale, size=(df.height, n_rep)), schema=["IMP"])
|
|
60
|
+
df = df.hstack(imp_df)
|
|
61
|
+
|
|
62
|
+
df = \
|
|
63
|
+
df.with_columns(# Impute only on AON ions in control conditions
|
|
64
|
+
pl.when(pl.col(f"{ctrl_name} ZC").eq(n_rep) & pl.col(f"{test_name} ZC").eq(0))
|
|
65
|
+
.then(pl.concat_list("IMP"))
|
|
66
|
+
.otherwise(pl.concat_list(ctrl_ints))
|
|
67
|
+
.alias(f"{ctrl_name} Intensity"),
|
|
68
|
+
# Impute only on AON ions in test conditions
|
|
69
|
+
pl.when(pl.col(f"{ctrl_name} ZC").eq(0) & pl.col(f"{test_name} ZC").eq(n_rep))
|
|
70
|
+
.then(pl.concat_list("IMP"))
|
|
71
|
+
.otherwise(pl.concat_list(test_ints))
|
|
72
|
+
.alias(f"{test_name} Intensity")
|
|
73
|
+
).with_columns(# Seperate out imputation (if perfromed) vars for control conditions
|
|
74
|
+
pl.col(f"{ctrl_name} Intensity").list.get(i).alias(col) for i, col in enumerate(ctrl_ints)
|
|
75
|
+
).with_columns(# Seperate out imputation (if perfromed) vars for test conditions
|
|
76
|
+
pl.col(f"{test_name} Intensity").list.get(i).alias(col) for i, col in enumerate(test_ints)
|
|
77
|
+
).with_columns(# Drop NaNs for further computations, this column will be droped down-stream
|
|
78
|
+
pl.col(f"{ctrl_name} Intensity").list.eval(pl.element().drop_nans()).alias(f"{ctrl_name} Intensity"),
|
|
79
|
+
pl.col(f"{test_name} Intensity").list.eval(pl.element().drop_nans()).alias(f"{test_name} Intensity")
|
|
80
|
+
).drop(["IMP", f"{ctrl_name} ZC", f"{test_name} ZC"])
|
|
81
|
+
|
|
82
|
+
return df
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _add_ttest(df: pl.DataFrame,
|
|
86
|
+
ctrl_name: str, test_name: str,
|
|
87
|
+
n_rep: int,
|
|
88
|
+
rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
89
|
+
|
|
90
|
+
df = \
|
|
91
|
+
df.with_columns(# Prepare the descriptive stats for `.ttest_ind_from_stats()`
|
|
92
|
+
pl.col(f"{ctrl_name} Intensity").list.mean().alias(f"{ctrl_name} Mean"),
|
|
93
|
+
pl.col(f"{ctrl_name} Intensity").list.std().alias(f"{ctrl_name} Std"),
|
|
94
|
+
|
|
95
|
+
pl.col(f"{test_name} Intensity").list.mean().alias(f"{test_name} Mean"),
|
|
96
|
+
pl.col(f"{test_name} Intensity").list.std().alias(f"{test_name} Std"),
|
|
97
|
+
).with_columns(
|
|
98
|
+
pl.struct(# Create struct with `.ttest_ind_from_stats()` var names
|
|
99
|
+
mean1=pl.col(f"{ctrl_name} Mean"),
|
|
100
|
+
std1=pl.col(f"{ctrl_name} Std"),
|
|
101
|
+
nobs1=n_rep,
|
|
102
|
+
|
|
103
|
+
mean2=pl.col(f"{test_name} Mean"),
|
|
104
|
+
std2=pl.col(f"{test_name} Std"),
|
|
105
|
+
nobs2=n_rep,
|
|
106
|
+
alternative=pl.col("Alternative Hypothesis")
|
|
107
|
+
).map_elements(# Perform T-test, slow step
|
|
108
|
+
lambda x: sp.stats.ttest_ind_from_stats(**x, equal_var=False),
|
|
109
|
+
return_dtype=pl.List(pl.Float64)
|
|
110
|
+
).alias("Stats")
|
|
111
|
+
).with_columns(# Seperate out T-test vars
|
|
112
|
+
pl.col("Stats").list.first().alias("T-test"),
|
|
113
|
+
pl.col("Stats").list.last().alias("P-value"),
|
|
114
|
+
).drop(["Stats", f"{ctrl_name} Intensity", f"{test_name} Intensity"])
|
|
115
|
+
|
|
116
|
+
return df
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _add_fdr(df: pl.DataFrame, rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
120
|
+
|
|
121
|
+
# Sort on P-value
|
|
122
|
+
df = df.sort(by=["Protein ID", "P-value"], descending=[False, False])
|
|
123
|
+
|
|
124
|
+
df = \
|
|
125
|
+
df.hstack(
|
|
126
|
+
df.group_by(by="Protein ID", maintain_order=True)
|
|
127
|
+
.agg(# Calculate Adj. P-value
|
|
128
|
+
pl.map_groups(
|
|
129
|
+
exprs="P-value",
|
|
130
|
+
function=lambda x: sp.stats.false_discovery_control(x[0]),
|
|
131
|
+
return_dtype=pl.List(pl.Float64)
|
|
132
|
+
).alias("Adj. P-value"),
|
|
133
|
+
)# Add Adj. P-value to the original `DataFrame`
|
|
134
|
+
.explode(pl.col("Adj. P-value"))
|
|
135
|
+
.select(pl.col("Adj. P-value"))
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return df
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _add_ratio(df: pl.DataFrame,
|
|
142
|
+
ctrl_name: str, test_name: str,
|
|
143
|
+
rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
144
|
+
|
|
145
|
+
df = \
|
|
146
|
+
df.with_columns(# Calculate FC and CV
|
|
147
|
+
(pl.col(f"{test_name} Mean").mean() / pl.col(f"{ctrl_name} Mean")).alias("FC"),
|
|
148
|
+
(pl.col(f"{test_name} Std").mean() / pl.col(f"{test_name} Mean")).alias("CV")
|
|
149
|
+
).drop([f"{ctrl_name} Mean", f"{ctrl_name} Std", f"{test_name} Mean", f"{test_name} Std"])
|
|
150
|
+
|
|
151
|
+
return df
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _normalize_ratios(df: pl.DataFrame, trp_norm: pl.DataFrame, rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
155
|
+
|
|
156
|
+
trp_prot_fc = rcParams.get("trp_protein.fc_sig_tresh", 1.0)
|
|
157
|
+
trp_prot_pval = rcParams.get("trp_protein.pval_sig_tresh", 0.01)
|
|
158
|
+
|
|
159
|
+
trp_norm = trp_norm.select(pl.col("Protein ID", "P-value", "FC"))
|
|
160
|
+
trp_norm = trp_norm.select(# Select only the significantly different proteins
|
|
161
|
+
pl.col("Protein ID"),
|
|
162
|
+
pl.when(pl.col("P-value").le(trp_prot_pval) & pl.col("FC").log(base=2).abs().ge(trp_prot_fc))
|
|
163
|
+
.then(pl.col("FC"))
|
|
164
|
+
.otherwise(pl.lit(0.0))
|
|
165
|
+
.alias("Normalization Factor")
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
df = df.join(trp_norm, on="Protein ID", how="left")
|
|
169
|
+
|
|
170
|
+
df = df.with_columns(# Apply normalization
|
|
171
|
+
pl.col("Normalization Factor").fill_null(strategy="zero"),
|
|
172
|
+
pl.when(pl.col("Normalization Factor").gt(0))
|
|
173
|
+
.then(pl.col("FC") / pl.col("Normalization Factor"))
|
|
174
|
+
.otherwise(pl.col("FC"))
|
|
175
|
+
.alias("Normalized FC")
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return df
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _add_start_end_aa(df: pl.DataFrame, rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
182
|
+
|
|
183
|
+
df = \
|
|
184
|
+
df.with_columns(# Parse the Starting and Ending AA of the peptide
|
|
185
|
+
pl.col("Peptide Sequence").str.head(1).alias("Start AA"),
|
|
186
|
+
pl.col("Peptide Sequence").str.tail(1).alias("End AA"),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return df
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _add_half_trpytic(df: pl.DataFrame, rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
193
|
+
|
|
194
|
+
df = \
|
|
195
|
+
df.with_columns(
|
|
196
|
+
pl.when(# [K/R/-].~~~~~(K/R).[X]
|
|
197
|
+
pl.col("Prev AA").is_in(["K", "R", "-"])
|
|
198
|
+
& pl.col("End AA").is_in(["K", "R"])
|
|
199
|
+
& ~pl.col("Next AA").is_in(["-"]))
|
|
200
|
+
.then(pl.lit("FULL_TRP"))
|
|
201
|
+
.when(# [K/R/-].~~~~~(X).[-]
|
|
202
|
+
pl.col("Prev AA").is_in(["K", "R"])
|
|
203
|
+
& pl.col("Next AA").is_in(["-"]))
|
|
204
|
+
.then(pl.lit("FULL_TRP"))
|
|
205
|
+
.when(# [M].2~~~~~(K/R).[X]
|
|
206
|
+
pl.col("Prev AA").is_in(["M"])
|
|
207
|
+
& pl.col("Start").eq(2)
|
|
208
|
+
& pl.col("End AA").is_in(["K", "R"]))
|
|
209
|
+
.then(pl.lit("FULL_TRP"))
|
|
210
|
+
.when(# [K/R/-].~~~~~(X).[X]
|
|
211
|
+
pl.col("Prev AA").is_in(["K", "R", "-"])
|
|
212
|
+
& ~pl.col("End AA").is_in(["K", "R"])
|
|
213
|
+
& ~pl.col("Next AA").is_in(["-"]))
|
|
214
|
+
.then(pl.lit("C_SEMI"))
|
|
215
|
+
.when(# [M].2~~~~~(X).[X]
|
|
216
|
+
pl.col("Prev AA").is_in(["M"])
|
|
217
|
+
& pl.col("Start").eq(2)
|
|
218
|
+
& ~pl.col("End AA").is_in(["K", "R"]))
|
|
219
|
+
.then(pl.lit("C_SEMI"))
|
|
220
|
+
.when(# [X].~~~~~(K/R).[X]
|
|
221
|
+
~pl.col("Prev AA").is_in(["K", "R", "-"])
|
|
222
|
+
& pl.col("End AA").is_in(["K", "R"])
|
|
223
|
+
& ~pl.col("Next AA").is_in(["-"]))
|
|
224
|
+
.then(pl.lit("N_SEMI"))
|
|
225
|
+
.when(# [X].~~~~~(X).[-]
|
|
226
|
+
~pl.col("Prev AA").is_in(["K", "R", "-"])
|
|
227
|
+
& pl.col("Next AA").is_in(["-"]))
|
|
228
|
+
.then(pl.lit("N_SEMI"))
|
|
229
|
+
.otherwise(pl.lit(None))
|
|
230
|
+
.alias("Cleavage Type")
|
|
231
|
+
).filter(# Filter out over-digested peptides
|
|
232
|
+
~pl.col("Cleavage Type").is_null()
|
|
233
|
+
).with_columns(# Fill in `Half Tryptic`
|
|
234
|
+
pl.col("Cleavage Type").ne("FULL_TRP").alias("Half Tryptic")
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return df
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _add_cut_sites(df: pl.DataFrame, rcParams: dict, **kwargs) -> pl.DataFrame:
|
|
241
|
+
|
|
242
|
+
df = \
|
|
243
|
+
df.with_columns(# Adding unique identifier for all `Cleavage Type`
|
|
244
|
+
pl.when(pl.col("Cleavage Type").eq("C_SEMI"))
|
|
245
|
+
.then(pl.format("{}{}", pl.col("Next AA"), pl.col("End") + 1))
|
|
246
|
+
.when(pl.col("Cleavage Type").eq("N_SEMI"))
|
|
247
|
+
.then(pl.format("{}{}", pl.col("Start AA"), pl.col("Start")))
|
|
248
|
+
.when((pl.col("Cleavage Type").eq("FULL_TRP")) & (pl.col("Prev AA").ne("-")))
|
|
249
|
+
.then(
|
|
250
|
+
pl.format(
|
|
251
|
+
"{}{}-{}{}",
|
|
252
|
+
pl.col("Prev AA"),
|
|
253
|
+
pl.col("Start") - 1,
|
|
254
|
+
pl.col("End AA"),
|
|
255
|
+
pl.col("End"),
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
.when((pl.col("Cleavage Type").eq("FULL_TRP")) & (pl.col("Prev AA").eq("-")))
|
|
259
|
+
.then(
|
|
260
|
+
pl.format(
|
|
261
|
+
"{}{}-{}{}",
|
|
262
|
+
pl.col("Start AA"),
|
|
263
|
+
pl.col("Start"),
|
|
264
|
+
pl.col("End AA"),
|
|
265
|
+
pl.col("End"),
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
.alias("Cut Site")
|
|
269
|
+
).with_columns(
|
|
270
|
+
pl.format("{}_{}", pl.col("Protein ID"), pl.col("Cut Site")).alias("Cut Site ID")
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return df
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _log2(df: pl.DataFrame, col: str) -> pl.DataFrame:
|
|
277
|
+
|
|
278
|
+
df = df.with_columns(
|
|
279
|
+
pl.when(pl.col(col).eq(0))
|
|
280
|
+
.then(0.0)
|
|
281
|
+
.otherwise(pl.col(col).log(base=2))
|
|
282
|
+
.alias(f"Log2 {col}")
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return df
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _log10(df: pl.DataFrame, col: str) -> pl.DataFrame:
|
|
289
|
+
return df.with_columns(-pl.col(col).log10().alias(f"-Log10 {col}"))
|
flippr/graph/__init__.py
ADDED
|
File without changes
|