pg-phenotype 0.1.0__tar.gz

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.
Files changed (51) hide show
  1. pg_phenotype-0.1.0/Cargo.lock +316 -0
  2. pg_phenotype-0.1.0/Cargo.toml +22 -0
  3. pg_phenotype-0.1.0/LICENSE +21 -0
  4. pg_phenotype-0.1.0/PKG-INFO +153 -0
  5. pg_phenotype-0.1.0/README.md +121 -0
  6. pg_phenotype-0.1.0/crates/core/Cargo.toml +17 -0
  7. pg_phenotype-0.1.0/crates/core/examples/am_bench.rs +79 -0
  8. pg_phenotype-0.1.0/crates/core/examples/prep_bench.rs +47 -0
  9. pg_phenotype-0.1.0/crates/core/examples/score_bench.rs +90 -0
  10. pg_phenotype-0.1.0/crates/core/src/assortative/bootstrap.rs +666 -0
  11. pg_phenotype-0.1.0/crates/core/src/assortative/bvn.rs +85 -0
  12. pg_phenotype-0.1.0/crates/core/src/assortative/cephes.rs +613 -0
  13. pg_phenotype-0.1.0/crates/core/src/assortative/estimators.rs +423 -0
  14. pg_phenotype-0.1.0/crates/core/src/assortative/fit.rs +214 -0
  15. pg_phenotype-0.1.0/crates/core/src/assortative/input.rs +345 -0
  16. pg_phenotype-0.1.0/crates/core/src/assortative/kernels.rs +1082 -0
  17. pg_phenotype-0.1.0/crates/core/src/assortative/mod.rs +514 -0
  18. pg_phenotype-0.1.0/crates/core/src/assortative/permutation.rs +634 -0
  19. pg_phenotype-0.1.0/crates/core/src/assortative/primitives_parity.rs +100 -0
  20. pg_phenotype-0.1.0/crates/core/src/assortative/result.rs +379 -0
  21. pg_phenotype-0.1.0/crates/core/src/assortative/rng.rs +88 -0
  22. pg_phenotype-0.1.0/crates/core/src/assortative/sample.rs +300 -0
  23. pg_phenotype-0.1.0/crates/core/src/assortative/sandwich.rs +326 -0
  24. pg_phenotype-0.1.0/crates/core/src/error.rs +388 -0
  25. pg_phenotype-0.1.0/crates/core/src/input.rs +76 -0
  26. pg_phenotype-0.1.0/crates/core/src/lib.rs +49 -0
  27. pg_phenotype-0.1.0/crates/core/src/lineage.rs +149 -0
  28. pg_phenotype-0.1.0/crates/core/src/normal.rs +296 -0
  29. pg_phenotype-0.1.0/crates/core/src/pafgrs/biv.rs +291 -0
  30. pg_phenotype-0.1.0/crates/core/src/pafgrs/cip.rs +266 -0
  31. pg_phenotype-0.1.0/crates/core/src/pafgrs/mod.rs +18 -0
  32. pg_phenotype-0.1.0/crates/core/src/pafgrs/order.rs +64 -0
  33. pg_phenotype-0.1.0/crates/core/src/pafgrs/pa.rs +55 -0
  34. pg_phenotype-0.1.0/crates/core/src/pafgrs/prep.rs +764 -0
  35. pg_phenotype-0.1.0/crates/core/src/pafgrs/triangle.rs +92 -0
  36. pg_phenotype-0.1.0/crates/core/src/pafgrs/uni.rs +170 -0
  37. pg_phenotype-0.1.0/crates/core/tests/pg_composition.rs +69 -0
  38. pg_phenotype-0.1.0/crates/python/Cargo.toml +23 -0
  39. pg_phenotype-0.1.0/crates/python/src/assortative.rs +258 -0
  40. pg_phenotype-0.1.0/crates/python/src/lib.rs +314 -0
  41. pg_phenotype-0.1.0/crates/python/src/test_hooks.rs +13 -0
  42. pg_phenotype-0.1.0/pg_phenotype/__init__.py +36 -0
  43. pg_phenotype-0.1.0/pg_phenotype/_errors.py +47 -0
  44. pg_phenotype-0.1.0/pg_phenotype/_input.py +119 -0
  45. pg_phenotype-0.1.0/pg_phenotype/_native.pyi +81 -0
  46. pg_phenotype-0.1.0/pg_phenotype/_threads.py +116 -0
  47. pg_phenotype-0.1.0/pg_phenotype/_trait.py +123 -0
  48. pg_phenotype-0.1.0/pg_phenotype/assortative.py +372 -0
  49. pg_phenotype-0.1.0/pg_phenotype/pafgrs.py +243 -0
  50. pg_phenotype-0.1.0/pg_phenotype/py.typed +0 -0
  51. pg_phenotype-0.1.0/pyproject.toml +101 -0
