bed-reader 0.2.37__cp312-none-win_amd64.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.
Files changed (51) hide show
  1. bed_reader/__init__.py +30 -0
  2. bed_reader/_open_bed.py +1290 -0
  3. bed_reader/_sample_data.py +99 -0
  4. bed_reader/_to_bed.py +235 -0
  5. bed_reader/bed_reader.cp312-win_amd64.pyd +0 -0
  6. bed_reader/tests/__init__.py +1 -0
  7. bed_reader/tests/benchmark/benchmark.py +129 -0
  8. bed_reader/tests/data/500x400_o640_array.memmap +0 -0
  9. bed_reader/tests/data/badfile.bed +1 -0
  10. bed_reader/tests/data/empty.bed +0 -0
  11. bed_reader/tests/data/plink_sim_10s_100v_10pmiss.bed +0 -0
  12. bed_reader/tests/data/plink_sim_10s_100v_10pmiss.bim +100 -0
  13. bed_reader/tests/data/plink_sim_10s_100v_10pmiss.fam +10 -0
  14. bed_reader/tests/data/small.bad_bim +4 -0
  15. bed_reader/tests/data/small.bed +1 -0
  16. bed_reader/tests/data/small.bim +4 -0
  17. bed_reader/tests/data/small.bim_bad_positions.bim +4 -0
  18. bed_reader/tests/data/small.deb +1 -0
  19. bed_reader/tests/data/small.fam +3 -0
  20. bed_reader/tests/data/small.fam_bad +4 -0
  21. bed_reader/tests/data/small.maf +3 -0
  22. bed_reader/tests/data/small.mib +4 -0
  23. bed_reader/tests/data/small2_array.memmap +0 -0
  24. bed_reader/tests/data/small_array.memmap +0 -0
  25. bed_reader/tests/data/small_no_bim.bed +1 -0
  26. bed_reader/tests/data/small_no_bim.fam +3 -0
  27. bed_reader/tests/data/small_no_fam.bed +1 -0
  28. bed_reader/tests/data/small_no_fam.bim +4 -0
  29. bed_reader/tests/data/small_too_short.bed +1 -0
  30. bed_reader/tests/data/small_too_short.bim +4 -0
  31. bed_reader/tests/data/small_too_short.fam +3 -0
  32. bed_reader/tests/data/smallmode0.bed +0 -0
  33. bed_reader/tests/data/smallmode0.bim +3 -0
  34. bed_reader/tests/data/smallmode0.fam +4 -0
  35. bed_reader/tests/data/smallmodebad.bed +1 -0
  36. bed_reader/tests/data/some_missing.bed +0 -0
  37. bed_reader/tests/data/some_missing.bim +100 -0
  38. bed_reader/tests/data/some_missing.fam +100 -0
  39. bed_reader/tests/data/some_missing.properties.npz +0 -0
  40. bed_reader/tests/data/some_missing.val.npy +0 -0
  41. bed_reader/tests/data/toydata.5chrom.bed +0 -0
  42. bed_reader/tests/data/toydata.5chrom.bim +10000 -0
  43. bed_reader/tests/data/toydata.5chrom.fam +500 -0
  44. bed_reader/tests/registry.txt +36 -0
  45. bed_reader/tests/test_file_dot.py +368 -0
  46. bed_reader/tests/test_open_bed.py +806 -0
  47. bed_reader-0.2.37.dist-info/METADATA +112 -0
  48. bed_reader-0.2.37.dist-info/RECORD +51 -0
  49. bed_reader-0.2.37.dist-info/WHEEL +4 -0
  50. bed_reader-0.2.37.dist-info/license_files/AUTHORS.txt +2 -0
  51. bed_reader-0.2.37.dist-info/license_files/LICENSE.md +202 -0
@@ -0,0 +1,1290 @@
1
+ import logging
2
+ import multiprocessing
3
+ import os
4
+ from dataclasses import dataclass
5
+ from itertools import repeat, takewhile
6
+ from pathlib import Path
7
+ from typing import Any, List, Mapping, Optional, Union
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ from .bed_reader import read_f32, read_f64, read_i8 # type: ignore
13
+
14
+
15
+ # https://stackoverflow.com/questions/845058/how-to-get-line-count-of-a-large-file-cheaply-in-python
16
+ def _rawincount(filepath):
17
+ with open(filepath, "rb") as f:
18
+ bufgen = takewhile(lambda x: x, (f.raw.read(1024 * 1024) for _ in repeat(None)))
19
+ return sum(buf.count(b"\n") for buf in bufgen)
20
+
21
+
22
+ @dataclass
23
+ class _MetaMeta:
24
+ suffix: str
25
+ column: int
26
+ dtype: type
27
+ missing_value: object
28
+ fill_sequence: object
29
+
30
+
31
+ def _all_same(key, length, missing, dtype):
32
+ if np.issubdtype(dtype, np.str_):
33
+ dtype = f"<U{len(missing)}"
34
+ return np.full(length, missing, dtype=dtype)
35
+
36
+
37
+ def _sequence(key, length, missing, dtype):
38
+ if np.issubdtype(dtype, np.str_):
39
+ longest = len(f"{key}{length}")
40
+ dtype = f"<U{longest}"
41
+ return np.fromiter(
42
+ (f"{key}{i + 1}" for i in range(length)), dtype=dtype, count=length
43
+ )
44
+
45
+
46
+ _delimiters = {"fam": r"\s+", "bim": "\t"}
47
+ _count_name = {"fam": "iid_count", "bim": "sid_count"}
48
+
49
+
50
+ _meta_meta = {
51
+ # https://stackoverflow.com/questions/41921255/staticmethod-object-is-not-callable
52
+ "fid": _MetaMeta("fam", 0, np.str_, "0", _all_same),
53
+ "iid": _MetaMeta("fam", 1, np.str_, None, _sequence),
54
+ "father": _MetaMeta("fam", 2, np.str_, "0", _all_same),
55
+ "mother": _MetaMeta("fam", 3, np.str_, "0", _all_same),
56
+ "sex": _MetaMeta("fam", 4, np.int32, 0, _all_same),
57
+ "pheno": _MetaMeta("fam", 5, np.str_, "0", _all_same),
58
+ "chromosome": _MetaMeta("bim", 0, np.str_, "0", _all_same),
59
+ "sid": _MetaMeta("bim", 1, np.str_, None, _sequence),
60
+ "cm_position": _MetaMeta("bim", 2, np.float32, 0, _all_same),
61
+ "bp_position": _MetaMeta("bim", 3, np.int32, 0, _all_same),
62
+ "allele_1": _MetaMeta("bim", 4, np.str_, "A1", _all_same),
63
+ "allele_2": _MetaMeta("bim", 5, np.str_, "A2", _all_same),
64
+ }
65
+
66
+
67
+ def get_num_threads(num_threads=None):
68
+ if num_threads is not None:
69
+ return num_threads
70
+ if "PST_NUM_THREADS" in os.environ:
71
+ return int(os.environ["PST_NUM_THREADS"])
72
+ if "NUM_THREADS" in os.environ:
73
+ return int(os.environ["NUM_THREADS"])
74
+ if "MKL_NUM_THREADS" in os.environ:
75
+ return int(os.environ["MKL_NUM_THREADS"])
76
+ return multiprocessing.cpu_count()
77
+
78
+
79
+ class open_bed:
80
+ """
81
+ Open a PLINK .bed file for reading.
82
+
83
+ Parameters
84
+ ----------
85
+ filepath: pathlib.Path or str
86
+ File path to the .bed file.
87
+ iid_count: None or int, optional
88
+ Number of individuals (samples) in the .bed file.
89
+ The default (``iid_count=None``) finds the number
90
+ automatically by quickly scanning the .fam file.
91
+ sid_count: None or int, optional
92
+ Number of SNPs (variants) in the .bed file.
93
+ The default (``sid_count=None``) finds the number
94
+ automatically by quickly scanning the .bim file.
95
+ properties: dict, optional
96
+ A dictionary of any replacement properties. The default is an empty dictionary.
97
+ The keys of the dictionary are the names of the properties to replace.
98
+ The possible keys are:
99
+
100
+ "fid" (family id), "iid" (individual or sample id), "father" (father id),
101
+ "mother" (mother id), "sex", "pheno" (phenotype), "chromosome", "sid"
102
+ (SNP or variant id), "cm_position" (centimorgan position), "bp_position"
103
+ (base-pair position), "allele_1", "allele_2".
104
+
105
+ The values are replacement lists or arrays. A value can also be `None`,
106
+ meaning do not read or offer this property. See examples, below.
107
+
108
+ The list or array will be converted to a :class:`numpy.ndarray`
109
+ of the appropriate dtype, if necessary. Any :class:`numpy.nan` values
110
+ will converted to the appropriate missing value. The PLINK `.fam specification
111
+ <https://www.cog-genomics.org/plink2/formats#fam>`_
112
+ and `.bim specification <https://www.cog-genomics.org/plink2/formats#bim>`_
113
+ lists the dtypes and missing values for each property.
114
+
115
+ count_A1: bool, optional
116
+ True (default) to count the number of A1 alleles (the PLINK standard).
117
+ False to count the number of A2 alleles.
118
+ num_threads: None or int, optional
119
+ The number of threads with which to read data. Defaults to all available
120
+ processors.
121
+ Can also be set with these environment variables (listed in priority order):
122
+ 'PST_NUM_THREADS', 'NUM_THREADS', 'MKL_NUM_THREADS'.
123
+ skip_format_check: bool, optional
124
+ False (default) to immediately check for expected starting bytes in
125
+ the .bed file. True to delay the check until (and if) data is read.
126
+ fam_filepath: pathlib.Path or str, optional
127
+ Path to the file containing information about each individual (sample).
128
+ Defaults to replacing the .bed file’s suffix with .fam.
129
+ bim_filepath: pathlib.Path or str, optional
130
+ Path to the file containing information about each SNP (variant).
131
+ Defaults to replacing the .bed file’s suffix with .bim.
132
+
133
+ Returns
134
+ -------
135
+ open_bed
136
+ an open_bed object
137
+
138
+ Examples
139
+ --------
140
+
141
+ List individual (sample) :attr:`iid` and SNP (variant) :attr:`sid`, then :meth:`read`
142
+ the whole file.
143
+
144
+ .. doctest::
145
+
146
+ >>> from bed_reader import open_bed, sample_file
147
+ >>>
148
+ >>> file_name = sample_file("small.bed")
149
+ >>> bed = open_bed(file_name)
150
+ >>> print(bed.iid)
151
+ ['iid1' 'iid2' 'iid3']
152
+ >>> print(bed.sid)
153
+ ['sid1' 'sid2' 'sid3' 'sid4']
154
+ >>> print(bed.read())
155
+ [[ 1. 0. nan 0.]
156
+ [ 2. 0. nan 2.]
157
+ [ 0. 1. 2. 0.]]
158
+ >>> del bed # optional: delete bed object
159
+
160
+ Open the file and read data for one SNP (variant)
161
+ at index position 2.
162
+
163
+ .. doctest::
164
+
165
+ >>> import numpy as np
166
+ >>> with open_bed(file_name) as bed:
167
+ ... print(bed.read(np.s_[:,2]))
168
+ [[nan]
169
+ [nan]
170
+ [ 2.]]
171
+
172
+ Replace :attr:`iid`.
173
+
174
+
175
+ >>> bed = open_bed(file_name, properties={"iid":["sample1","sample2","sample3"]})
176
+ >>> print(bed.iid) # replaced
177
+ ['sample1' 'sample2' 'sample3']
178
+ >>> print(bed.sid) # same as before
179
+ ['sid1' 'sid2' 'sid3' 'sid4']
180
+
181
+ Give the number of individuals (samples) and SNPs (variants) so that the .fam and
182
+ .bim files need never be opened.
183
+
184
+ >>> with open_bed(file_name, iid_count=3, sid_count=4) as bed:
185
+ ... print(bed.read())
186
+ [[ 1. 0. nan 0.]
187
+ [ 2. 0. nan 2.]
188
+ [ 0. 1. 2. 0.]]
189
+
190
+ Mark some properties as "don’t read or offer".
191
+
192
+ >>> bed = open_bed(file_name, properties={
193
+ ... "father" : None, "mother" : None, "sex" : None, "pheno" : None,
194
+ ... "allele_1" : None, "allele_2":None })
195
+ >>> print(bed.iid) # read from file
196
+ ['iid1' 'iid2' 'iid3']
197
+ >>> print(bed.allele_2) # not read and not offered
198
+ None
199
+
200
+ See the :meth:`read` for details of reading batches via slicing and fancy indexing.
201
+
202
+ """
203
+
204
+ def __init__(
205
+ self,
206
+ filepath: Union[str, Path],
207
+ iid_count: Optional[int] = None,
208
+ sid_count: Optional[int] = None,
209
+ properties: Mapping[str, List[Any]] = {},
210
+ count_A1: bool = True,
211
+ num_threads: Optional[int] = None,
212
+ skip_format_check: bool = False,
213
+ fam_filepath: Union[str, Path] = None,
214
+ bim_filepath: Union[str, Path] = None,
215
+ ):
216
+ self.filepath = Path(filepath)
217
+ self.count_A1 = count_A1
218
+ self._num_threads = num_threads
219
+ self.skip_format_check = skip_format_check
220
+ self._fam_filepath = (
221
+ Path(fam_filepath)
222
+ if fam_filepath is not None
223
+ else self.filepath.parent / (self.filepath.stem + ".fam")
224
+ )
225
+ self._bim_filepath = (
226
+ Path(bim_filepath)
227
+ if bim_filepath is not None
228
+ else self.filepath.parent / (self.filepath.stem + ".bim")
229
+ )
230
+
231
+ self.properties_dict, self._counts = open_bed._fix_up_properties(
232
+ properties, iid_count, sid_count, use_fill_sequence=False
233
+ )
234
+ self._iid_range = None
235
+ self._sid_range = None
236
+
237
+ if not self.skip_format_check:
238
+ with open(self.filepath, "rb") as filepointer:
239
+ self._check_file(filepointer)
240
+
241
+ def read(
242
+ self,
243
+ index: Optional[Any] = None,
244
+ dtype: Optional[Union[type, str]] = "float32",
245
+ order: Optional[str] = "F",
246
+ force_python_only: Optional[bool] = False,
247
+ num_threads=None,
248
+ ) -> np.ndarray:
249
+ """
250
+ Read genotype information.
251
+
252
+ Parameters
253
+ ----------
254
+ index:
255
+ An optional expression specifying the individuals (samples) and SNPs
256
+ (variants) to read. (See examples, below).
257
+ Defaults to ``None``, meaning read all.
258
+
259
+ (If index is a tuple, the first component indexes the individuals and the
260
+ second indexes
261
+ the SNPs. If it is not a tuple and not None, it indexes SNPs.)
262
+
263
+ dtype: {'float32' (default), 'float64', 'int8'}, optional
264
+ The desired data-type for the returned array.
265
+ order : {'F','C'}, optional
266
+ The desired memory layout for the returned array.
267
+ Defaults to ``F`` (Fortran order, which is SNP-major).
268
+ force_python_only: bool, optional
269
+ If False (default), uses the faster Rust code; otherwise it uses the slower
270
+ pure Python code.
271
+
272
+ num_threads: None or int, optional
273
+ The number of threads with which to read data. Defaults to all available
274
+ processors.
275
+ Can also be set with :class:`open_bed` or these
276
+ environment variables (listed in priority order):
277
+ 'PST_NUM_THREADS', 'NUM_THREADS', 'MKL_NUM_THREADS'.
278
+
279
+
280
+ Returns
281
+ -------
282
+ numpy.ndarray
283
+ 2-D array containing values of 0, 1, 2, or missing
284
+
285
+
286
+ Rows represent individuals (samples). Columns represent SNPs (variants).
287
+
288
+ For ``dtype`` 'float32' and 'float64', NaN indicates missing values.
289
+ For 'int8', -127 indicates missing values.
290
+
291
+ Examples
292
+ --------
293
+
294
+ To read all data in a .bed file, set ``index`` to ``None``. This is the default.
295
+
296
+ .. doctest::
297
+
298
+ >>> from bed_reader import open_bed, sample_file
299
+ >>>
300
+ >>> file_name = sample_file("small.bed")
301
+ >>> with open_bed(file_name) as bed:
302
+ ... print(bed.read())
303
+ [[ 1. 0. nan 0.]
304
+ [ 2. 0. nan 2.]
305
+ [ 0. 1. 2. 0.]]
306
+
307
+ To read selected individuals (samples) and/or SNPs (variants), set each part of
308
+ a :class:`numpy.s_` to an `int`, a list of `int`, a slice expression, or
309
+ a list of `bool`.
310
+ Negative integers count from the end of the list.
311
+
312
+
313
+ .. doctest::
314
+
315
+ >>> import numpy as np
316
+ >>> bed = open_bed(file_name)
317
+ >>> print(bed.read(np.s_[:,2])) # read the SNPs indexed by 2.
318
+ [[nan]
319
+ [nan]
320
+ [ 2.]]
321
+ >>> print(bed.read(np.s_[:,[2,3,0]])) # read the SNPs indexed by 2, 3, and 0
322
+ [[nan 0. 1.]
323
+ [nan 2. 2.]
324
+ [ 2. 0. 0.]]
325
+ >>> # read SNPs from 1 (inclusive) to 4 (exclusive)
326
+ >>> print(bed.read(np.s_[:,1:4]))
327
+ [[ 0. nan 0.]
328
+ [ 0. nan 2.]
329
+ [ 1. 2. 0.]]
330
+ >>> print(np.unique(bed.chromosome)) # print unique chrom values
331
+ ['1' '5' 'Y']
332
+ >>> print(bed.read(np.s_[:,bed.chromosome=='5'])) # read all SNPs in chrom 5
333
+ [[nan]
334
+ [nan]
335
+ [ 2.]]
336
+ >>> print(bed.read(np.s_[0,:])) # Read 1st individual (across all SNPs)
337
+ [[ 1. 0. nan 0.]]
338
+ >>> print(bed.read(np.s_[::2,:])) # Read every 2nd individual
339
+ [[ 1. 0. nan 0.]
340
+ [ 0. 1. 2. 0.]]
341
+ >>> #read last and 2nd-to-last individuals and the last SNPs
342
+ >>> print(bed.read(np.s_[[-1,-2],-1]))
343
+ [[0.]
344
+ [2.]]
345
+
346
+
347
+ You can give a dtype for the output.
348
+
349
+ .. doctest::
350
+
351
+ >>> print(bed.read(dtype='int8'))
352
+ [[ 1 0 -127 0]
353
+ [ 2 0 -127 2]
354
+ [ 0 1 2 0]]
355
+ >>> del bed # optional: delete bed object
356
+
357
+ """
358
+
359
+ iid_index_or_slice_etc, sid_index_or_slice_etc = self._split_index(index)
360
+
361
+ dtype = np.dtype(dtype)
362
+ if order not in {"F", "C"}:
363
+ raise ValueError(f"order '{order}' not known, only 'F', 'C'")
364
+
365
+ # Later happy with _iid_range and _sid_range or could it be done with
366
+ # allocation them?
367
+ if self._iid_range is None:
368
+ self._iid_range = np.arange(self.iid_count, dtype="intp")
369
+ if self._sid_range is None:
370
+ self._sid_range = np.arange(self.sid_count, dtype="intp")
371
+
372
+ iid_index = np.ascontiguousarray(
373
+ self._iid_range[iid_index_or_slice_etc],
374
+ dtype="intp",
375
+ )
376
+ sid_index = np.ascontiguousarray(
377
+ self._sid_range[sid_index_or_slice_etc], dtype="intp"
378
+ )
379
+
380
+ if not force_python_only:
381
+ num_threads = get_num_threads(
382
+ self._num_threads if num_threads is None else num_threads
383
+ )
384
+
385
+ val = np.zeros((len(iid_index), len(sid_index)), order=order, dtype=dtype)
386
+
387
+ if self.iid_count > 0 and self.sid_count > 0:
388
+ if dtype == np.int8:
389
+ reader = read_i8
390
+ elif dtype == np.float64:
391
+ reader = read_f64
392
+ elif dtype == np.float32:
393
+ reader = read_f32
394
+ else:
395
+ raise ValueError(
396
+ f"dtype '{val.dtype}' not known, only "
397
+ + "'int8', 'float32', and 'float64' are allowed."
398
+ )
399
+
400
+ reader(
401
+ str(self.filepath),
402
+ iid_count=self.iid_count,
403
+ sid_count=self.sid_count,
404
+ is_a1_counted=self.count_A1,
405
+ iid_index=iid_index,
406
+ sid_index=sid_index,
407
+ val=val,
408
+ num_threads=num_threads,
409
+ )
410
+
411
+ else:
412
+ if not self.count_A1:
413
+ byteZero = 0
414
+ byteThree = 2
415
+ else:
416
+ byteZero = 2
417
+ byteThree = 0
418
+ if dtype == np.int8:
419
+ missing = -127
420
+ else:
421
+ missing = np.nan
422
+ # An earlier version of this code had a way to read consecutive SNPs of code
423
+ # in one read. May want
424
+ # to add that ability back to the code.
425
+ # Also, note that reading with python will often result in
426
+ # non-contiguous memory
427
+ # logging.warn("using pure python plink parser (might be much slower!!)")
428
+ val = np.zeros(
429
+ ((int(np.ceil(0.25 * self.iid_count)) * 4), len(sid_index)),
430
+ order=order,
431
+ dtype=dtype,
432
+ ) # allocate it a little big
433
+
434
+ nbyte = int(np.ceil(0.25 * self.iid_count))
435
+ with open(self.filepath, "rb") as filepointer:
436
+ for SNPsIndex, bimIndex in enumerate(sid_index):
437
+ startbit = int(np.ceil(0.25 * self.iid_count) * bimIndex + 3)
438
+ filepointer.seek(startbit)
439
+ bytes = np.array(bytearray(filepointer.read(nbyte))).reshape(
440
+ (int(np.ceil(0.25 * self.iid_count)), 1), order="F"
441
+ )
442
+
443
+ val[3::4, SNPsIndex : SNPsIndex + 1] = byteZero
444
+ val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 64] = missing
445
+ val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 128] = 1
446
+ val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 192] = byteThree
447
+ bytes = np.mod(bytes, 64)
448
+ val[2::4, SNPsIndex : SNPsIndex + 1] = byteZero
449
+ val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 16] = missing
450
+ val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 32] = 1
451
+ val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 48] = byteThree
452
+ bytes = np.mod(bytes, 16)
453
+ val[1::4, SNPsIndex : SNPsIndex + 1] = byteZero
454
+ val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 4] = missing
455
+ val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 8] = 1
456
+ val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 12] = byteThree
457
+ bytes = np.mod(bytes, 4)
458
+ val[0::4, SNPsIndex : SNPsIndex + 1] = byteZero
459
+ val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 1] = missing
460
+ val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 2] = 1
461
+ val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 3] = byteThree
462
+ val = val[iid_index, :] # reorder or trim any extra allocation
463
+ assert val.dtype == np.dtype(dtype) # real assert
464
+ if not open_bed._array_properties_are_ok(val, order):
465
+ val = val.copy(order=order)
466
+
467
+ return val
468
+
469
+ def __str__(self) -> str:
470
+ return f"{self.__class__.__name__}('{self.filepath}',...)"
471
+
472
+ @property
473
+ def fid(self) -> np.ndarray:
474
+ """
475
+ Family id of each individual (sample).
476
+
477
+ Returns
478
+ -------
479
+ numpy.ndarray
480
+ array of str
481
+
482
+
483
+ '0' represents a missing value.
484
+
485
+ If needed, will cause a one-time read of the .fam file.
486
+
487
+ Example
488
+ -------
489
+
490
+ .. doctest::
491
+
492
+ >>> from bed_reader import open_bed, sample_file
493
+ >>>
494
+ >>> file_name = sample_file("small.bed")
495
+ >>> with open_bed(file_name) as bed:
496
+ ... print(bed.fid)
497
+ ['fid1' 'fid1' 'fid2']
498
+
499
+ """
500
+
501
+ return self.property_item("fid")
502
+
503
+ @property
504
+ def iid(self) -> np.ndarray:
505
+ """
506
+ Individual id of each individual (sample).
507
+
508
+ Returns
509
+ -------
510
+ numpy.ndarray
511
+ array of str
512
+
513
+
514
+ If needed, will cause a one-time read of the .fam file.
515
+
516
+ Example
517
+ -------
518
+
519
+ .. doctest::
520
+
521
+ >>> from bed_reader import open_bed, sample_file
522
+ >>>
523
+ >>> file_name = sample_file("small.bed")
524
+ >>> with open_bed(file_name) as bed:
525
+ ... print(bed.iid)
526
+ ['iid1' 'iid2' 'iid3']
527
+
528
+ """
529
+ return self.property_item("iid")
530
+
531
+ @property
532
+ def father(self) -> np.ndarray:
533
+ """
534
+ Father id of each individual (sample).
535
+
536
+ Returns
537
+ -------
538
+ numpy.ndarray
539
+ array of str
540
+
541
+
542
+ '0' represents a missing value.
543
+
544
+ If needed, will cause a one-time read of the .fam file.
545
+
546
+ Example
547
+ -------
548
+
549
+ .. doctest::
550
+
551
+ >>> from bed_reader import open_bed, sample_file
552
+ >>>
553
+ >>> file_name = sample_file("small.bed")
554
+ >>> with open_bed(file_name) as bed:
555
+ ... print(bed.father)
556
+ ['iid23' 'iid23' 'iid22']
557
+
558
+ """
559
+ return self.property_item("father")
560
+
561
+ @property
562
+ def mother(self) -> np.ndarray:
563
+ """
564
+ Mother id of each individual (sample).
565
+
566
+ Returns
567
+ -------
568
+ numpy.ndarray
569
+ array of str
570
+
571
+
572
+ '0' represents a missing value.
573
+
574
+ If needed, will cause a one-time read of the .fam file.
575
+
576
+ Example
577
+ -------
578
+
579
+ .. doctest::
580
+
581
+ >>> from bed_reader import open_bed, sample_file
582
+ >>>
583
+ >>> file_name = sample_file("small.bed")
584
+ >>> with open_bed(file_name) as bed:
585
+ ... print(bed.mother)
586
+ ['iid34' 'iid34' 'iid33']
587
+
588
+ """
589
+ return self.property_item("mother")
590
+
591
+ @property
592
+ def sex(self) -> np.ndarray:
593
+ """
594
+ Sex of each individual (sample).
595
+
596
+ Returns
597
+ -------
598
+ numpy.ndarray
599
+ array of 0, 1, or 2
600
+
601
+
602
+ 0 is unknown, 1 is male, 2 is female
603
+
604
+ If needed, will cause a one-time read of the .fam file.
605
+
606
+ Example
607
+ -------
608
+ .. doctest::
609
+
610
+ >>> from bed_reader import open_bed, sample_file
611
+ >>>
612
+ >>> file_name = sample_file("small.bed")
613
+ >>> with open_bed(file_name) as bed:
614
+ ... print(bed.sex)
615
+ [1 2 0]
616
+
617
+ """
618
+ return self.property_item("sex")
619
+
620
+ @property
621
+ def pheno(self) -> np.ndarray:
622
+ """
623
+ A phenotype for each individual (sample)
624
+ (seldom used).
625
+
626
+ Returns
627
+ -------
628
+ numpy.ndarray
629
+ array of str
630
+
631
+
632
+ '0' may represent a missing value.
633
+
634
+ If needed, will cause a one-time read of the .fam file.
635
+
636
+ Example
637
+ -------
638
+
639
+ .. doctest::
640
+
641
+ >>> from bed_reader import open_bed, sample_file
642
+ >>>
643
+ >>> file_name = sample_file("small.bed")
644
+ >>> with open_bed(file_name) as bed:
645
+ ... print(bed.pheno)
646
+ ['red' 'red' 'blue']
647
+
648
+ """
649
+ return self.property_item("pheno")
650
+
651
+ @property
652
+ def properties(self) -> Mapping[str, np.array]:
653
+ """
654
+ All the properties returned as a dictionary.
655
+
656
+ Returns
657
+ -------
658
+ dict
659
+ all the properties
660
+
661
+
662
+ The keys of the dictionary are the names of the properties, namely:
663
+
664
+ "fid" (family id), "iid" (individual or sample id), "father" (father id),
665
+ "mother" (mother id), "sex", "pheno" (phenotype), "chromosome", "sid"
666
+ (SNP or variant id), "cm_position" (centimorgan position), "bp_position"
667
+ (base-pair position), "allele_1", "allele_2".
668
+
669
+ The values are :class:`numpy.ndarray`.
670
+
671
+ If needed, will cause a one-time read of the .fam and .bim file.
672
+
673
+ Example
674
+ -------
675
+
676
+ .. doctest::
677
+
678
+ >>> from bed_reader import open_bed, sample_file
679
+ >>>
680
+ >>> file_name = sample_file("small.bed")
681
+ >>> with open_bed(file_name) as bed:
682
+ ... print(len(bed.properties)) #length of dict
683
+ 12
684
+
685
+ """
686
+ for key in _meta_meta:
687
+ self.property_item(key)
688
+ return self.properties_dict
689
+
690
+ def property_item(self, name: str) -> np.ndarray:
691
+ """
692
+ Retrieve one property by name.
693
+
694
+ Returns
695
+ -------
696
+ numpy.ndarray
697
+ a property value
698
+
699
+
700
+ The name is one of these:
701
+
702
+ "fid" (family id), "iid" (individual or sample id), "father" (father id),
703
+ "mother" (mother id), "sex", "pheno" (phenotype), "chromosome", "sid"
704
+ (SNP or variant id), "cm_position" (centimorgan position), "bp_position"
705
+ (base-pair position), "allele_1", "allele_2".
706
+
707
+ If needed, will cause a one-time read of the .fam or .bim file.
708
+
709
+ Example
710
+ -------
711
+
712
+ .. doctest::
713
+
714
+ >>> from bed_reader import open_bed, sample_file
715
+ >>>
716
+ >>> file_name = sample_file("small.bed")
717
+ >>> with open_bed(file_name) as bed:
718
+ ... print(bed.property_item('chromosome'))
719
+ ['1' '1' '5' 'Y']
720
+
721
+ """
722
+ if name not in self.properties_dict:
723
+ mm = _meta_meta[name]
724
+ self._read_fam_or_bim(suffix=mm.suffix)
725
+ return self.properties_dict[name]
726
+
727
+ @property
728
+ def chromosome(self) -> np.ndarray:
729
+ """
730
+ Chromosome of each SNP (variant)
731
+
732
+ Returns
733
+ -------
734
+ numpy.ndarray
735
+ array of str
736
+
737
+
738
+ '0' represents a missing value.
739
+
740
+ If needed, will cause a one-time read of the .bim file.
741
+
742
+ Example
743
+ -------
744
+
745
+ .. doctest::
746
+
747
+ >>> from bed_reader import open_bed, sample_file
748
+ >>>
749
+ >>> file_name = sample_file("small.bed")
750
+ >>> with open_bed(file_name) as bed:
751
+ ... print(bed.chromosome)
752
+ ['1' '1' '5' 'Y']
753
+
754
+ """
755
+ return self.property_item("chromosome")
756
+
757
+ @property
758
+ def sid(self) -> np.ndarray:
759
+ """
760
+ SNP id of each SNP (variant).
761
+
762
+ Returns
763
+ -------
764
+ numpy.ndarray
765
+ array of str
766
+
767
+
768
+ If needed, will cause a one-time read of the .bim file.
769
+
770
+ Example
771
+ -------
772
+
773
+ .. doctest::
774
+
775
+ >>> from bed_reader import open_bed, sample_file
776
+ >>>
777
+ >>> file_name = sample_file("small.bed")
778
+ >>> with open_bed(file_name) as bed:
779
+ ... print(bed.sid)
780
+ ['sid1' 'sid2' 'sid3' 'sid4']
781
+
782
+ """
783
+ return self.property_item("sid")
784
+
785
+ @property
786
+ def cm_position(self) -> np.ndarray:
787
+ """
788
+ Centimorgan position of each SNP (variant).
789
+
790
+ Returns
791
+ -------
792
+ numpy.ndarray
793
+ array of float
794
+
795
+
796
+ 0.0 represents a missing value.
797
+
798
+ If needed, will cause a one-time read of the .bim file.
799
+
800
+ Example
801
+ -------
802
+
803
+ .. doctest::
804
+
805
+ >>> from bed_reader import open_bed, sample_file
806
+ >>>
807
+ >>> file_name = sample_file("small.bed")
808
+ >>> with open_bed(file_name) as bed:
809
+ ... print(bed.cm_position)
810
+ [ 100.4 2000.5 4000.7 7000.9]
811
+
812
+ """
813
+ return self.property_item("cm_position")
814
+
815
+ @property
816
+ def bp_position(self) -> np.ndarray:
817
+ """
818
+ Base-pair position of each SNP (variant).
819
+
820
+ Returns
821
+ -------
822
+ numpy.ndarray
823
+ array of int
824
+
825
+
826
+ 0 represents a missing value.
827
+
828
+ If needed, will cause a one-time read of the .bim file.
829
+
830
+ Example
831
+ -------
832
+
833
+ .. doctest::
834
+
835
+ >>> from bed_reader import open_bed, sample_file
836
+ >>>
837
+ >>> file_name = sample_file("small.bed")
838
+ >>> with open_bed(file_name) as bed:
839
+ ... print(bed.bp_position)
840
+ [ 1 100 1000 1004]
841
+
842
+ """
843
+ return self.property_item("bp_position")
844
+
845
+ @property
846
+ def allele_1(self) -> np.ndarray:
847
+ """
848
+ First allele of each SNP (variant).
849
+
850
+ Returns
851
+ -------
852
+ numpy.ndarray
853
+ array of str
854
+
855
+
856
+ If needed, will cause a one-time read of the .bim file.
857
+
858
+ Example
859
+ -------
860
+
861
+ .. doctest::
862
+
863
+ >>> from bed_reader import open_bed, sample_file
864
+ >>>
865
+ >>> file_name = sample_file("small.bed")
866
+ >>> with open_bed(file_name) as bed:
867
+ ... print(bed.allele_1)
868
+ ['A' 'T' 'A' 'T']
869
+
870
+ """
871
+ return self.property_item("allele_1")
872
+
873
+ @property
874
+ def allele_2(self) -> np.ndarray:
875
+ """
876
+ Second allele of each SNP (variant),
877
+
878
+ Returns
879
+ -------
880
+ numpy.ndarray
881
+ array of str
882
+
883
+
884
+ If needed, will cause a one-time read of the .bim file.
885
+
886
+ Example
887
+ -------
888
+
889
+ .. doctest::
890
+
891
+ >>> from bed_reader import open_bed, sample_file
892
+ >>>
893
+ >>> file_name = sample_file("small.bed")
894
+ >>> with open_bed(file_name) as bed:
895
+ ... print(bed.allele_2)
896
+ ['A' 'C' 'C' 'G']
897
+
898
+ """
899
+ return self.property_item("allele_2")
900
+
901
+ @property
902
+ def iid_count(self) -> np.ndarray:
903
+ """
904
+ Number of individuals (samples).
905
+
906
+ Returns
907
+ -------
908
+ int
909
+ number of individuals
910
+
911
+
912
+ If needed, will cause a fast line-count of the .fam file.
913
+
914
+ Example
915
+ -------
916
+
917
+ .. doctest::
918
+
919
+ >>> from bed_reader import open_bed, sample_file
920
+ >>>
921
+ >>> file_name = sample_file("small.bed")
922
+ >>> with open_bed(file_name) as bed:
923
+ ... print(bed.iid_count)
924
+ 3
925
+
926
+ """
927
+ return self._count("fam")
928
+
929
+ @property
930
+ def sid_count(self) -> np.ndarray:
931
+ """
932
+ Number of SNPs (variants).
933
+
934
+ Returns
935
+ -------
936
+ int
937
+ number of SNPs
938
+
939
+
940
+ If needed, will cause a fast line-count of the .bim file.
941
+
942
+ Example
943
+ -------
944
+
945
+ .. doctest::
946
+
947
+ >>> from bed_reader import open_bed, sample_file
948
+ >>>
949
+ >>> file_name = sample_file("small.bed")
950
+ >>> with open_bed(file_name) as bed:
951
+ ... print(bed.sid_count)
952
+ 4
953
+
954
+ """
955
+ return self._count("bim")
956
+
957
+ def _property_filepath(self, suffix):
958
+ if suffix == "fam":
959
+ return self._fam_filepath
960
+ else:
961
+ assert suffix == "bim" # real assert
962
+ return self._bim_filepath
963
+
964
+ def _count(self, suffix):
965
+ count = self._counts[suffix]
966
+ if count is None:
967
+ count = _rawincount(self._property_filepath(suffix))
968
+ self._counts[suffix] = count
969
+ return count
970
+
971
+ @staticmethod
972
+ def _check_file(filepointer):
973
+ mode = filepointer.read(2)
974
+ if mode != b"l\x1b":
975
+ raise ValueError("Not a valid .bed file")
976
+ mode = filepointer.read(1) # \x01 = SNP major \x00 = individual major
977
+ if mode != b"\x01":
978
+ raise ValueError("only SNP-major is implemented")
979
+
980
+ def __del__(self):
981
+ self.__exit__()
982
+
983
+ def __enter__(self):
984
+ return self
985
+
986
+ def __exit__(self, *_):
987
+ pass
988
+
989
+ @staticmethod
990
+ def _array_properties_are_ok(val, order):
991
+ if order == "F":
992
+ return val.flags["F_CONTIGUOUS"]
993
+ else:
994
+ assert order == "C" # real assert
995
+ return val.flags["C_CONTIGUOUS"]
996
+
997
+ @property
998
+ def shape(self):
999
+ """
1000
+ Number of individuals (samples) and SNPs (variants).
1001
+
1002
+ Returns
1003
+ -------
1004
+ (int, int)
1005
+ number of individuals, number of SNPs
1006
+
1007
+
1008
+ If needed, will cause a fast line-count of the .fam and .bim files.
1009
+
1010
+ Example
1011
+ -------
1012
+
1013
+ .. doctest::
1014
+
1015
+ >>> from bed_reader import open_bed, sample_file
1016
+ >>>
1017
+ >>> file_name = sample_file("small.bed")
1018
+ >>> with open_bed(file_name) as bed:
1019
+ ... print(bed.shape)
1020
+ (3, 4)
1021
+
1022
+ """
1023
+ return (len(self.iid), len(self.sid))
1024
+
1025
+ @staticmethod
1026
+ def _split_index(index):
1027
+ if not isinstance(index, tuple):
1028
+ index = (None, index)
1029
+ iid_index = open_bed._fix_up_index(index[0])
1030
+ sid_index = open_bed._fix_up_index(index[1])
1031
+ return iid_index, sid_index
1032
+
1033
+ @staticmethod
1034
+ def _fix_up_index(index):
1035
+ if index is None: # make a shortcut for None
1036
+ return slice(None)
1037
+ try: # If index is an int, return it in an array
1038
+ index = index.__index__() # (see
1039
+ # https://stackoverflow.com/questions/3501382/checking-whether-a-variable-is-an-integer-or-not)
1040
+ return [index]
1041
+ except Exception:
1042
+ pass
1043
+ return index
1044
+
1045
+ @staticmethod
1046
+ def _write_fam_or_bim(base_filepath, properties, suffix, property_filepath):
1047
+ assert suffix in {"fam", "bim"}, "real assert"
1048
+
1049
+ filepath = (
1050
+ Path(property_filepath)
1051
+ if property_filepath is not None
1052
+ else base_filepath.parent / (base_filepath.stem + "." + suffix)
1053
+ )
1054
+
1055
+ fam_bim_list = []
1056
+ for key, mm in _meta_meta.items():
1057
+ if mm.suffix == suffix:
1058
+ assert len(fam_bim_list) == mm.column, "real assert"
1059
+ fam_bim_list.append(properties[key])
1060
+
1061
+ sep = " " if suffix == "fam" else "\t"
1062
+
1063
+ with open(filepath, "w") as filepointer:
1064
+ for index in range(len(fam_bim_list[0])):
1065
+ filepointer.write(
1066
+ sep.join(str(seq[index]) for seq in fam_bim_list) + "\n"
1067
+ )
1068
+
1069
+ @staticmethod
1070
+ def _fix_up_properties_array(input, dtype, missing_value, key):
1071
+ if input is None:
1072
+ return None
1073
+ if len(input) == 0:
1074
+ return np.zeros([0], dtype=dtype)
1075
+
1076
+ if not isinstance(input, np.ndarray):
1077
+ return open_bed._fix_up_properties_array(
1078
+ np.array(input), dtype, missing_value, key
1079
+ )
1080
+
1081
+ if len(input.shape) != 1:
1082
+ raise ValueError(f"{key} should be one dimensional")
1083
+
1084
+ if not np.issubdtype(input.dtype, dtype):
1085
+ output = np.array(input, dtype=dtype)
1086
+ else:
1087
+ output = input
1088
+
1089
+ # Change NaN in input to correct missing value
1090
+ if np.issubdtype(input.dtype, np.floating):
1091
+ output[input != input] = missing_value
1092
+
1093
+ return output
1094
+
1095
+ @staticmethod
1096
+ def _fix_up_properties(properties, iid_count, sid_count, use_fill_sequence):
1097
+ for key in properties:
1098
+ if key not in _meta_meta:
1099
+ raise KeyError(f"properties key '{key}' not known")
1100
+
1101
+ count_dict = {"fam": iid_count, "bim": sid_count}
1102
+ properties_dict = {}
1103
+ for key, mm in _meta_meta.items():
1104
+ count = count_dict[mm.suffix]
1105
+
1106
+ if key not in properties or (use_fill_sequence and properties[key] is None):
1107
+ if use_fill_sequence:
1108
+ output = mm.fill_sequence(key, count, mm.missing_value, mm.dtype)
1109
+ else:
1110
+ continue # Test coverage reaches this, but doesn't report it.
1111
+ else:
1112
+ output = open_bed._fix_up_properties_array(
1113
+ properties[key], mm.dtype, mm.missing_value, key
1114
+ )
1115
+
1116
+ if output is not None:
1117
+ if count is None:
1118
+ count_dict[mm.suffix] = len(output)
1119
+ else:
1120
+ if count != len(output):
1121
+ raise ValueError(
1122
+ f"The length of override {key}, {len(output)}, should not "
1123
+ + "be different from the current "
1124
+ + f"{_count_name[mm.suffix]}, {count}"
1125
+ )
1126
+ properties_dict[key] = output
1127
+ return properties_dict, count_dict
1128
+
1129
+ def _read_fam_or_bim(self, suffix):
1130
+ property_filepath = self._property_filepath(suffix)
1131
+
1132
+ logging.info("Loading {0} file {1}".format(suffix, property_filepath))
1133
+
1134
+ count = self._counts[suffix]
1135
+
1136
+ delimiter = _delimiters[suffix]
1137
+ if delimiter in {r"\s+"}:
1138
+ delimiter = None
1139
+ delim_whitespace = True
1140
+ else:
1141
+ delim_whitespace = False
1142
+
1143
+ usecolsdict = {}
1144
+ dtype_dict = {}
1145
+ for key, mm in _meta_meta.items():
1146
+ if mm.suffix is suffix and key not in self.properties_dict:
1147
+ usecolsdict[key] = mm.column
1148
+ dtype_dict[mm.column] = mm.dtype
1149
+ assert list(usecolsdict.values()) == sorted(usecolsdict.values()) # real assert
1150
+ assert len(usecolsdict) > 0 # real assert
1151
+
1152
+ if os.path.getsize(property_filepath) == 0:
1153
+ fields = []
1154
+ else:
1155
+ fields = pd.read_csv(
1156
+ property_filepath,
1157
+ delimiter=delimiter,
1158
+ delim_whitespace=delim_whitespace,
1159
+ header=None,
1160
+ index_col=False,
1161
+ comment=None,
1162
+ dtype=dtype_dict,
1163
+ usecols=usecolsdict.values(),
1164
+ )
1165
+
1166
+ if count is None:
1167
+ self._counts[suffix] = len(fields)
1168
+ else:
1169
+ if count != len(fields):
1170
+ raise ValueError(
1171
+ f"The number of lines in the *.{suffix} file, {len(fields)}, "
1172
+ + "should not be different from the current "
1173
+ + "f{_count_name[suffix]}, {count}"
1174
+ )
1175
+ for key in usecolsdict.keys():
1176
+ mm = _meta_meta[key]
1177
+ if len(fields) == 0:
1178
+ output = np.array([], dtype=mm.dtype)
1179
+ else:
1180
+ output = fields[mm.column].values
1181
+ if not np.issubdtype(output.dtype, mm.dtype):
1182
+ output = np.array(output, dtype=mm.dtype)
1183
+ self.properties_dict[key] = output
1184
+
1185
+
1186
+ if __name__ == "__main__":
1187
+ logging.basicConfig(level=logging.INFO)
1188
+
1189
+ # if True:
1190
+ # from bed_reader import sample_file
1191
+
1192
+ # file_name = sample_file("small.bed")
1193
+ # with open_bed(file_name) as bed:
1194
+ # print(bed.iid)
1195
+ # print(bed.sid)
1196
+ # print(bed.read())
1197
+
1198
+ # if False:
1199
+ # import numpy as np
1200
+ # from bed_reader._open_bed import open_bed
1201
+
1202
+ # # Can get file from
1203
+ # https://www.dropbox.com/sh/xluk9opjiaobteg/AABgEggLk0ZoO0KQq0I4CaTJa?dl=0
1204
+ # bigfile = r"M:\deldir\genbgen\2\merged_487400x220000.1.bed"
1205
+ # # bigfile = '/mnt/m/deldir/genbgen/2/merged_487400x220000.1.bed'
1206
+ # with open_bed(bigfile, num_threads=20) as bed:
1207
+ # sid_batch = 22 * 1000
1208
+ # for sid_start in range(0, 10 * sid_batch, sid_batch):
1209
+ # slicer = np.s_[:10000, sid_start : sid_start + sid_batch]
1210
+ # print(slicer)
1211
+ # val = bed.read(slicer)
1212
+ # print(val.shape)
1213
+
1214
+ # if False:
1215
+ # file = r"D:\OneDrive\programs\sgkit-plink\bed_reader\tests\data
1216
+ # /plink_sim_10s_100v_10pmiss.bed"
1217
+ # with open_bed(file) as bed:
1218
+ # print(bed.iid)
1219
+ # print(bed.shape)
1220
+ # val = bed.read()
1221
+ # print(val)
1222
+
1223
+ # if False:
1224
+
1225
+ # # bed_file = example_file('doc/ipynb/all.*','*.bed')
1226
+ # bed_file = r"F:\backup\carlk4d\data\carlk\cachebio\genetics\onemil\
1227
+ # id1000000.sid_1000000.seed0.byiid\iid990000to1000000.bed"
1228
+ # bed = Bed(bed_file, count_A1=False)
1229
+ # snpdata1 = bed[:, :1000].read()
1230
+ # snpdata2 = bed[:, :1000].read(dtype="int8", _require_float32_64=False)
1231
+ # print(snpdata2)
1232
+ # snpdata3 = bed[:, :1000].read(
1233
+ # dtype="int8", order="C", _require_float32_64=False
1234
+ # )
1235
+ # print(snpdata3)
1236
+ # snpdata3.val = snpdata3.val.astype("float32")
1237
+ # snpdata3.val.dtype
1238
+
1239
+ # if False:
1240
+ # from bed_reader import Bed, SnpGen
1241
+
1242
+ # iid_count = 487409
1243
+ # sid_count = 5000
1244
+ # sid_count_max = 5765294
1245
+ # sid_batch_size = 50
1246
+
1247
+ # sid_batch_count = -(sid_count // -sid_batch_size)
1248
+ # sid_batch_count_max = -(sid_count_max // -sid_batch_size)
1249
+ # snpgen = SnpGen(seed=234, iid_count=iid_count, sid_count=sid_count_max)
1250
+
1251
+ # for batch_index in range(sid_batch_count):
1252
+ # sid_index_start = batch_index * sid_batch_size
1253
+ # sid_index_end = (batch_index + 1) * sid_batch_size # what about rounding
1254
+ # filename = r"d:\deldir\rand\fakeukC{0}x{1}-{2}.bed".format(
1255
+ # iid_count, sid_index_start, sid_index_end
1256
+ # )
1257
+ # if not os.path.exists(filename):
1258
+ # Bed.write(
1259
+ # filename + ".temp", snpgen[:, sid_index_start:sid_index_end].read()
1260
+ # )
1261
+ # os.rename(filename + ".temp", filename)
1262
+
1263
+ # if False:
1264
+ # from bed_reader import Pheno, Bed
1265
+
1266
+ # filename = r"m:\deldir\New folder (4)\all_chr.maf0.001.N300.bed"
1267
+ # iid_count = 300
1268
+ # iid = [["0", "iid_{0}".format(iid_index)] for iid_index in range(iid_count)]
1269
+ # bed = Bed(filename, iid=iid, count_A1=False)
1270
+ # print(bed.iid_count)
1271
+
1272
+ # if False:
1273
+ # from pysnptools.util import example_file
1274
+
1275
+ # pheno_fn = example_file("pysnptools/examples/toydata.phe")
1276
+
1277
+ # if False:
1278
+ # from bed_reader import Pheno, Bed
1279
+
1280
+ # print(os.getcwd())
1281
+ # # Read data from Pheno format
1282
+ # snpdata = Pheno("../examples/toydata.phe").read()
1283
+ # # pstutil.create_directory_if_necessary("tempdir/toydata.5chrom.bed")
1284
+ # Bed.write(
1285
+ # "tempdir/toydata.5chrom.bed", snpdata, count_A1=False
1286
+ # ) # Write data in Bed format
1287
+
1288
+ import pytest
1289
+
1290
+ pytest.main(["--doctest-modules", __file__])