bed-reader 1.0.2__tar.gz → 1.0.3__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 (106) hide show
  1. {bed_reader-1.0.2 → bed_reader-1.0.3}/CHANGELOG.md +7 -1
  2. {bed_reader-1.0.2 → bed_reader-1.0.3}/Cargo.lock +1 -1
  3. {bed_reader-1.0.2 → bed_reader-1.0.3}/Cargo.toml +1 -1
  4. {bed_reader-1.0.2 → bed_reader-1.0.3}/PKG-INFO +2 -2
  5. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/__init__.py +2 -1
  6. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/_open_bed.py +106 -30
  7. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/_sample_data.py +1 -1
  8. bed_reader-1.0.3/bed_reader/_to_bed.py +592 -0
  9. bed_reader-1.0.3/bed_reader/tests/benchmark/bench2.ipynb +251 -0
  10. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/benchmark/benchmark.py +29 -11
  11. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/test_open_bed.py +538 -16
  12. {bed_reader-1.0.2 → bed_reader-1.0.3}/pyproject.toml +1 -1
  13. {bed_reader-1.0.2 → bed_reader-1.0.3}/requirements-dev.txt +1 -1
  14. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/lib.rs +227 -21
  15. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/python_module.rs +62 -1
  16. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/tests.rs +20 -0
  17. {bed_reader-1.0.2 → bed_reader-1.0.3}/useful.md +18 -0
  18. bed_reader-1.0.2/bed_reader/_to_bed.py +0 -235
  19. {bed_reader-1.0.2 → bed_reader-1.0.3}/.flake8 +0 -0
  20. {bed_reader-1.0.2 → bed_reader-1.0.3}/.github/workflows/ci.yml +0 -0
  21. {bed_reader-1.0.2 → bed_reader-1.0.3}/.gitignore +0 -0
  22. {bed_reader-1.0.2 → bed_reader-1.0.3}/.isort.cfg +0 -0
  23. {bed_reader-1.0.2 → bed_reader-1.0.3}/AUTHORS.txt +0 -0
  24. {bed_reader-1.0.2 → bed_reader-1.0.3}/LICENSE.md +0 -0
  25. {bed_reader-1.0.2 → bed_reader-1.0.3}/README-rust.md +0 -0
  26. {bed_reader-1.0.2 → bed_reader-1.0.3}/README.md +0 -0
  27. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/_sphinx_javascript_frameworks_compat.js +0 -0
  28. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/basic.css +0 -0
  29. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/doctools.js +0 -0
  30. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/documentation_options.js +0 -0
  31. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/file.png +0 -0
  32. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/jquery-3.4.1.js +0 -0
  33. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/jquery-3.5.1.js +0 -0
  34. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/jquery-3.6.0.js +0 -0
  35. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/jquery.js +0 -0
  36. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/js/badge_only.js +0 -0
  37. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/js/html5shiv-printshiv.min.js +0 -0
  38. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/js/html5shiv.min.js +0 -0
  39. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/js/modernizr.min.js +0 -0
  40. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/js/theme.js +0 -0
  41. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/language_data.js +0 -0
  42. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/minus.png +0 -0
  43. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/plus.png +0 -0
  44. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/pygments.css +0 -0
  45. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/searchtools.js +0 -0
  46. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/sphinx_highlight.js +0 -0
  47. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/underscore-1.12.0.js +0 -0
  48. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/underscore-1.13.1.js +0 -0
  49. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/underscore-1.3.1.js +0 -0
  50. {bed_reader-1.0.2 → bed_reader-1.0.3}/_static/underscore.js +0 -0
  51. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/__init__.py +0 -0
  52. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/500x400_o640_array.memmap +0 -0
  53. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/badfile.bed +0 -0
  54. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/empty.bed +0 -0
  55. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/plink_sim_10s_100v_10pmiss.bed +0 -0
  56. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/plink_sim_10s_100v_10pmiss.bim +0 -0
  57. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/plink_sim_10s_100v_10pmiss.fam +0 -0
  58. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.bad_bim +0 -0
  59. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.bed +0 -0
  60. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.bim +0 -0
  61. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.bim_bad_positions.bim +0 -0
  62. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.deb +0 -0
  63. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.fam +0 -0
  64. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.fam_bad +0 -0
  65. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.maf +0 -0
  66. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small.mib +0 -0
  67. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small2_array.memmap +0 -0
  68. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_array.memmap +0 -0
  69. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_no_bim.bed +0 -0
  70. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_no_bim.fam +0 -0
  71. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_no_fam.bed +0 -0
  72. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_no_fam.bim +0 -0
  73. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_too_short.bed +0 -0
  74. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_too_short.bim +0 -0
  75. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/small_too_short.fam +0 -0
  76. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/smallmode0.bed +0 -0
  77. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/smallmode0.bim +0 -0
  78. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/smallmode0.fam +0 -0
  79. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/smallmodebad.bed +0 -0
  80. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/some_missing.bed +0 -0
  81. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/some_missing.bim +0 -0
  82. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/some_missing.fam +0 -0
  83. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/some_missing.properties.npz +0 -0
  84. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/some_missing.val.npy +0 -0
  85. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/sparse.bed +0 -0
  86. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/sparse.bim +0 -0
  87. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/sparse.fam +0 -0
  88. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/toydata.5chrom.bed +0 -0
  89. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/toydata.5chrom.bim +0 -0
  90. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/data/toydata.5chrom.fam +0 -0
  91. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/registry.txt +0 -0
  92. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/test_file_dot.py +0 -0
  93. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/test_open_bed_cloud.py +0 -0
  94. {bed_reader-1.0.2 → bed_reader-1.0.3}/bed_reader/tests/test_opt_dep.py +0 -0
  95. {bed_reader-1.0.2 → bed_reader-1.0.3}/build.rs +0 -0
  96. {bed_reader-1.0.2 → bed_reader-1.0.3}/examples/cloud.rs +0 -0
  97. {bed_reader-1.0.2 → bed_reader-1.0.3}/examples/no_cloud.rs +0 -0
  98. {bed_reader-1.0.2 → bed_reader-1.0.3}/pytest.ini +0 -0
  99. {bed_reader-1.0.2 → bed_reader-1.0.3}/requirements-min-dev.txt +0 -0
  100. {bed_reader-1.0.2 → bed_reader-1.0.3}/requirements.txt +0 -0
  101. {bed_reader-1.0.2 → bed_reader-1.0.3}/setpath.bat +0 -0
  102. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/bed_cloud.rs +0 -0
  103. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/supplemental_documents/cloud_urls_etc.md +0 -0
  104. {bed_reader-1.0.2 → bed_reader-1.0.3}/src/supplemental_documents/options_etc.md +0 -0
  105. {bed_reader-1.0.2 → bed_reader-1.0.3}/tests/tests_api.rs +0 -0
  106. {bed_reader-1.0.2 → bed_reader-1.0.3}/tests/tests_api_cloud.rs +0 -0
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.0.1] - 2024-4-16
8
+ ## [1.0.2] - 2024-4-17
9
+
10
+ - On the Python file, add `create-bed`, a method for writing large
11
+ bed files SNP-by-SNP (or individual-by-individual).
12
+ Also, add full support for the less common individual-major files.
13
+
14
+ ## [1.0.1] - 2024-3-16
9
15
 
10
16
  - Add support for cloud files to both Rust and Python.
11
17
 
@@ -136,7 +136,7 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
136
136
 
137
137
  [[package]]
138
138
  name = "bed-reader"
139
- version = "1.0.2"
139
+ version = "1.0.3"
140
140
  dependencies = [
141
141
  "anyhow",
142
142
  "anyinput",
@@ -1,5 +1,5 @@
1
1
  [package]
2
- version = "1.0.2"
2
+ version = "1.0.3"
3
3
  name = "bed-reader"
4
4
  description = "Read and write the PLINK BED format, simply and efficiently."
5
5
  repository = "https://github.com/fastlmm/bed-reader"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: bed-reader
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Classifier: Programming Language :: Python :: 3.8
5
5
  Classifier: Programming Language :: Python :: 3.9
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -7,9 +7,10 @@
7
7
 
8
8
  from bed_reader._open_bed import get_num_threads, open_bed
9
9
  from bed_reader._sample_data import sample_file, sample_url, tmp_path
10
- from bed_reader._to_bed import to_bed
10
+ from bed_reader._to_bed import create_bed, to_bed
11
11
 
12
12
  from .bed_reader import (
13
+ encode1_i8,
13
14
  file_aat_piece_f32_orderf,
14
15
  file_aat_piece_f64_orderf,
15
16
  file_ata_piece_f32_orderf,
@@ -301,7 +301,7 @@ class open_bed:
301
301
  check_file_cloud(self.location.geturl(), self.cloud_options)
302
302
  else:
303
303
  with open(self.location, "rb") as filepointer:
304
- self._check_file(filepointer)
304
+ self._mode = self._check_file(filepointer)
305
305
 
306
306
  # # its an error to set both location and filepath
307
307
  # location = self._combined(location, filepath, "location", "filepath")
@@ -547,6 +547,7 @@ class open_bed:
547
547
  )
548
548
 
549
549
  else:
550
+
550
551
  if not self.count_A1:
551
552
  byteZero = 0
552
553
  byteThree = 2
@@ -563,45 +564,77 @@ class open_bed:
563
564
  # Also, note that reading with python will often result in
564
565
  # non-contiguous memory
565
566
  # logging.warn("using pure python plink parser (might be much slower!!)")
567
+
568
+ if self.major == "SNP":
569
+ minor_count = self.iid_count
570
+ minor_index = iid_index
571
+ major_index = sid_index
572
+ else:
573
+ minor_count = self.sid_count
574
+ minor_index = sid_index
575
+ major_index = iid_index
576
+
566
577
  val = np.zeros(
567
- ((int(np.ceil(0.25 * self.iid_count)) * 4), len(sid_index)),
578
+ ((int(np.ceil(0.25 * minor_count)) * 4), len(major_index)),
568
579
  order=order,
569
580
  dtype=dtype,
570
581
  ) # allocate it a little big
571
582
 
572
- nbyte = int(np.ceil(0.25 * self.iid_count))
583
+ nbyte = int(np.ceil(0.25 * minor_count))
573
584
  with open(self.location, "rb") as filepointer:
574
- for SNPsIndex, bimIndex in enumerate(sid_index):
575
- startbit = int(np.ceil(0.25 * self.iid_count) * bimIndex + 3)
585
+ for major_index_value, major_index_index in enumerate(major_index):
586
+ startbit = int(np.ceil(0.25 * minor_count) * major_index_index + 3)
576
587
  filepointer.seek(startbit)
577
588
  bytes = np.array(bytearray(filepointer.read(nbyte))).reshape(
578
- (int(np.ceil(0.25 * self.iid_count)), 1), order="F"
589
+ (int(np.ceil(0.25 * minor_count)), 1), order="F"
579
590
  )
580
591
 
581
- val[3::4, SNPsIndex : SNPsIndex + 1] = byteZero
582
- val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 64] = missing
583
- val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 128] = 1
584
- val[3::4, SNPsIndex : SNPsIndex + 1][bytes >= 192] = byteThree
592
+ val[3::4, major_index_value : major_index_value + 1] = byteZero
593
+ val[3::4, major_index_value : major_index_value + 1][
594
+ bytes >= 64
595
+ ] = missing
596
+ val[3::4, major_index_value : major_index_value + 1][
597
+ bytes >= 128
598
+ ] = 1
599
+ val[3::4, major_index_value : major_index_value + 1][
600
+ bytes >= 192
601
+ ] = byteThree
585
602
  bytes = np.mod(bytes, 64)
586
- val[2::4, SNPsIndex : SNPsIndex + 1] = byteZero
587
- val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 16] = missing
588
- val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 32] = 1
589
- val[2::4, SNPsIndex : SNPsIndex + 1][bytes >= 48] = byteThree
603
+ val[2::4, major_index_value : major_index_value + 1] = byteZero
604
+ val[2::4, major_index_value : major_index_value + 1][
605
+ bytes >= 16
606
+ ] = missing
607
+ val[2::4, major_index_value : major_index_value + 1][
608
+ bytes >= 32
609
+ ] = 1
610
+ val[2::4, major_index_value : major_index_value + 1][
611
+ bytes >= 48
612
+ ] = byteThree
590
613
  bytes = np.mod(bytes, 16)
591
- val[1::4, SNPsIndex : SNPsIndex + 1] = byteZero
592
- val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 4] = missing
593
- val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 8] = 1
594
- val[1::4, SNPsIndex : SNPsIndex + 1][bytes >= 12] = byteThree
614
+ val[1::4, major_index_value : major_index_value + 1] = byteZero
615
+ val[1::4, major_index_value : major_index_value + 1][
616
+ bytes >= 4
617
+ ] = missing
618
+ val[1::4, major_index_value : major_index_value + 1][bytes >= 8] = 1
619
+ val[1::4, major_index_value : major_index_value + 1][
620
+ bytes >= 12
621
+ ] = byteThree
595
622
  bytes = np.mod(bytes, 4)
596
- val[0::4, SNPsIndex : SNPsIndex + 1] = byteZero
597
- val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 1] = missing
598
- val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 2] = 1
599
- val[0::4, SNPsIndex : SNPsIndex + 1][bytes >= 3] = byteThree
600
- val = val[iid_index, :] # reorder or trim any extra allocation
623
+ val[0::4, major_index_value : major_index_value + 1] = byteZero
624
+ val[0::4, major_index_value : major_index_value + 1][
625
+ bytes >= 1
626
+ ] = missing
627
+ val[0::4, major_index_value : major_index_value + 1][bytes >= 2] = 1
628
+ val[0::4, major_index_value : major_index_value + 1][
629
+ bytes >= 3
630
+ ] = byteThree
631
+ val = val[minor_index, :] # reorder or trim any extra allocation
601
632
  assert val.dtype == np.dtype(dtype) # real assert
602
633
  if not open_bed._array_properties_are_ok(val, order):
603
634
  val = val.copy(order=order)
604
-
635
+ # if in force python mode, and individual-major mode, then we need to transpose
636
+ if self.major == "individual":
637
+ val = val.T
605
638
  return val
606
639
 
607
640
  def _pick_reader(self, dtype):
@@ -633,6 +666,47 @@ class open_bed:
633
666
  def __str__(self) -> str:
634
667
  return f"{self.__class__.__name__}('{self.location}',...)"
635
668
 
669
+ @property
670
+ def major(self) -> str:
671
+ """
672
+ Major mode of a local .bed file.
673
+
674
+ Returns
675
+ -------
676
+ str
677
+ 'SNP' or 'individual'
678
+
679
+
680
+ Almost all PLINK 1.9 .bed files are 'SNP' major. This makes
681
+ reading the data by SNP(s) fast.
682
+
683
+ Errors
684
+ ------
685
+ ValueError
686
+ If the file is a cloud file.
687
+
688
+ Example
689
+ -------
690
+
691
+ .. doctest::
692
+
693
+ >>> from bed_reader import open_bed, sample_file
694
+ >>>
695
+ >>> file_name = sample_file("small.bed")
696
+ >>> with open_bed(file_name) as bed:
697
+ ... print(bed.major)
698
+ SNP
699
+
700
+ """
701
+ if self._is_url(self.location):
702
+ raise ValueError("Cannot determine major mode for cloud files")
703
+ # if self._mode is not set, set it
704
+ if not hasattr(self, "mode"):
705
+ with open(self.location, "rb") as filepointer:
706
+ self._mode = self._check_file(filepointer)
707
+
708
+ return "individual" if self._mode == b"\x00" else "SNP"
709
+
636
710
  @property
637
711
  def fid(self) -> np.ndarray:
638
712
  """
@@ -1158,12 +1232,14 @@ class open_bed:
1158
1232
 
1159
1233
  @staticmethod
1160
1234
  def _check_file(filepointer):
1161
- mode = filepointer.read(2)
1162
- if mode != b"l\x1b":
1235
+ magic_number = filepointer.read(2)
1236
+ if magic_number != b"l\x1b":
1237
+ raise ValueError("Not a valid .bed file")
1238
+ mode = filepointer.read(1)
1239
+ # Check if mode is either individual-major or SNP-major
1240
+ if mode not in (b"\x00", b"\x01"):
1163
1241
  raise ValueError("Not a valid .bed file")
1164
- mode = filepointer.read(1) # \x01 = SNP major \x00 = individual major
1165
- if mode != b"\x01":
1166
- raise ValueError("only SNP-major is implemented")
1242
+ return mode
1167
1243
 
1168
1244
  def __del__(self):
1169
1245
  self.__exit__()
@@ -1389,7 +1465,7 @@ class open_bed:
1389
1465
  num_threads=None,
1390
1466
  max_concurrent_requests=None,
1391
1467
  max_chunk_bytes=None,
1392
- ) -> (Union[sparse.csc_matrix, sparse.csr_matrix]) if sparse is not None else None:
1468
+ ) -> (Union[sparse.csc_matrix, sparse.csr_matrix]) if sparse is not None else None: # type: ignore
1393
1469
  """
1394
1470
  Read genotype information into a :mod:`scipy.sparse` matrix. Sparse matrices
1395
1471
  may be useful when the data is mostly zeros.
@@ -71,7 +71,7 @@ def sample_file(filepath: Union[str, Path]) -> str:
71
71
  if pooch is None:
72
72
  raise ImportError(
73
73
  "The function sample_file() requires pooch. "
74
- + "Install it with 'pip install --upgrade bed-reader[sample]'."
74
+ + "Install it with 'pip install --upgrade bed-reader[samples]'."
75
75
  )
76
76
 
77
77
  filepath = Path(filepath)