biocutils 0.3.1.dev0__tar.gz → 0.3.2__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 (109) hide show
  1. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/CHANGELOG.md +9 -1
  2. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/PKG-INFO +1 -1
  3. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/setup.py +1 -0
  4. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/__init__.py +2 -1
  5. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/factor.py +56 -2
  6. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/float_list.py +1 -1
  7. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/integer_list.py +1 -1
  8. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/named_list.py +106 -2
  9. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/names.py +49 -0
  10. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/show_as_cell.py +1 -1
  11. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/which.py +1 -1
  12. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/PKG-INFO +1 -1
  13. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_BooleanList.py +2 -2
  14. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_Factor.py +55 -4
  15. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_FloatList.py +1 -1
  16. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_IntegerList.py +1 -1
  17. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_NamedList.py +75 -0
  18. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_Names.py +62 -1
  19. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_StringList.py +2 -2
  20. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_biocobject.py +13 -13
  21. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_sequences.py +1 -1
  22. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_factorize.py +1 -1
  23. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.coveragerc +0 -0
  24. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.github/workflows/publish-pypi.yml +0 -0
  25. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.github/workflows/run-tests.yml +0 -0
  26. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.gitignore +0 -0
  27. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.pre-commit-config.yaml +0 -0
  28. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.readthedocs.yml +0 -0
  29. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/AUTHORS.md +0 -0
  30. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/CONTRIBUTING.md +0 -0
  31. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/LICENSE.txt +0 -0
  32. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/README.md +0 -0
  33. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/Makefile +0 -0
  34. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/_static/.gitignore +0 -0
  35. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/authors.md +0 -0
  36. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/changelog.md +0 -0
  37. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/conf.py +0 -0
  38. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/contributing.md +0 -0
  39. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/index.md +0 -0
  40. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/license.md +0 -0
  41. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/readme.md +0 -0
  42. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/requirements.txt +0 -0
  43. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/pyproject.toml +0 -0
  44. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/setup.cfg +0 -0
  45. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/_utils_combine.py +0 -0
  46. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign.py +0 -0
  47. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign_rows.py +0 -0
  48. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign_sequence.py +0 -0
  49. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/bioc_object.py +0 -0
  50. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/boolean_list.py +0 -0
  51. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine.py +0 -0
  52. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_columns.py +0 -0
  53. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_rows.py +0 -0
  54. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_sequences.py +0 -0
  55. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/convert_to_dense.py +0 -0
  56. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/extract_column_names.py +0 -0
  57. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/extract_row_names.py +0 -0
  58. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/factorize.py +0 -0
  59. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/get_height.py +0 -0
  60. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/intersect.py +0 -0
  61. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_high_dimensional.py +0 -0
  62. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_list_of_type.py +0 -0
  63. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_missing_scalar.py +0 -0
  64. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/map_to_index.py +0 -0
  65. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/match.py +0 -0
  66. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/normalize_subscript.py +0 -0
  67. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/package_utils.py +0 -0
  68. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/print_truncated.py +0 -0
  69. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/print_wrapped_table.py +0 -0
  70. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/relaxed_combine_columns.py +0 -0
  71. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/relaxed_combine_rows.py +0 -0
  72. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/reverse_index.py +0 -0
  73. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/string_list.py +0 -0
  74. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset.py +0 -0
  75. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset_rows.py +0 -0
  76. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset_sequence.py +0 -0
  77. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/table.py +0 -0
  78. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/union.py +0 -0
  79. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/SOURCES.txt +0 -0
  80. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/dependency_links.txt +0 -0
  81. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/not-zip-safe +0 -0
  82. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/requires.txt +0 -0
  83. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/top_level.txt +0 -0
  84. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/conftest.py +0 -0
  85. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign.py +0 -0
  86. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign_rows.py +0 -0
  87. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign_sequence.py +0 -0
  88. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine.py +0 -0
  89. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_columns.py +0 -0
  90. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_rows.py +0 -0
  91. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_extract_column_names.py +0 -0
  92. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_extract_row_names.py +0 -0
  93. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_get_height.py +0 -0
  94. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_intersect.py +0 -0
  95. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_is_high_dimensional.py +0 -0
  96. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_list_type_checks.py +0 -0
  97. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_map_to_index.py +0 -0
  98. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_match.py +0 -0
  99. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_normalize_subscript.py +0 -0
  100. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_package_utils.py +0 -0
  101. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_print_truncated.py +0 -0
  102. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_print_wrapped_table.py +0 -0
  103. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_show_as_cell.py +0 -0
  104. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset.py +0 -0
  105. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset_rows.py +0 -0
  106. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset_sequence.py +0 -0
  107. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_union.py +0 -0
  108. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_which.py +0 -0
  109. {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tox.ini +0 -0
@@ -1,8 +1,16 @@
1
1
  # Changelog
2
2
 
3
- ## Version 0.3.0
3
+ ## Version 0.3.0 - 0.3.2
4
4
 
5
5
  - Provide a base `BiocObject` class similar to the `Annotated` class in Bioconductor. The class provides `metadata` slot, accessors and validation functions.
6
+ - Renaming code files to follow pep guidelines
7
+ - Update Github actions and workflow to the new biocsetup versions
8
+ - Changes to improve `NamedList`, `Names` classes
9
+ - get name at index
10
+ - delete method for namedlist/names
11
+ - add is_unique
12
+ - add lint errors
13
+ - linting documentation, typehints etc
6
14
 
7
15
  ## Version 0.2.3
8
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biocutils
3
- Version: 0.3.1.dev0
3
+ Version: 0.3.2
4
4
  Summary: Utilities to use across the biocpy packages.
5
5
  Home-page: https://github.com/biocpy/biocutils
6
6
  Author: Aaron Lun
@@ -4,6 +4,7 @@ This file was generated with PyScaffold 4.5.
4
4
  PyScaffold helps you to put up the scaffold of your new Python project.
5
5
  Learn more under: https://pyscaffold.org/
6
6
  """
7
+
7
8
  from setuptools import setup
8
9
 
9
10
  if __name__ == "__main__":
@@ -61,4 +61,5 @@ from .convert_to_dense import convert_to_dense
61
61
  from .get_height import get_height
62
62
  from .is_high_dimensional import is_high_dimensional
63
63
 
64
- from .bioc_object import BiocObject
64
+ from .bioc_object import BiocObject
65
+ from .table import table
@@ -424,8 +424,8 @@ class Factor:
424
424
  output._codes[index] = -1
425
425
  return output
426
426
 
427
- for i, l in enumerate(output._levels):
428
- if l == value:
427
+ for i, lev in enumerate(output._levels):
428
+ if lev == value:
429
429
  output._codes[index] = i
430
430
  return output
431
431
 
@@ -764,6 +764,60 @@ class Factor:
764
764
  levels, indices = factorize(x, levels=levels, sort_levels=sort_levels, **kwargs)
765
765
  return Factor(indices, levels=levels, ordered=ordered, names=names)
766
766
 
767
+ ################################
768
+ #####>>>> List methods <<<<#####
769
+ ################################
770
+
771
+ def as_list(self) -> list:
772
+ """
773
+ Returns:
774
+ List of strings corresponding to the factor elements.
775
+ Missing values are represented as None.
776
+ """
777
+ return [self._levels[c] if c >= 0 else None for c in self._codes]
778
+
779
+ def safe_delete(self, index: Union[int, str, slice], in_place: bool = False) -> Factor:
780
+ """
781
+ Args:
782
+ index:
783
+ Integer index or slice containing position(s) to delete.
784
+ Alternatively, the name of the value to delete (the first
785
+ occurrence of the name is used).
786
+
787
+ in_place:
788
+ Whether to modify the current object in place.
789
+
790
+ Returns:
791
+ A ``Factor`` where the item at ``index`` is removed. This is a
792
+ new object if ``in_place = False``, otherwise it is a reference to
793
+ the current object.
794
+ """
795
+ if in_place:
796
+ output = self
797
+ else:
798
+ output = copy(self)
799
+ output._codes = copy(self._codes)
800
+ if output._names is not None:
801
+ output._names = output._names.copy()
802
+
803
+ if isinstance(index, str):
804
+ index = _name_to_position(output._names, index)
805
+
806
+ output._codes = numpy.delete(output._codes, index)
807
+
808
+ if output._names is not None:
809
+ output._names.delete(index)
810
+
811
+ return output
812
+
813
+ def delete(self, index: Union[int, str, slice]):
814
+ """Alias for :py:meth:`~safe_delete` with ``in_place = True``."""
815
+ self.safe_delete(index, in_place=True)
816
+
817
+ def __delitem__(self, index: Union[int, str, slice]):
818
+ """Alias for :py:meth:`~delete`."""
819
+ self.delete(index)
820
+
767
821
 
768
822
  @subset_sequence.register
769
823
  def _subset_sequence_Factor(x: Factor, indices: Sequence[int]) -> Factor:
@@ -12,7 +12,7 @@ def _coerce_to_float(x: Any):
12
12
  return None
13
13
  try:
14
14
  return float(x)
15
- except:
15
+ except Exception as _:
16
16
  return None
17
17
 
18
18
 
@@ -12,7 +12,7 @@ def _coerce_to_int(x: Any):
12
12
  return None
13
13
  try:
14
14
  return int(x)
15
- except:
15
+ except Exception as _:
16
16
  return None
17
17
 
18
18
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from copy import deepcopy
4
- from typing import Any, Dict, Iterable, Optional, Sequence, Union
4
+ from typing import Any, Dict, Iterable, Optional, Sequence, Tuple, Union
5
5
 
6
6
  from .assign_sequence import assign_sequence
7
7
  from .combine_sequences import combine_sequences
@@ -153,12 +153,27 @@ class NamedList:
153
153
  output._names = _sanitize_names(names, len(self))
154
154
  return output
155
155
 
156
+ def get_name(self, index: int) -> Optional[str]:
157
+ """Get name at an index.
158
+
159
+ Args:
160
+ index:
161
+ Integer index of the element.
162
+ Returns:
163
+ Names for the list elements.
164
+ """
165
+ if self._names is None:
166
+ return None
167
+
168
+ return self._names.get_value(index)
169
+
156
170
  #################################
157
171
  #####>>>> Get/set items <<<<#####
158
172
  #################################
159
173
 
160
174
  def get_value(self, index: Union[str, int]) -> Any:
161
- """
175
+ """Get value at an index.
176
+
162
177
  Args:
163
178
  index:
164
179
  Integer index of the element to obtain. Alternatively, a string
@@ -420,6 +435,95 @@ class NamedList:
420
435
  self.extend(other)
421
436
  return self
422
437
 
438
+ def safe_delete(self, index: Union[int, str, slice], in_place: bool = False) -> NamedList:
439
+ """
440
+ Args:
441
+ index:
442
+ An integer index or slice containing position(s) to delete.
443
+ Alternatively, the name of the value to delete (the first
444
+ occurrence of the name is used).
445
+
446
+ in_place:
447
+ Whether to modify the current object in place.
448
+
449
+ Returns:
450
+ A ``NamedList`` where the item at ``index`` is removed. This is a
451
+ new object if ``in_place = False``, otherwise it is a reference to
452
+ the current object.
453
+ """
454
+ if in_place:
455
+ output = self
456
+ else:
457
+ output = self._shallow_copy()
458
+ output._data = output._data[:] # Shallow copy of the list
459
+ if output._names is not None:
460
+ output._names = output._names.copy()
461
+
462
+ if isinstance(index, str):
463
+ index = _name_to_position(self._names, index)
464
+
465
+ del output._data[index]
466
+ if output._names is not None:
467
+ output._names.delete(index)
468
+
469
+ return output
470
+
471
+ def delete(self, index: Union[int, str, slice]):
472
+ """Alias for :py:meth:`~safe_delete` with ``in_place = True``."""
473
+ self.safe_delete(index, in_place=True)
474
+
475
+ def __delitem__(self, index: Union[int, str, slice]):
476
+ """Alias for :py:meth:`~delete`."""
477
+ self.delete(index)
478
+
479
+ #####################################
480
+ #####>>>> dict like methods <<<<#####
481
+ #####################################
482
+
483
+ def keys(self) -> Iterable[str]:
484
+ """
485
+ Returns:
486
+ Iterator over the names of the list elements.
487
+ """
488
+ if self._names is None:
489
+ return iter([])
490
+ return iter(self._names)
491
+
492
+ def values(self) -> Iterable[Any]:
493
+ """
494
+ Returns:
495
+ Iterator over the values of the list elements.
496
+ """
497
+ return iter(self._data)
498
+
499
+ def items(self) -> Iterable[Tuple[str, Any]]:
500
+ """
501
+ Returns:
502
+ Iterator over (name, value) pairs.
503
+ If names are missing, keys are returned as stringified indices.
504
+ """
505
+ if self._names is not None:
506
+ return zip(self._names, self._data)
507
+ else:
508
+ return zip((str(i) for i in range(len(self))), self._data)
509
+
510
+ def get(self, key: Union[str, int], default: Any = None) -> Any:
511
+ """
512
+ Args:
513
+ key:
514
+ Name or index of the element.
515
+
516
+ default:
517
+ Value to return if ``key`` is not found.
518
+
519
+ Returns:
520
+ Value at ``key`` or ``default``.
521
+ """
522
+ try:
523
+ return self.get_value(key)
524
+ except (KeyError, IndexError):
525
+ return default
526
+
423
527
  ################################
424
528
  #####>>>> Copy methods <<<<#####
425
529
  ################################
@@ -121,6 +121,17 @@ class Names:
121
121
  else:
122
122
  return -1
123
123
 
124
+ def __contains__(self, name: str) -> bool:
125
+ """
126
+ Args:
127
+ name:
128
+ Name to check.
129
+
130
+ Returns:
131
+ True if ``name`` exists, otherwise False.
132
+ """
133
+ return self.map(name) >= 0
134
+
124
135
  #################################
125
136
  #####>>>> Get/set items <<<<#####
126
137
  #################################
@@ -328,6 +339,35 @@ class Names:
328
339
  self.extend(other)
329
340
  return self
330
341
 
342
+ def safe_delete(self, index: Union[int, slice], in_place: bool = False) -> Names:
343
+ """
344
+ Args:
345
+ index:
346
+ Position(s) of the name(s) to delete.
347
+
348
+ in_place:
349
+ Whether to perform this deletion in-place.
350
+
351
+ Returns:
352
+ A ``Names`` object with the deleted name(s). This is a new object
353
+ if ``in_place = False``, otherwise it is a reference to the current
354
+ object.
355
+ """
356
+ output = self._define_output(in_place)
357
+ if in_place:
358
+ output._wipe_reverse_index()
359
+
360
+ del output._names[index]
361
+ return output
362
+
363
+ def delete(self, index: Union[int, slice]):
364
+ """Alias for :py:attr:`~safe_delete` with ``in_place = True``."""
365
+ self.safe_delete(index, in_place=True)
366
+
367
+ def __delitem__(self, index: Union[int, slice]):
368
+ """Alias for :py:attr:`~delete`."""
369
+ self.delete(index)
370
+
331
371
  ################################
332
372
  #####>>>> Copy methods <<<<#####
333
373
  ################################
@@ -359,6 +399,15 @@ class Names:
359
399
  """
360
400
  return type(self)(deepcopy(self._names, memo, _nil), _validate=False)
361
401
 
402
+ @property
403
+ def is_unique(self) -> bool:
404
+ """
405
+ Returns:
406
+ True if all names are unique, otherwise False.
407
+ """
408
+ self._populate_reverse_index()
409
+ return len(self._reverse) == len(self._names)
410
+
362
411
 
363
412
  @subset_sequence.register
364
413
  def _subset_sequence_Names(x: Names, indices: Sequence[int]) -> Names:
@@ -30,6 +30,6 @@ def show_as_cell(x: Any, indices: Sequence[int]) -> List[str]:
30
30
  if nl >= 0:
31
31
  candidate = candidate[:nl] + "..."
32
32
  output.append(candidate)
33
- except:
33
+ except Exception as _:
34
34
  output.append("####")
35
35
  return output
@@ -23,7 +23,7 @@ def which(
23
23
  """
24
24
  if isinstance(x, numpy.ndarray):
25
25
  found = numpy.where(x)[0]
26
- if not dtype is None:
26
+ if dtype is not None:
27
27
  found = found.astype(dtype=dtype, copy=False, order="A")
28
28
  return found
29
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biocutils
3
- Version: 0.3.1.dev0
3
+ Version: 0.3.2
4
4
  Summary: Utilities to use across the biocpy packages.
5
5
  Home-page: https://github.com/biocpy/biocutils
6
6
  Author: Aaron Lun
@@ -44,7 +44,7 @@ def test_BooleanList_setitem():
44
44
  x = BooleanList([False, True, True, False])
45
45
  x[0] = None
46
46
  assert x.as_list() == [None, True, True, False]
47
- x[0] = 12345
47
+ x[0] = 12345
48
48
  assert x.as_list() == [True, True, True, False]
49
49
 
50
50
  x[1:3] = [False, False]
@@ -54,7 +54,7 @@ def test_BooleanList_setitem():
54
54
  assert x.as_list() == [None, False, None, False]
55
55
 
56
56
  x.set_names(["A", "B", "C", "D"], in_place=True)
57
- x["C"] = True
57
+ x["C"] = True
58
58
  assert x.as_list() == [None, False, True, False]
59
59
  x[["A", "B"]] = [False, True]
60
60
  assert x.as_list() == [False, True, True, False]
@@ -17,13 +17,13 @@ def test_factor_init():
17
17
  assert len(f) == 6
18
18
  assert list(f) == ["A", "B", None, "A", None, "E"]
19
19
  assert list(f.get_codes()) == [0, 1, -1, 0, -1, 4]
20
-
20
+
21
21
  f = Factor([None] * 10, levels=["A", "B", "C", "D", "E"])
22
22
  assert list(f) == [None] * 10
23
23
 
24
24
  # Works with NumPy inputs.
25
25
  f = Factor(numpy.array([4,3,2,1,0], dtype=numpy.uint8), levels=numpy.array(["A", "B", "C", "D", "E"]))
26
- assert len(f) == 5
26
+ assert len(f) == 5
27
27
  assert f.get_codes().dtype == numpy.int8
28
28
  assert isinstance(f.get_levels(), StringList)
29
29
 
@@ -98,7 +98,7 @@ def test_Factor_get_value():
98
98
  def test_Factor_get_slice():
99
99
  f = Factor([0, 1, 2, -1, 2, 4], levels=["A", "B", "C", "D", "E"])
100
100
 
101
- sub = f.get_slice([0, 1])
101
+ sub = f.get_slice([0, 1])
102
102
  assert list(sub) == ["A", "B"]
103
103
  assert sub.get_levels() == f.get_levels()
104
104
 
@@ -176,7 +176,7 @@ def test_Factor_setitem():
176
176
  f[-1] = "D"
177
177
  assert list(f.get_codes()) == [1, 1, 0, 0, 2, 3]
178
178
 
179
- f[2:5] = Factor([4, 3, 1], levels=["A", "B", "C", "D", "E"])
179
+ f[2:5] = Factor([4, 3, 1], levels=["A", "B", "C", "D", "E"])
180
180
  assert list(f.get_codes()) == [1, 1, 4, 3, 1, 3]
181
181
  assert f.get_levels() == f.get_levels()
182
182
 
@@ -339,3 +339,54 @@ def test_Factor_init_from_list():
339
339
  assert isinstance(f1, Factor)
340
340
  assert len(f1) == 5
341
341
  assert len(f1.get_levels()) == 3
342
+
343
+ def test_Factor_as_list():
344
+ f = Factor([0, 1, -1, 0], levels=["A", "B"])
345
+ assert f.as_list() == ["A", "B", None, "A"]
346
+
347
+ empty = Factor([], levels=[])
348
+ assert empty.as_list() == []
349
+
350
+
351
+ def test_Factor_safe_delete():
352
+ f = Factor([0, 1, 2, 0], levels=["A", "B", "C"], names=["x", "y", "z", "w"])
353
+
354
+ y = f.safe_delete(1)
355
+ assert y.as_list() == ["A", "C", "A"]
356
+ assert y.get_names().as_list() == ["x", "z", "w"]
357
+ assert f.as_list() == ["A", "B", "C", "A"]
358
+
359
+ y = f.safe_delete("y")
360
+ assert y.as_list() == ["A", "C", "A"]
361
+ assert y.get_names().as_list() == ["x", "z", "w"]
362
+
363
+ y = f.safe_delete(slice(1, 3))
364
+ assert y.as_list() == ["A", "A"]
365
+ assert y.get_names().as_list() == ["x", "w"]
366
+
367
+
368
+ def test_Factor_delete():
369
+ f = Factor([0, 1, 2], levels=["A", "B", "C"], names=["x", "y", "z"])
370
+
371
+ f.delete(1)
372
+ assert f.as_list() == ["A", "C"]
373
+ assert f.get_names().as_list() == ["x", "z"]
374
+
375
+ f.delete("z")
376
+ assert f.as_list() == ["A"]
377
+ assert f.get_names().as_list() == ["x"]
378
+
379
+
380
+ def test_Factor_delitem():
381
+ f = Factor([0, 1, 2, 0], levels=["A", "B", "C"], names=["x", "y", "z", "w"])
382
+
383
+ del f["y"]
384
+ assert f.as_list() == ["A", "C", "A"]
385
+ assert f.get_names().as_list() == ["x", "z", "w"]
386
+
387
+ del f[0]
388
+ assert f.as_list() == ["C", "A"]
389
+ assert f.get_names().as_list() == ["z", "w"]
390
+
391
+ del f[:]
392
+ assert len(f) == 0
@@ -44,7 +44,7 @@ def test_FloatList_setitem():
44
44
  x = FloatList([ 0.5, -2.1, -3.2, -4.5 ])
45
45
  x[0] = None
46
46
  assert x.as_list() == [None, -2.1, -3.2, -4.5]
47
- x[0] = 12345
47
+ x[0] = 12345
48
48
  assert x.as_list() == [12345.0, -2.1, -3.2, -4.5]
49
49
 
50
50
  x[1:3] = [10.1, 20.2]
@@ -44,7 +44,7 @@ def test_IntegerList_setitem():
44
44
  x = IntegerList([1,2,3,4])
45
45
  x[0] = None
46
46
  assert x.as_list() == [None, 2, 3, 4]
47
- x[0] = 12345
47
+ x[0] = 12345
48
48
  assert x.as_list() == [12345, 2, 3, 4]
49
49
 
50
50
  x[1:3] = [10, 20]
@@ -10,6 +10,7 @@ def test_NamedList_init():
10
10
  assert x.as_list() == [ 1,2,3,4 ]
11
11
  assert x.get_names().as_list() == ["a", "b", "c", "d"]
12
12
  assert len(x) == 4
13
+ assert x.get_name(0) == "a"
13
14
 
14
15
  y = NamedList(x)
15
16
  assert y.as_list() == [1,2,3,4]
@@ -23,6 +24,7 @@ def test_NamedList_init():
23
24
  x = NamedList([1,2,3,4])
24
25
  assert x.as_list() == [1,2,3,4]
25
26
  assert x.get_names() is None
27
+ assert x.get_name(1) is None
26
28
 
27
29
 
28
30
  def test_Names_iter():
@@ -255,3 +257,76 @@ def test_NamedList_generics():
255
257
  y = biocutils.assign_sequence(x, [1, 3], NamedList([ 20, 40 ], names=["b", "d" ]))
256
258
  assert y.as_list() == [ 1, 20, 3, 40 ]
257
259
  assert y.get_names().as_list() == [ "A", "B", "C", "D" ] # doesn't set the names, as per policy.
260
+
261
+ def test_NamedList_safe_delete():
262
+ x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
263
+
264
+ y = x.safe_delete(1)
265
+ assert y.as_list() == [1, 3, 4]
266
+ assert y.get_names().as_list() == ["A", "C", "D"]
267
+ assert x.as_list() == [1, 2, 3, 4]
268
+
269
+ y = x.safe_delete("C")
270
+ assert y.as_list() == [1, 2, 4]
271
+ assert y.get_names().as_list() == ["A", "B", "D"]
272
+
273
+ y = x.safe_delete(slice(1, 3))
274
+ assert y.as_list() == [1, 4]
275
+ assert y.get_names().as_list() == ["A", "D"]
276
+
277
+ y = x.safe_delete(-1)
278
+ assert y.as_list() == [1, 2, 3]
279
+ assert y.get_names().as_list() == ["A", "B", "C"]
280
+
281
+
282
+ def test_NamedList_delete():
283
+ x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
284
+
285
+ x.delete(0)
286
+ assert x.as_list() == [2, 3, 4]
287
+ assert x.get_names().as_list() == ["B", "C", "D"]
288
+
289
+ x.delete("D")
290
+ assert x.as_list() == [2, 3]
291
+ assert x.get_names().as_list() == ["B", "C"]
292
+
293
+
294
+ def test_NamedList_delitem():
295
+ x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
296
+
297
+ del x[1]
298
+ assert x.as_list() == [1, 3, 4]
299
+ assert x.get_names().as_list() == ["A", "C", "D"]
300
+
301
+ del x["A"]
302
+ assert x.as_list() == [3, 4]
303
+ assert x.get_names().as_list() == ["C", "D"]
304
+
305
+ x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
306
+ del x[0:2]
307
+ assert x.as_list() == [3, 4]
308
+ assert x.get_names().as_list() == ["C", "D"]
309
+
310
+ with pytest.raises(KeyError):
311
+ del x["Missing"]
312
+
313
+ with pytest.raises(IndexError):
314
+ del x[10]
315
+
316
+ def test_NamedList_dict_methods():
317
+ x = NamedList([1, 2, 3], names=["A", "B", "C"])
318
+
319
+ assert list(x.keys()) == ["A", "B", "C"]
320
+ assert list(x.values()) == [1, 2, 3]
321
+ assert list(x.items()) == [("A", 1), ("B", 2), ("C", 3)]
322
+
323
+ assert x.get("A") == 1
324
+ assert x.get("C") == 3
325
+ assert x.get("Missing") is None
326
+ assert x.get("Missing", 100) == 100
327
+ assert x.get(1) == 2 # Integer index access via get
328
+
329
+ y = NamedList([10, 20])
330
+ assert list(y.keys()) == []
331
+ assert list(y.values()) == [10, 20]
332
+ assert list(y.items()) == [("0", 10), ("1", 20)]
@@ -185,7 +185,7 @@ def test_Names_generics():
185
185
  sub = biocutils.subset_sequence(x, [0,3,2,1])
186
186
  assert isinstance(sub, Names)
187
187
  assert sub.as_list() == ["1", "4", "3", "2"]
188
-
188
+
189
189
  y = ["a", "b", "c", "d"]
190
190
  com = biocutils.combine_sequences(x, y)
191
191
  assert isinstance(com, Names)
@@ -196,3 +196,64 @@ def test_Names_generics():
196
196
  assert isinstance(ass, Names)
197
197
  assert ass.as_list() == ["1", "b", "c", "4"]
198
198
 
199
+ def test_Names_safe_delete():
200
+ x = Names(["A", "B", "C", "D"])
201
+
202
+ y = x.safe_delete(1)
203
+ assert y.as_list() == ["A", "C", "D"]
204
+ assert y.map("B") == -1
205
+ assert y.map("C") == 1
206
+ assert x.as_list() == ["A", "B", "C", "D"]
207
+
208
+ y = x.safe_delete(slice(0, 2))
209
+ assert y.as_list() == ["C", "D"]
210
+ assert y.map("A") == -1
211
+ assert y.map("C") == 0
212
+
213
+
214
+ def test_Names_delete():
215
+ x = Names(["A", "B", "C", "D"])
216
+
217
+ x.delete(2)
218
+ assert x.as_list() == ["A", "B", "D"]
219
+ assert x.map("C") == -1
220
+ assert x.map("D") == 2
221
+
222
+ x.delete(0)
223
+ assert x.as_list() == ["B", "D"]
224
+ assert x.map("A") == -1
225
+ assert x.map("B") == 0
226
+
227
+
228
+ def test_Names_delitem():
229
+ x = Names(["1", "2", "3", "4"])
230
+
231
+ del x[1]
232
+ assert x.as_list() == ["1", "3", "4"]
233
+ assert x.map("2") == -1
234
+ assert x.map("3") == 1
235
+
236
+ del x[0:2]
237
+ assert x.as_list() == ["4"]
238
+ assert x.map("1") == -1
239
+ assert x.map("4") == 0
240
+
241
+ def test_Names_contains():
242
+ x = Names(["A", "B", "C"])
243
+ assert "A" in x
244
+ assert "B" in x
245
+ assert "Z" not in x
246
+
247
+ # Works with duplicates
248
+ y = Names(["A", "A", "B"])
249
+ assert "A" in y
250
+
251
+ def test_Names_is_unique():
252
+ x = Names(["A", "B", "C"])
253
+ assert x.is_unique
254
+
255
+ y = Names(["A", "B", "A"])
256
+ assert not y.is_unique
257
+
258
+ empty = Names([])
259
+ assert empty.is_unique
@@ -44,7 +44,7 @@ def test_StringList_setitem():
44
44
  x = StringList([1,2,3,4])
45
45
  x[0] = None
46
46
  assert x.as_list() == [None, "2", "3", "4"]
47
- x[0] = 12345
47
+ x[0] = 12345
48
48
  assert x.as_list() == ["12345", "2", "3", "4"]
49
49
 
50
50
  x[1:3] = [10, 20]
@@ -89,7 +89,7 @@ def test_StringList_generics():
89
89
  sub = biocutils.subset_sequence(x, [0,3,2,1])
90
90
  assert isinstance(sub, StringList)
91
91
  assert sub.as_list() == ["1", "4", "3", "2"]
92
-
92
+
93
93
  y = ["a", "b", "c", "d"]
94
94
  com = biocutils.combine_sequences(x, y)
95
95
  assert isinstance(com, StringList)
@@ -3,7 +3,7 @@ from copy import copy
3
3
  from biocutils.bioc_object import BiocObject
4
4
  from biocutils.named_list import NamedList
5
5
 
6
-
6
+
7
7
  def test_init_empty():
8
8
  """Test initialization with default values."""
9
9
  obj = BiocObject()
@@ -14,7 +14,7 @@ def test_init_with_dict():
14
14
  """Test initialization with a dictionary."""
15
15
  meta = {"author": "jkanche", "version": 1}
16
16
  obj = BiocObject(metadata=meta)
17
-
17
+
18
18
  assert isinstance(obj.metadata, NamedList)
19
19
  assert len(obj.metadata) == 2
20
20
 
@@ -28,7 +28,7 @@ def test_metadata_property_setter():
28
28
  obj = BiocObject()
29
29
  new_meta = {"tag": "experiment_1"}
30
30
  obj.metadata = new_meta
31
-
31
+
32
32
  assert len(obj.metadata) == 1
33
33
  assert isinstance(obj.metadata, NamedList)
34
34
 
@@ -36,21 +36,21 @@ def test_set_metadata_copy():
36
36
  """Test functional style set_metadata (copy-on-write)."""
37
37
  obj = BiocObject(metadata={"id": 1})
38
38
  original_id = id(obj)
39
-
39
+
40
40
  new_obj = obj.set_metadata({"id": 2})
41
-
41
+
42
42
  assert id(new_obj) != original_id
43
43
  assert len(new_obj.metadata) == 1
44
-
45
- assert len(obj.metadata) == 1
44
+
45
+ assert len(obj.metadata) == 1
46
46
 
47
47
  def test_set_metadata_inplace():
48
48
  """Test imperative style set_metadata (in-place)."""
49
49
  obj = BiocObject(metadata={"id": 1})
50
50
  original_id = id(obj)
51
-
51
+
52
52
  new_obj = obj.set_metadata({"id": 2}, in_place=True)
53
-
53
+
54
54
  assert id(new_obj) == original_id
55
55
  assert new_obj is obj
56
56
  assert len(obj.metadata) == 1
@@ -59,18 +59,18 @@ def test_inheritance():
59
59
  """Test that subclasses maintain their type when copying."""
60
60
  class GenomicContainer(BiocObject):
61
61
  pass
62
-
62
+
63
63
  obj = GenomicContainer(metadata={"genome": "hg38"})
64
64
  new_obj = obj.set_metadata({"genome": "mm10"})
65
-
65
+
66
66
  assert isinstance(new_obj, GenomicContainer)
67
67
  assert new_obj is not obj
68
68
 
69
69
  def test_shallow_copy_behavior():
70
70
  heavy_data = ["large", "data"]
71
-
71
+
72
72
  obj = BiocObject()
73
73
  obj._heavy_data = heavy_data
74
74
  new_obj = obj.set_metadata({"new": "meta"})
75
75
  assert new_obj is not obj
76
- assert new_obj._heavy_data is obj._heavy_data
76
+ assert new_obj._heavy_data is obj._heavy_data
@@ -35,7 +35,7 @@ def test_basic_dense_masked():
35
35
  x = [1, 2, 3]
36
36
  y = [0.1, 0.2]
37
37
  xd = np.array(x)
38
- yd = np.ma.array(y, mask=[True]*2)
38
+ yd = np.ma.array(y, mask=[True]*2)
39
39
 
40
40
  zcomb = combine_sequences(xd, yd)
41
41
  z = x + y
@@ -52,7 +52,7 @@ def test_factorize_sorted():
52
52
  def test_factorize_factor():
53
53
  f = Factor([4, 3, 2, 1, 0], ["A", "B", "C", "D", "E"])
54
54
  lev, ind = factorize(f)
55
- assert lev == ["E", "D", "C", "B", "A"]
55
+ assert lev == ["E", "D", "C", "B", "A"]
56
56
  assert list(ind) == [0, 1, 2, 3, 4]
57
57
 
58
58
  lev, ind = factorize(f, sort_levels=True)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes