statgen 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.
- statgen-0.1.0/PKG-INFO +19 -0
- statgen-0.1.0/README.md +9 -0
- statgen-0.1.0/pyproject.toml +19 -0
- statgen-0.1.0/setup.cfg +4 -0
- statgen-0.1.0/statgen/__init__.py +1 -0
- statgen-0.1.0/statgen/_utils.py +1 -0
- statgen-0.1.0/statgen/annotations.py +1 -0
- statgen-0.1.0/statgen/genotype.py +1 -0
- statgen-0.1.0/statgen/ld.py +1 -0
- statgen-0.1.0/statgen/reference.py +370 -0
- statgen-0.1.0/statgen/sumstats.py +1 -0
- statgen-0.1.0/statgen.egg-info/PKG-INFO +19 -0
- statgen-0.1.0/statgen.egg-info/SOURCES.txt +14 -0
- statgen-0.1.0/statgen.egg-info/dependency_links.txt +1 -0
- statgen-0.1.0/statgen.egg-info/requires.txt +3 -0
- statgen-0.1.0/statgen.egg-info/top_level.txt +1 -0
statgen-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: statgen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Statistical genetics data objects and loaders for Python and MATLAB/Octave.
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: scipy
|
|
9
|
+
Requires-Dist: pandas
|
|
10
|
+
|
|
11
|
+
# statgen (Python package)
|
|
12
|
+
|
|
13
|
+
`statgen` provides statistical genetics data objects and loaders for aligned
|
|
14
|
+
reference, LD, annotation, genotype metadata, and summary-statistics workflows.
|
|
15
|
+
|
|
16
|
+
The package is developed alongside a MATLAB/Octave implementation, with shared
|
|
17
|
+
contracts defined in the repository `spec/` directory.
|
|
18
|
+
|
|
19
|
+
Repository: https://github.com/precimed/statgen
|
statgen-0.1.0/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# statgen (Python package)
|
|
2
|
+
|
|
3
|
+
`statgen` provides statistical genetics data objects and loaders for aligned
|
|
4
|
+
reference, LD, annotation, genotype metadata, and summary-statistics workflows.
|
|
5
|
+
|
|
6
|
+
The package is developed alongside a MATLAB/Octave implementation, with shared
|
|
7
|
+
contracts defined in the repository `spec/` directory.
|
|
8
|
+
|
|
9
|
+
Repository: https://github.com/precimed/statgen
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "statgen"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Statistical genetics data objects and loaders for Python and MATLAB/Octave."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"numpy",
|
|
13
|
+
"scipy",
|
|
14
|
+
"pandas",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
where = ["."]
|
|
19
|
+
include = ["statgen*"]
|
statgen-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Shared internal utilities
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# AnnotationShard, AnnotationPanel, load_annotations — Phase 3
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# GenotypeShard, GenotypePanel, load_genotype — Phase 6
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# LDShard, LDPanel, load_ld, fast_prune — Phase 4
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from pandas.errors import ParserError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
_CACHE_SCHEMA = "reference_cache/0.1"
|
|
13
|
+
_BIM_NCOLS = 6
|
|
14
|
+
_CANONICAL_CHR_ORDER = [str(i) for i in range(1, 23)] + ["X"]
|
|
15
|
+
_CHR_RANK = {c: i for i, c in enumerate(_CANONICAL_CHR_ORDER)}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _validate_reference_sort_order(chr_col: pd.Series, bp_col: np.ndarray, path: Path) -> None:
|
|
19
|
+
chr_rank = chr_col.map(_CHR_RANK)
|
|
20
|
+
bad_chr = chr_rank.isna()
|
|
21
|
+
if bad_chr.any():
|
|
22
|
+
idx = int(bad_chr.idxmax())
|
|
23
|
+
raise ValueError(
|
|
24
|
+
f"{path}:{idx + 1}: chr must use canonical labels 1-22 or X"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
chr_rank_vals = chr_rank.to_numpy(dtype=np.int64)
|
|
28
|
+
bp_vals = np.asarray(bp_col, dtype=np.int64)
|
|
29
|
+
bad_order = (chr_rank_vals[1:] < chr_rank_vals[:-1]) | (
|
|
30
|
+
(chr_rank_vals[1:] == chr_rank_vals[:-1]) & (bp_vals[1:] < bp_vals[:-1])
|
|
31
|
+
)
|
|
32
|
+
if bad_order.any():
|
|
33
|
+
idx = int(np.flatnonzero(bad_order)[0]) + 1
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"{path}:{idx + 1}: rows must be sorted by canonical chromosome (1-22, X) and ascending bp"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _parse_bim(path: Path) -> list:
|
|
40
|
+
try:
|
|
41
|
+
df = pd.read_csv(
|
|
42
|
+
path,
|
|
43
|
+
sep="\t",
|
|
44
|
+
header=None,
|
|
45
|
+
dtype=str,
|
|
46
|
+
keep_default_na=False,
|
|
47
|
+
na_filter=False,
|
|
48
|
+
)
|
|
49
|
+
except ParserError as exc:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"{path}: expected 6 tab-separated columns"
|
|
52
|
+
) from exc
|
|
53
|
+
|
|
54
|
+
if df.shape[1] != _BIM_NCOLS:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"{path}: expected 6 tab-separated columns, got {df.shape[1]}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
chr_col = df[0]
|
|
60
|
+
snp_col = df[1]
|
|
61
|
+
cm_raw = df[2]
|
|
62
|
+
bp_raw = df[3]
|
|
63
|
+
a1_col = df[4]
|
|
64
|
+
a2_col = df[5]
|
|
65
|
+
|
|
66
|
+
bad_chr = chr_col.eq("")
|
|
67
|
+
if bad_chr.any():
|
|
68
|
+
idx = int(bad_chr.idxmax())
|
|
69
|
+
raise ValueError(f"{path}:{idx + 1}: chr must be non-empty")
|
|
70
|
+
|
|
71
|
+
bad_allele = a1_col.eq("") | a2_col.eq("")
|
|
72
|
+
if bad_allele.any():
|
|
73
|
+
idx = int(bad_allele.idxmax())
|
|
74
|
+
raise ValueError(f"{path}:{idx + 1}: a1 and a2 must be non-empty")
|
|
75
|
+
|
|
76
|
+
cm_num = pd.to_numeric(cm_raw, errors="coerce")
|
|
77
|
+
bad_cm = cm_num.isna()
|
|
78
|
+
if bad_cm.any():
|
|
79
|
+
idx = int(bad_cm.idxmax())
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"{path}:{idx + 1}: cm is not a number: {cm_raw.iat[idx]!r}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
bp_num = pd.to_numeric(bp_raw, errors="coerce")
|
|
85
|
+
bad_bp = bp_num.isna() | (np.floor(bp_num) != bp_num)
|
|
86
|
+
if bad_bp.any():
|
|
87
|
+
idx = int(bad_bp.idxmax())
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"{path}:{idx + 1}: bp is not an integer: {bp_raw.iat[idx]!r}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
bp_int = bp_num.astype(np.int64)
|
|
93
|
+
_validate_reference_sort_order(chr_col, bp_int.to_numpy(dtype=np.int64), path)
|
|
94
|
+
|
|
95
|
+
# cm is validated for BIM schema compatibility but intentionally ignored
|
|
96
|
+
# in-memory per spec.
|
|
97
|
+
return list(
|
|
98
|
+
zip(
|
|
99
|
+
chr_col.tolist(),
|
|
100
|
+
snp_col.tolist(),
|
|
101
|
+
bp_int.tolist(),
|
|
102
|
+
a1_col.tolist(),
|
|
103
|
+
a2_col.tolist(),
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _checksum(rows) -> str:
|
|
109
|
+
text = "".join(f"{r[0]}:{r[2]}:{r[3]}:{r[4]}\n" for r in rows)
|
|
110
|
+
return hashlib.md5(text.encode()).hexdigest()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _chr_sort_key(label: str):
|
|
114
|
+
if label not in _CHR_RANK:
|
|
115
|
+
raise ValueError(f"Unsupported shard label: {label!r}; expected canonical labels 1-22 or X")
|
|
116
|
+
return _CHR_RANK[label]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class ReferenceShard:
|
|
120
|
+
def __init__(self, label: str, rows, *, _checksum_override: str | None = None):
|
|
121
|
+
self._label = label
|
|
122
|
+
self._chr = np.array([r[0] for r in rows], dtype=object)
|
|
123
|
+
self._snp = np.array([r[1] for r in rows], dtype=object)
|
|
124
|
+
self._bp = np.array([r[2] for r in rows], dtype=np.int64)
|
|
125
|
+
self._a1 = np.array([r[3] for r in rows], dtype=object)
|
|
126
|
+
self._a2 = np.array([r[4] for r in rows], dtype=object)
|
|
127
|
+
self._checksum = (
|
|
128
|
+
_checksum_override if _checksum_override is not None else _checksum(rows)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def label(self) -> str:
|
|
133
|
+
return self._label
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def num_snp(self) -> int:
|
|
137
|
+
return len(self._chr)
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def chr(self) -> np.ndarray:
|
|
141
|
+
return self._chr
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def snp(self) -> np.ndarray:
|
|
145
|
+
return self._snp
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def bp(self) -> np.ndarray:
|
|
149
|
+
return self._bp
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def a1(self) -> np.ndarray:
|
|
153
|
+
return self._a1
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def a2(self) -> np.ndarray:
|
|
157
|
+
return self._a2
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def checksum(self) -> str:
|
|
161
|
+
return self._checksum
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def _from_arrays(cls, label, chr_arr, snp_arr, bp_arr, a1_arr, a2_arr, checksum):
|
|
165
|
+
obj = cls.__new__(cls)
|
|
166
|
+
obj._label = label
|
|
167
|
+
obj._chr = np.asarray(chr_arr, dtype=object)
|
|
168
|
+
obj._snp = np.asarray(snp_arr, dtype=object)
|
|
169
|
+
obj._bp = np.asarray(bp_arr, dtype=np.int64)
|
|
170
|
+
obj._a1 = np.asarray(a1_arr, dtype=object)
|
|
171
|
+
obj._a2 = np.asarray(a2_arr, dtype=object)
|
|
172
|
+
obj._checksum = checksum
|
|
173
|
+
return obj
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ReferencePanel:
|
|
177
|
+
def __init__(self, shards: list):
|
|
178
|
+
self._shards = list(shards)
|
|
179
|
+
offsets = []
|
|
180
|
+
pos = 0
|
|
181
|
+
for s in self._shards:
|
|
182
|
+
offsets.append({"shard_label": s.label, "start0": pos, "stop0": pos + s.num_snp})
|
|
183
|
+
pos += s.num_snp
|
|
184
|
+
self._shard_offsets = offsets
|
|
185
|
+
self._num_snp = pos
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def num_snp(self) -> int:
|
|
189
|
+
return self._num_snp
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def chr(self) -> np.ndarray:
|
|
193
|
+
if not self._shards:
|
|
194
|
+
return np.array([], dtype=object)
|
|
195
|
+
return np.concatenate([s.chr for s in self._shards])
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def snp(self) -> np.ndarray:
|
|
199
|
+
if not self._shards:
|
|
200
|
+
return np.array([], dtype=object)
|
|
201
|
+
return np.concatenate([s.snp for s in self._shards])
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def bp(self) -> np.ndarray:
|
|
205
|
+
if not self._shards:
|
|
206
|
+
return np.array([], dtype=np.int64)
|
|
207
|
+
return np.concatenate([s.bp for s in self._shards])
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def a1(self) -> np.ndarray:
|
|
211
|
+
if not self._shards:
|
|
212
|
+
return np.array([], dtype=object)
|
|
213
|
+
return np.concatenate([s.a1 for s in self._shards])
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def a2(self) -> np.ndarray:
|
|
217
|
+
if not self._shards:
|
|
218
|
+
return np.array([], dtype=object)
|
|
219
|
+
return np.concatenate([s.a2 for s in self._shards])
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def shard_offsets(self) -> list:
|
|
223
|
+
return list(self._shard_offsets)
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def shards(self) -> list:
|
|
227
|
+
return list(self._shards)
|
|
228
|
+
|
|
229
|
+
def is_object_compatible(self, obj) -> bool:
|
|
230
|
+
log_fn = logger.warning
|
|
231
|
+
|
|
232
|
+
obj_shards = getattr(obj, "shards", None)
|
|
233
|
+
if obj_shards is None:
|
|
234
|
+
log_fn("statgen: is_object_compatible: object has no shards attribute")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
obj_shards = list(obj_shards)
|
|
238
|
+
if len(obj_shards) != len(self._shards):
|
|
239
|
+
log_fn(
|
|
240
|
+
"statgen: is_object_compatible: shard count mismatch: "
|
|
241
|
+
f"reference has {len(self._shards)}, object has {len(obj_shards)}"
|
|
242
|
+
)
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
ok = True
|
|
246
|
+
for ref_s, obj_s in zip(self._shards, obj_shards):
|
|
247
|
+
obj_num = getattr(obj_s, "num_snp", None)
|
|
248
|
+
if obj_num is None:
|
|
249
|
+
log_fn(
|
|
250
|
+
f"statgen: is_object_compatible: shard {ref_s.label}: "
|
|
251
|
+
"object shard has no num_snp"
|
|
252
|
+
)
|
|
253
|
+
ok = False
|
|
254
|
+
continue
|
|
255
|
+
if obj_num != ref_s.num_snp:
|
|
256
|
+
log_fn(
|
|
257
|
+
f"statgen: is_object_compatible: shard {ref_s.label}: "
|
|
258
|
+
f"row count mismatch: reference {ref_s.num_snp}, object {obj_num}"
|
|
259
|
+
)
|
|
260
|
+
ok = False
|
|
261
|
+
continue
|
|
262
|
+
obj_chk = getattr(obj_s, "checksum", None)
|
|
263
|
+
if obj_chk is not None and obj_chk != ref_s.checksum:
|
|
264
|
+
log_fn(
|
|
265
|
+
f"statgen: is_object_compatible: shard {ref_s.label}: checksum mismatch"
|
|
266
|
+
)
|
|
267
|
+
ok = False
|
|
268
|
+
return ok
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def load_reference(path, no_shard: bool = False) -> ReferencePanel:
|
|
272
|
+
path_str = str(path)
|
|
273
|
+
path_obj = Path(path_str)
|
|
274
|
+
|
|
275
|
+
if "@" in path_str:
|
|
276
|
+
parent = path_obj.parent
|
|
277
|
+
name = path_obj.name
|
|
278
|
+
at_idx = name.index("@")
|
|
279
|
+
name_prefix = name[:at_idx]
|
|
280
|
+
name_suffix = name[at_idx + 1:]
|
|
281
|
+
|
|
282
|
+
candidates = []
|
|
283
|
+
for f in sorted(parent.iterdir()):
|
|
284
|
+
if not f.is_file():
|
|
285
|
+
continue
|
|
286
|
+
n = f.name
|
|
287
|
+
if not n.startswith(name_prefix):
|
|
288
|
+
continue
|
|
289
|
+
if name_suffix and not n.endswith(name_suffix):
|
|
290
|
+
continue
|
|
291
|
+
inner = n[len(name_prefix): -len(name_suffix) if name_suffix else None]
|
|
292
|
+
if inner:
|
|
293
|
+
candidates.append((inner, f))
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
candidates.sort(key=lambda x: _chr_sort_key(x[0]))
|
|
297
|
+
except ValueError as exc:
|
|
298
|
+
raise ValueError(f"{path_str}: {exc}") from None
|
|
299
|
+
|
|
300
|
+
if not candidates:
|
|
301
|
+
raise FileNotFoundError(
|
|
302
|
+
f"No BIM shards found matching template: {path_str}"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
shards = [
|
|
306
|
+
ReferenceShard(label, _parse_bim(bim_path))
|
|
307
|
+
for label, bim_path in candidates
|
|
308
|
+
]
|
|
309
|
+
return ReferencePanel(shards)
|
|
310
|
+
|
|
311
|
+
rows = _parse_bim(path_obj)
|
|
312
|
+
|
|
313
|
+
if no_shard:
|
|
314
|
+
return ReferencePanel([ReferenceShard("all", rows)])
|
|
315
|
+
|
|
316
|
+
by_chr: dict = {}
|
|
317
|
+
for r in rows:
|
|
318
|
+
c = r[0]
|
|
319
|
+
if c not in by_chr:
|
|
320
|
+
by_chr[c] = []
|
|
321
|
+
by_chr[c].append(r)
|
|
322
|
+
|
|
323
|
+
return ReferencePanel([ReferenceShard(c, by_chr[c]) for c in by_chr])
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def save_reference_cache(panel: ReferencePanel, path) -> None:
|
|
327
|
+
# Metadata (schema, labels, checksums) as a compact JSON blob stored in the npz.
|
|
328
|
+
# SNP-axis numeric vectors (bp) and string arrays are stored as native
|
|
329
|
+
# binary numpy arrays — not JSON — per the performance contract.
|
|
330
|
+
meta = {
|
|
331
|
+
"schema": _CACHE_SCHEMA,
|
|
332
|
+
"shard_labels": [s.label for s in panel.shards],
|
|
333
|
+
"shard_checksums": [s.checksum for s in panel.shards],
|
|
334
|
+
}
|
|
335
|
+
arrays: dict = {
|
|
336
|
+
"_meta": np.frombuffer(json.dumps(meta).encode(), dtype=np.uint8)
|
|
337
|
+
}
|
|
338
|
+
for i, s in enumerate(panel.shards):
|
|
339
|
+
p = f"s{i}_"
|
|
340
|
+
arrays[p + "chr"] = np.asarray([str(v) for v in s.chr], dtype=str)
|
|
341
|
+
arrays[p + "snp"] = np.asarray([str(v) for v in s.snp], dtype=str)
|
|
342
|
+
arrays[p + "bp"] = s.bp
|
|
343
|
+
arrays[p + "a1"] = np.asarray([str(v) for v in s.a1], dtype=str)
|
|
344
|
+
arrays[p + "a2"] = np.asarray([str(v) for v in s.a2], dtype=str)
|
|
345
|
+
np.savez_compressed(path, **arrays)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def load_reference_cache(path) -> ReferencePanel:
|
|
349
|
+
data = np.load(path, allow_pickle=False)
|
|
350
|
+
meta = json.loads(bytes(data["_meta"]).decode())
|
|
351
|
+
schema = meta.get("schema")
|
|
352
|
+
if schema != _CACHE_SCHEMA:
|
|
353
|
+
raise ValueError(f"Unsupported reference cache schema: {schema!r}")
|
|
354
|
+
shards = []
|
|
355
|
+
for i, (label, checksum) in enumerate(
|
|
356
|
+
zip(meta["shard_labels"], meta["shard_checksums"])
|
|
357
|
+
):
|
|
358
|
+
p = f"s{i}_"
|
|
359
|
+
shards.append(
|
|
360
|
+
ReferenceShard._from_arrays(
|
|
361
|
+
label,
|
|
362
|
+
data[p + "chr"],
|
|
363
|
+
data[p + "snp"],
|
|
364
|
+
data[p + "bp"],
|
|
365
|
+
data[p + "a1"],
|
|
366
|
+
data[p + "a2"],
|
|
367
|
+
checksum,
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
return ReferencePanel(shards)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# SumstatsShard, Sumstats, load_sumstats — Phase 2
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: statgen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Statistical genetics data objects and loaders for Python and MATLAB/Octave.
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: scipy
|
|
9
|
+
Requires-Dist: pandas
|
|
10
|
+
|
|
11
|
+
# statgen (Python package)
|
|
12
|
+
|
|
13
|
+
`statgen` provides statistical genetics data objects and loaders for aligned
|
|
14
|
+
reference, LD, annotation, genotype metadata, and summary-statistics workflows.
|
|
15
|
+
|
|
16
|
+
The package is developed alongside a MATLAB/Octave implementation, with shared
|
|
17
|
+
contracts defined in the repository `spec/` directory.
|
|
18
|
+
|
|
19
|
+
Repository: https://github.com/precimed/statgen
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
statgen/__init__.py
|
|
4
|
+
statgen/_utils.py
|
|
5
|
+
statgen/annotations.py
|
|
6
|
+
statgen/genotype.py
|
|
7
|
+
statgen/ld.py
|
|
8
|
+
statgen/reference.py
|
|
9
|
+
statgen/sumstats.py
|
|
10
|
+
statgen.egg-info/PKG-INFO
|
|
11
|
+
statgen.egg-info/SOURCES.txt
|
|
12
|
+
statgen.egg-info/dependency_links.txt
|
|
13
|
+
statgen.egg-info/requires.txt
|
|
14
|
+
statgen.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
statgen
|