@@ -0,0 +1,316 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
+
11
+ [[package]]
12
+ name = "crossbeam-deque"
13
+ version = "0.8.8"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
16
+ dependencies = [
17
+ "crossbeam-epoch",
18
+ "crossbeam-utils",
19
+ ]
20
+
21
+ [[package]]
22
+ name = "crossbeam-epoch"
23
+ version = "0.9.21"
24
+ source = "registry+https://github.com/rust-lang/crates.io-index"
25
+ checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
26
+ dependencies = [
27
+ "crossbeam-utils",
28
+ ]
29
+
30
+ [[package]]
31
+ name = "crossbeam-utils"
32
+ version = "0.8.23"
33
+ source = "registry+https://github.com/rust-lang/crates.io-index"
34
+ checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
35
+
36
+ [[package]]
37
+ name = "either"
38
+ version = "1.19.0"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
41
+
42
+ [[package]]
43
+ name = "heck"
44
+ version = "0.5.0"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
47
+
48
+ [[package]]
49
+ name = "libc"
50
+ version = "0.2.190"
51
+ source = "registry+https://github.com/rust-lang/crates.io-index"
52
+ checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
53
+
54
+ [[package]]
55
+ name = "libm"
56
+ version = "0.2.16"
57
+ source = "registry+https://github.com/rust-lang/crates.io-index"
58
+ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
59
+
60
+ [[package]]
61
+ name = "matrixmultiply"
62
+ version = "0.3.11"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7"
65
+ dependencies = [
66
+ "autocfg",
67
+ "rawpointer",
68
+ ]
69
+
70
+ [[package]]
71
+ name = "ndarray"
72
+ version = "0.17.2"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
75
+ dependencies = [
76
+ "matrixmultiply",
77
+ "num-complex",
78
+ "num-integer",
79
+ "num-traits",
80
+ "portable-atomic",
81
+ "portable-atomic-util",
82
+ "rawpointer",
83
+ ]
84
+
85
+ [[package]]
86
+ name = "num-bigint"
87
+ version = "0.4.8"
88
+ source = "registry+https://github.com/rust-lang/crates.io-index"
89
+ checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
90
+ dependencies = [
91
+ "num-integer",
92
+ "num-traits",
93
+ ]
94
+
95
+ [[package]]
96
+ name = "num-complex"
97
+ version = "0.4.6"
98
+ source = "registry+https://github.com/rust-lang/crates.io-index"
99
+ checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
100
+ dependencies = [
101
+ "num-traits",
102
+ ]
103
+
104
+ [[package]]
105
+ name = "num-integer"
106
+ version = "0.1.47"
107
+ source = "registry+https://github.com/rust-lang/crates.io-index"
108
+ checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
109
+ dependencies = [
110
+ "num-traits",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "num-traits"
115
+ version = "0.2.19"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
118
+ dependencies = [
119
+ "autocfg",
120
+ ]
121
+
122
+ [[package]]
123
+ name = "numpy"
124
+ version = "0.29.0"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "6a5b15d63a5ff39e378daed0e1340d3a5964703ea9712eb09a0dc66fade996f4"
127
+ dependencies = [
128
+ "libc",
129
+ "ndarray",
130
+ "num-complex",
131
+ "num-integer",
132
+ "num-traits",
133
+ "pyo3",
134
+ "pyo3-build-config",
135
+ "rustc-hash",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "once_cell"
140
+ version = "1.21.4"
141
+ source = "registry+https://github.com/rust-lang/crates.io-index"
142
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
143
+
144
+ [[package]]
145
+ name = "pedigree-graph-core"
146
+ version = "0.12.2"
147
+ source = "git+https://github.com/rwaples/pedigree-graph?rev=62c82fa6468aec0f579dc9286c5dbebe7d077500#62c82fa6468aec0f579dc9286c5dbebe7d077500"
148
+ dependencies = [
149
+ "num-bigint",
150
+ "num-integer",
151
+ "num-traits",
152
+ "rayon",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "pg-phenotype-core"
157
+ version = "0.1.0"
158
+ dependencies = [
159
+ "libm",
160
+ "pedigree-graph-core",
161
+ "rayon",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "pg-phenotype-python"
166
+ version = "0.1.0"
167
+ dependencies = [
168
+ "numpy",
169
+ "pg-phenotype-core",
170
+ "pyo3",
171
+ ]
172
+
173
+ [[package]]
174
+ name = "portable-atomic"
175
+ version = "1.15.0"
176
+ source = "registry+https://github.com/rust-lang/crates.io-index"
177
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
178
+
179
+ [[package]]
180
+ name = "portable-atomic-util"
181
+ version = "0.2.8"
182
+ source = "registry+https://github.com/rust-lang/crates.io-index"
183
+ checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715"
184
+ dependencies = [
185
+ "portable-atomic",
186
+ ]
187
+
188
+ [[package]]
189
+ name = "proc-macro2"
190
+ version = "1.0.107"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
193
+ dependencies = [
194
+ "unicode-ident",
195
+ ]
196
+
197
+ [[package]]
198
+ name = "pyo3"
199
+ version = "0.29.3"
200
+ source = "registry+https://github.com/rust-lang/crates.io-index"
201
+ checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655"
202
+ dependencies = [
203
+ "libc",
204
+ "once_cell",
205
+ "portable-atomic",
206
+ "pyo3-build-config",
207
+ "pyo3-ffi",
208
+ "pyo3-macros",
209
+ ]
210
+
211
+ [[package]]
212
+ name = "pyo3-build-config"
213
+ version = "0.29.3"
214
+ source = "registry+https://github.com/rust-lang/crates.io-index"
215
+ checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb"
216
+ dependencies = [
217
+ "target-lexicon",
218
+ ]
219
+
220
+ [[package]]
221
+ name = "pyo3-ffi"
222
+ version = "0.29.3"
223
+ source = "registry+https://github.com/rust-lang/crates.io-index"
224
+ checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330"
225
+ dependencies = [
226
+ "libc",
227
+ "pyo3-build-config",
228
+ ]
229
+
230
+ [[package]]
231
+ name = "pyo3-macros"
232
+ version = "0.29.3"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6"
235
+ dependencies = [
236
+ "proc-macro2",
237
+ "pyo3-macros-backend",
238
+ "quote",
239
+ "syn",
240
+ ]
241
+
242
+ [[package]]
243
+ name = "pyo3-macros-backend"
244
+ version = "0.29.3"
245
+ source = "registry+https://github.com/rust-lang/crates.io-index"
246
+ checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3"
247
+ dependencies = [
248
+ "heck",
249
+ "proc-macro2",
250
+ "quote",
251
+ "syn",
252
+ ]
253
+
254
+ [[package]]
255
+ name = "quote"
256
+ version = "1.0.47"
257
+ source = "registry+https://github.com/rust-lang/crates.io-index"
258
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
259
+ dependencies = [
260
+ "proc-macro2",
261
+ ]
262
+
263
+ [[package]]
264
+ name = "rawpointer"
265
+ version = "0.2.1"
266
+ source = "registry+https://github.com/rust-lang/crates.io-index"
267
+ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
268
+
269
+ [[package]]
270
+ name = "rayon"
271
+ version = "1.12.0"
272
+ source = "registry+https://github.com/rust-lang/crates.io-index"
273
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
274
+ dependencies = [
275
+ "either",
276
+ "rayon-core",
277
+ ]
278
+
279
+ [[package]]
280
+ name = "rayon-core"
281
+ version = "1.13.0"
282
+ source = "registry+https://github.com/rust-lang/crates.io-index"
283
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
284
+ dependencies = [
285
+ "crossbeam-deque",
286
+ "crossbeam-utils",
287
+ ]
288
+
289
+ [[package]]
290
+ name = "rustc-hash"
291
+ version = "2.1.3"
292
+ source = "registry+https://github.com/rust-lang/crates.io-index"
293
+ checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
294
+
295
+ [[package]]
296
+ name = "syn"
297
+ version = "2.0.119"
298
+ source = "registry+https://github.com/rust-lang/crates.io-index"
299
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
300
+ dependencies = [
301
+ "proc-macro2",
302
+ "quote",
303
+ "unicode-ident",
304
+ ]
305
+
306
+ [[package]]
307
+ name = "target-lexicon"
308
+ version = "0.13.5"
309
+ source = "registry+https://github.com/rust-lang/crates.io-index"
310
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
311
+
312
+ [[package]]
313
+ name = "unicode-ident"
314
+ version = "1.0.26"
315
+ source = "registry+https://github.com/rust-lang/crates.io-index"
316
+ checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
@@ -0,0 +1,22 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = ["crates/core", "crates/python"]
4
+
5
+ [workspace.package]
6
+ version = "0.1.0"
7
+ edition = "2021"
8
+ # pedigree-graph-core's floor; the R binding crate states the same.
9
+ rust-version = "1.85"
10
+ license = "MIT"
11
+ repository = "https://github.com/rwaples/pg-phenotype"
12
+
13
+ [workspace.dependencies]
14
+ # Re-pinned on purpose only (ADR 0001): pg-core is unpublished until 1.0.
15
+ # This rev is pedigree-graph v0.12.2.
16
+ pedigree-graph-core = { git = "https://github.com/rwaples/pedigree-graph", rev = "62c82fa6468aec0f579dc9286c5dbebe7d077500" }
17
+ rayon = "1.10"
18
+
19
+ [profile.release]
20
+ opt-level = 3
21
+ debug = 1
22
+ codegen-units = 1
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan Waples
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.
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: pg-phenotype
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Science/Research
6
+ Classifier: Operating System :: OS Independent
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Programming Language :: Python :: 3.14
10
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
11
+ Requires-Dist: numpy>=2.2,<3
12
+ Requires-Dist: scipy>=1.14,<2 ; extra == 'test'
13
+ Requires-Dist: pedigree-graph>=0.12.2,<0.13 ; extra == 'test'
14
+ Requires-Dist: pytest>=9,<10 ; extra == 'test'
15
+ Requires-Dist: pytest-xdist>=3.8,<4 ; extra == 'test'
16
+ Requires-Dist: ruff>=0.16,<0.17 ; extra == 'test'
17
+ Requires-Dist: hypothesis>=6,<7 ; extra == 'test'
18
+ Requires-Dist: pandas>=3.0,<4 ; extra == 'test'
19
+ Requires-Dist: polars>=1.43.2,<2 ; extra == 'test'
20
+ Requires-Dist: ty==0.0.84 ; extra == 'typecheck'
21
+ Provides-Extra: test
22
+ Provides-Extra: typecheck
23
+ License-File: LICENSE
24
+ Summary: Phenotypes in the context of a pedigree, on pedigree-graph's Rust core: PA-FGRS and more
25
+ Author: Ryan Waples
26
+ License-Expression: MIT
27
+ Requires-Python: >=3.13
28
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
29
+ Project-URL: Homepage, https://github.com/rwaples/pg-phenotype
30
+ Project-URL: Repository, https://github.com/rwaples/pg-phenotype
31
+
32
+ # pg-phenotype
33
+
34
+ Phenotypes in the context of a pedigree, as an extension of
35
+ [pedigree-graph](https://github.com/rwaples/pedigree-graph): a Rust core with
36
+ Python and R bindings. Relationships and kinship come from pedigree-graph's
37
+ core; each method is a module on top.
38
+
39
+ | Method | Module | Status |
40
+ |---|---|---|
41
+ | PA-FGRS family genetic risk scores (Dybdahl Krebs et al., Am J Hum Genet 2024, doi:10.1016/j.ajhg.2024.09.009) | `pg_phenotype.pafgrs` | available |
42
+ | Assortative mating: the Mate Correlation of one or two traits, ported from pedsum #13 | `pg_phenotype.assortative` | available |
43
+
44
+ ## Python
45
+
46
+ ```python
47
+ import pg_phenotype as pgp
48
+ from pg_phenotype import pafgrs
49
+
50
+ trait = pgp.Trait(dx, kind="binary") # one value per pedigree row
51
+ prep = pafgrs.prepare(pedigree, ndegree=2) # once per pedigree
52
+ cip = pafgrs.Cip(ages, cip_values) # K is the last value
53
+ scores = pafgrs.score_univariate(prep, trait, age=dx_age, cip=cip, h2=0.4)
54
+ scores.est, scores.var, scores.n_relatives, scores.metadata
55
+
56
+ both = pafgrs.score_bivariate(prep, (t1, t2), age=(a1, a2), cip=(c1, c2), h2=(0.4, 0.6), rg=0.5)
57
+ ```
58
+
59
+ - `pedigree`: columns `id`, `mother`, `father` (`-1` or NA when missing) and
60
+ optional `twin`, `sex` (`0` female, `1` male), as a polars or pandas frame
61
+ or a mapping of arrays. It is validated by pedigree-graph's rules.
62
+ - `Trait(values, kind=None)`: one phenotype column on the pedigree's rows,
63
+ NA where unknown. The kind is continuous, binary, ordinal or categorical,
64
+ inferred when unambiguous.
65
+ - PA-FGRS takes a binary trait. `age` is the onset age for a case and the
66
+ last observed age for a control; a control without an age is unobserved
67
+ and counted.
68
+ - `probands=` restricts the scored rows; relatives are drawn from the whole
69
+ pedigree. Output is one record per proband in input-row order.
70
+ - `configure_threads(n)` or `PG_PHENOTYPE_THREADS` sets the thread budget
71
+ (default 1). Results are identical for every budget.
72
+
73
+ Errors are `pg_phenotype.ValidationError`, `ParameterError` and
74
+ `ResourceError`, each with a stable `.code` and `.fields`.
75
+
76
+ ### Assortative mating
77
+
78
+ ```python
79
+ from pg_phenotype.assortative import mate_correlation
80
+
81
+ res = mate_correlation(pedigree, [pgp.Trait(liab), pgp.Trait(dx)], stratum=birth_decade, bootstrap=1000, seed=1)
82
+ cell = res.cell(0, 1) # mothers' trait 0 x fathers' trait 1
83
+ cell.primary.value, cell.primary.ci, cell.primary.permutation.p
84
+ cell.stratified.result.value # within birth-decade strata
85
+ res.within_person["mothers"]
86
+ ```
87
+
88
+ The Mate Correlation is the correlation between the mother's and the
89
+ father's value of a trait over a pedigree's Mating Pairs. Each cell uses
90
+ the estimators its trait kinds call for (Pearson, tetrachoric, polychoric,
91
+ biserial or polyserial, with their closed-form companions), a Mate Network
92
+ sandwich SE, an optional Mate Network bootstrap, and a sequential
93
+ father-permutation p-value. [docs/assortative-mating.md](docs/assortative-mating.md)
94
+ describes the inputs, rules and every result field, and
95
+ [docs/assortative-mating-design.md](docs/assortative-mating-design.md)
96
+ explains the design.
97
+
98
+ ## R
99
+
100
+ ```r
101
+ library(pgphenotype)
102
+
103
+ dx <- trait(ped$dx, kind = "binary")
104
+ prep <- pafgrs_prepare(ped, ndegree = 2L)
105
+ cip <- pafgrs_cip(ages, cip_values)
106
+ scores <- pafgrs_score_univariate(prep, dx, age = ped$dx_age, cip = cip, h2 = 0.4)
107
+ both <- pafgrs_score_bivariate(prep, list(t1, t2), ages = list(a1, a2),
108
+ cips = list(c1, c2), h2 = c(0.4, 0.6), rg = 0.5)
109
+ am <- assortative_mate_correlation(ped, list(trait(ped$liab), trait(ped$dx)),
110
+ stratum = ped$birth_decade, bootstrap = 1000, seed = 1)
111
+ ```
112
+
113
+ The R package runs the same core as Python and returns the same scores bit
114
+ for bit. Errors are conditions of class `pgphenotype_error` (with
115
+ `pgphenotype_validation_error` and friends) carrying `$code` and `$fields`,
116
+ positions 1-based.
117
+
118
+ ## Correctness (PA-FGRS)
119
+
120
+ - Rust against NumPy reference cores (fitACE's, verbatim) given the same
121
+ covariance and order: within 1e-10.
122
+ - Against BioPsyk/PAFGRS's own `pa_fgrs` on stored fixtures: within 1e-10
123
+ (1.1e-15 observed).
124
+ - Every kinship value bit-identical to pedigree-graph's `pair_kinship`.
125
+
126
+ ## Correctness (assortative mating)
127
+
128
+ - Against pedsum #13 at `142adf3` on 74 stored cases (28 fixtures, pedsum's
129
+ own benchmark pedigrees at 10^4 pairs, and 40 random pedigrees), every
130
+ count, reason, flag and p-value is identical. Closed-form estimates agree
131
+ within 4.5e-15, and latent ones within 3.2e-10, inside Brent's 1e-7
132
+ tolerance. One case differs on float noise in pedsum. The
133
+ [gate report](docs/gates/assortative-mating/README.md) has the details.
134
+ - On pedsum's benchmark pedigrees at 10^4 to 10^6 pairs and up to 12
135
+ threads, pg-phenotype's median wall time and peak memory are at most
136
+ 0.994 and 0.981 of pedsum's
137
+ ([benchmark report](docs/gates/assortative-mating/benchmark.md)).
138
+ - The SciPy and C-library functions pedsum calls are ported bit for bit
139
+ ([ADR 0005](docs/adr/0005-assortative-mating-reproduces-pedsums-numerics.md)).
140
+
141
+ Design decisions are in `docs/adr/`; vocabulary in `CONTEXT.md`.
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ pixi run test # rebuilds the extension with test hooks, then pytest
147
+ pixi run test-rust
148
+ pixi run -e r r-test
149
+ pixi run test-against-pg ../pedigree-graph # every suite against a pg checkout
150
+ ```
151
+
152
+ To release a version, follow [How to release pg-phenotype](docs/releasing.md).
153
+
@@ -0,0 +1,121 @@
1
+ # pg-phenotype
2
+
3
+ Phenotypes in the context of a pedigree, as an extension of
4
+ [pedigree-graph](https://github.com/rwaples/pedigree-graph): a Rust core with
5
+ Python and R bindings. Relationships and kinship come from pedigree-graph's
6
+ core; each method is a module on top.
7
+
8
+ | Method | Module | Status |
9
+ |---|---|---|
10
+ | PA-FGRS family genetic risk scores (Dybdahl Krebs et al., Am J Hum Genet 2024, doi:10.1016/j.ajhg.2024.09.009) | `pg_phenotype.pafgrs` | available |
11
+ | Assortative mating: the Mate Correlation of one or two traits, ported from pedsum #13 | `pg_phenotype.assortative` | available |
12
+
13
+ ## Python
14
+
15
+ ```python
16
+ import pg_phenotype as pgp
17
+ from pg_phenotype import pafgrs
18
+
19
+ trait = pgp.Trait(dx, kind="binary") # one value per pedigree row
20
+ prep = pafgrs.prepare(pedigree, ndegree=2) # once per pedigree
21
+ cip = pafgrs.Cip(ages, cip_values) # K is the last value
22
+ scores = pafgrs.score_univariate(prep, trait, age=dx_age, cip=cip, h2=0.4)
23
+ scores.est, scores.var, scores.n_relatives, scores.metadata
24
+
25
+ both = pafgrs.score_bivariate(prep, (t1, t2), age=(a1, a2), cip=(c1, c2), h2=(0.4, 0.6), rg=0.5)
26
+ ```
27
+
28
+ - `pedigree`: columns `id`, `mother`, `father` (`-1` or NA when missing) and
29
+ optional `twin`, `sex` (`0` female, `1` male), as a polars or pandas frame
30
+ or a mapping of arrays. It is validated by pedigree-graph's rules.
31
+ - `Trait(values, kind=None)`: one phenotype column on the pedigree's rows,
32
+ NA where unknown. The kind is continuous, binary, ordinal or categorical,
33
+ inferred when unambiguous.
34
+ - PA-FGRS takes a binary trait. `age` is the onset age for a case and the
35
+ last observed age for a control; a control without an age is unobserved
36
+ and counted.
37
+ - `probands=` restricts the scored rows; relatives are drawn from the whole
38
+ pedigree. Output is one record per proband in input-row order.
39
+ - `configure_threads(n)` or `PG_PHENOTYPE_THREADS` sets the thread budget
40
+ (default 1). Results are identical for every budget.
41
+
42
+ Errors are `pg_phenotype.ValidationError`, `ParameterError` and
43
+ `ResourceError`, each with a stable `.code` and `.fields`.
44
+
45
+ ### Assortative mating
46
+
47
+ ```python
48
+ from pg_phenotype.assortative import mate_correlation
49
+
50
+ res = mate_correlation(pedigree, [pgp.Trait(liab), pgp.Trait(dx)], stratum=birth_decade, bootstrap=1000, seed=1)
51
+ cell = res.cell(0, 1) # mothers' trait 0 x fathers' trait 1
52
+ cell.primary.value, cell.primary.ci, cell.primary.permutation.p
53
+ cell.stratified.result.value # within birth-decade strata
54
+ res.within_person["mothers"]
55
+ ```
56
+
57
+ The Mate Correlation is the correlation between the mother's and the
58
+ father's value of a trait over a pedigree's Mating Pairs. Each cell uses
59
+ the estimators its trait kinds call for (Pearson, tetrachoric, polychoric,
60
+ biserial or polyserial, with their closed-form companions), a Mate Network
61
+ sandwich SE, an optional Mate Network bootstrap, and a sequential
62
+ father-permutation p-value. [docs/assortative-mating.md](docs/assortative-mating.md)
63
+ describes the inputs, rules and every result field, and
64
+ [docs/assortative-mating-design.md](docs/assortative-mating-design.md)
65
+ explains the design.
66
+
67
+ ## R
68
+
69
+ ```r
70
+ library(pgphenotype)
71
+
72
+ dx <- trait(ped$dx, kind = "binary")
73
+ prep <- pafgrs_prepare(ped, ndegree = 2L)
74
+ cip <- pafgrs_cip(ages, cip_values)
75
+ scores <- pafgrs_score_univariate(prep, dx, age = ped$dx_age, cip = cip, h2 = 0.4)
76
+ both <- pafgrs_score_bivariate(prep, list(t1, t2), ages = list(a1, a2),
77
+ cips = list(c1, c2), h2 = c(0.4, 0.6), rg = 0.5)
78
+ am <- assortative_mate_correlation(ped, list(trait(ped$liab), trait(ped$dx)),
79
+ stratum = ped$birth_decade, bootstrap = 1000, seed = 1)
80
+ ```
81
+
82
+ The R package runs the same core as Python and returns the same scores bit
83
+ for bit. Errors are conditions of class `pgphenotype_error` (with
84
+ `pgphenotype_validation_error` and friends) carrying `$code` and `$fields`,
85
+ positions 1-based.
86
+
87
+ ## Correctness (PA-FGRS)
88
+
89
+ - Rust against NumPy reference cores (fitACE's, verbatim) given the same
90
+ covariance and order: within 1e-10.
91
+ - Against BioPsyk/PAFGRS's own `pa_fgrs` on stored fixtures: within 1e-10
92
+ (1.1e-15 observed).
93
+ - Every kinship value bit-identical to pedigree-graph's `pair_kinship`.
94
+
95
+ ## Correctness (assortative mating)
96
+
97
+ - Against pedsum #13 at `142adf3` on 74 stored cases (28 fixtures, pedsum's
98
+ own benchmark pedigrees at 10^4 pairs, and 40 random pedigrees), every
99
+ count, reason, flag and p-value is identical. Closed-form estimates agree
100
+ within 4.5e-15, and latent ones within 3.2e-10, inside Brent's 1e-7
101
+ tolerance. One case differs on float noise in pedsum. The
102
+ [gate report](docs/gates/assortative-mating/README.md) has the details.
103
+ - On pedsum's benchmark pedigrees at 10^4 to 10^6 pairs and up to 12
104
+ threads, pg-phenotype's median wall time and peak memory are at most
105
+ 0.994 and 0.981 of pedsum's
106
+ ([benchmark report](docs/gates/assortative-mating/benchmark.md)).
107
+ - The SciPy and C-library functions pedsum calls are ported bit for bit
108
+ ([ADR 0005](docs/adr/0005-assortative-mating-reproduces-pedsums-numerics.md)).
109
+
110
+ Design decisions are in `docs/adr/`; vocabulary in `CONTEXT.md`.
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ pixi run test # rebuilds the extension with test hooks, then pytest
116
+ pixi run test-rust
117
+ pixi run -e r r-test
118
+ pixi run test-against-pg ../pedigree-graph # every suite against a pg checkout
119
+ ```
120
+
121
+ To release a version, follow [How to release pg-phenotype](docs/releasing.md).
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "pg-phenotype-core"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ rust-version.workspace = true
6
+ license.workspace = true
7
+ repository.workspace = true
8
+ publish = false
9
+ description = "Host-neutral phenotype-in-pedigree methods on pedigree-graph-core"
10
+
11
+ [lib]
12
+ name = "pg_phenotype_core"
13
+
14
+ [dependencies]
15
+ pedigree-graph-core.workspace = true
16
+ rayon.workspace = true
17
+ libm = "0.2"