biocutils 0.3.2.dev1__tar.gz → 0.3.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 (109) hide show
  1. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/CHANGELOG.md +9 -1
  2. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/PKG-INFO +1 -1
  3. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/setup.py +1 -0
  4. biocutils-0.3.2.dev1/src/biocutils/boolean_list.py → biocutils-0.3.3/src/biocutils/BooleanList.py +24 -27
  5. biocutils-0.3.2.dev1/src/biocutils/factor.py → biocutils-0.3.3/src/biocutils/Factor.py +100 -83
  6. biocutils-0.3.2.dev1/src/biocutils/float_list.py → biocutils-0.3.3/src/biocutils/FloatList.py +13 -18
  7. biocutils-0.3.2.dev1/src/biocutils/integer_list.py → biocutils-0.3.3/src/biocutils/IntegerList.py +16 -20
  8. biocutils-0.3.2.dev1/src/biocutils/named_list.py → biocutils-0.3.3/src/biocutils/NamedList.py +142 -48
  9. biocutils-0.3.2.dev1/src/biocutils/names.py → biocutils-0.3.3/src/biocutils/Names.py +74 -20
  10. biocutils-0.3.2.dev1/src/biocutils/string_list.py → biocutils-0.3.3/src/biocutils/StringList.py +11 -17
  11. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/__init__.py +10 -7
  12. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/_utils_combine.py +2 -0
  13. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/assign.py +2 -1
  14. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/assign_rows.py +3 -3
  15. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/assign_sequence.py +2 -6
  16. biocutils-0.3.2.dev1/src/biocutils/bioc_object.py → biocutils-0.3.3/src/biocutils/biocobject.py +6 -11
  17. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/combine.py +5 -5
  18. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/combine_columns.py +9 -8
  19. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/combine_rows.py +4 -6
  20. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/combine_sequences.py +5 -6
  21. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/convert_to_dense.py +2 -1
  22. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/extract_column_names.py +4 -3
  23. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/extract_row_names.py +2 -2
  24. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/is_high_dimensional.py +2 -1
  25. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/is_list_of_type.py +7 -4
  26. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/is_missing_scalar.py +2 -1
  27. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/map_to_index.py +2 -3
  28. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/match.py +4 -3
  29. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/normalize_subscript.py +7 -17
  30. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/package_utils.py +2 -1
  31. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/print_truncated.py +6 -13
  32. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/print_wrapped_table.py +1 -3
  33. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/relaxed_combine_columns.py +1 -5
  34. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/relaxed_combine_rows.py +1 -3
  35. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/reverse_index.py +2 -1
  36. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/show_as_cell.py +2 -2
  37. biocutils-0.3.3/src/biocutils/table.py +38 -0
  38. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/which.py +2 -1
  39. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/PKG-INFO +1 -1
  40. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/SOURCES.txt +9 -8
  41. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_BooleanList.py +2 -2
  42. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_Factor.py +55 -4
  43. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_FloatList.py +1 -1
  44. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_IntegerList.py +1 -1
  45. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_NamedList.py +75 -0
  46. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_Names.py +62 -1
  47. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_StringList.py +2 -2
  48. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_biocobject.py +15 -15
  49. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_combine_sequences.py +1 -1
  50. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_factorize.py +1 -1
  51. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.coveragerc +0 -0
  52. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.github/workflows/publish-pypi.yml +0 -0
  53. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.github/workflows/run-tests.yml +0 -0
  54. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.gitignore +0 -0
  55. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.pre-commit-config.yaml +0 -0
  56. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/.readthedocs.yml +0 -0
  57. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/AUTHORS.md +0 -0
  58. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/CONTRIBUTING.md +0 -0
  59. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/LICENSE.txt +0 -0
  60. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/README.md +0 -0
  61. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/Makefile +0 -0
  62. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/_static/.gitignore +0 -0
  63. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/authors.md +0 -0
  64. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/changelog.md +0 -0
  65. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/conf.py +0 -0
  66. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/contributing.md +0 -0
  67. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/index.md +0 -0
  68. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/license.md +0 -0
  69. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/readme.md +0 -0
  70. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/docs/requirements.txt +0 -0
  71. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/pyproject.toml +0 -0
  72. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/setup.cfg +0 -0
  73. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/factorize.py +0 -0
  74. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/get_height.py +0 -0
  75. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/intersect.py +0 -0
  76. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/subset.py +0 -0
  77. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/subset_rows.py +0 -0
  78. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/subset_sequence.py +0 -0
  79. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils/union.py +0 -0
  80. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/dependency_links.txt +0 -0
  81. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/not-zip-safe +0 -0
  82. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/requires.txt +0 -0
  83. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/src/biocutils.egg-info/top_level.txt +0 -0
  84. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/conftest.py +0 -0
  85. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_assign.py +0 -0
  86. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_assign_rows.py +0 -0
  87. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_assign_sequence.py +0 -0
  88. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_combine.py +0 -0
  89. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_combine_columns.py +0 -0
  90. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_combine_rows.py +0 -0
  91. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_extract_column_names.py +0 -0
  92. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_extract_row_names.py +0 -0
  93. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_get_height.py +0 -0
  94. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_intersect.py +0 -0
  95. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_is_high_dimensional.py +0 -0
  96. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_list_type_checks.py +0 -0
  97. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_map_to_index.py +0 -0
  98. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_match.py +0 -0
  99. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_normalize_subscript.py +0 -0
  100. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_package_utils.py +0 -0
  101. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_print_truncated.py +0 -0
  102. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_print_wrapped_table.py +0 -0
  103. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_show_as_cell.py +0 -0
  104. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_subset.py +0 -0
  105. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_subset_rows.py +0 -0
  106. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_subset_sequence.py +0 -0
  107. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_union.py +0 -0
  108. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/tests/test_which.py +0 -0
  109. {biocutils-0.3.2.dev1 → biocutils-0.3.3}/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.2.dev1
3
+ Version: 0.3.3
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__":
@@ -1,7 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import Any, Iterable, Optional, Sequence, Union
2
4
 
3
- from .named_list import NamedList
4
- from .names import Names
5
+ from .NamedList import NamedList
6
+ from .Names import Names
5
7
  from .normalize_subscript import SubscriptTypes
6
8
 
7
9
 
@@ -16,7 +18,8 @@ class _SubscriptCoercer:
16
18
  """Initialize the coercer.
17
19
 
18
20
  Args:
19
- data: Sequence of values to coerce.
21
+ data:
22
+ Sequence of values to coerce.
20
23
  """
21
24
  self._data = data
22
25
 
@@ -24,7 +27,8 @@ class _SubscriptCoercer:
24
27
  """Get an item and coerce it to boolean.
25
28
 
26
29
  Args:
27
- index: Index of the item.
30
+ index:
31
+ Index of the item.
28
32
 
29
33
  Returns:
30
34
  Coerced boolean value.
@@ -42,7 +46,7 @@ class BooleanList(NamedList):
42
46
 
43
47
  def __init__(
44
48
  self,
45
- data: Optional[Iterable] = None,
49
+ data: Optional[Sequence] = None,
46
50
  names: Optional[Names] = None,
47
51
  _validate: bool = True,
48
52
  ):
@@ -60,41 +64,34 @@ class BooleanList(NamedList):
60
64
  _validate:
61
65
  Internal use only.
62
66
  """
63
- if _validate:
64
- if data is not None:
65
- if isinstance(data, BooleanList):
67
+ if data is not None:
68
+ if isinstance(data, BooleanList):
69
+ data = data._data
70
+ else:
71
+ if isinstance(data, NamedList):
66
72
  data = data._data
67
- else:
68
- if isinstance(data, NamedList):
69
- data = data._data
70
- original = data
71
- data = list(_coerce_to_bool(item) for item in original)
73
+
74
+ original = data
75
+ data = list(_coerce_to_bool(item) for item in original)
76
+
72
77
  super().__init__(data, names, _validate=_validate)
73
78
 
74
- def set_value(
75
- self, index: Union[int, str], value: Any, in_place: bool = False
76
- ) -> "BooleanList":
79
+ def set_value(self, index: Union[int, str], value: Any, in_place: bool = False) -> BooleanList:
77
80
  """Calls :py:meth:`~biocutils.NamedList.NamedList.set_value` after coercing ``value`` to a boolean."""
78
81
  return super().set_value(index, _coerce_to_bool(value), in_place=in_place)
79
82
 
80
- def set_slice(
81
- self, index: SubscriptTypes, value: Sequence, in_place: bool = False
82
- ) -> "BooleanList":
83
+ def set_slice(self, index: SubscriptTypes, value: Sequence, in_place: bool = False) -> BooleanList:
83
84
  """Calls :py:meth:`~biocutils.NamedList.NamedList.set_slice` after coercing ``value`` to booleans."""
84
85
  return super().set_slice(index, _SubscriptCoercer(value), in_place=in_place)
85
86
 
86
- def safe_insert(
87
- self, index: Union[int, str], value: Any, in_place: bool = False
88
- ) -> "BooleanList":
87
+ def safe_insert(self, index: Union[int, str], value: Any, in_place: bool = False) -> BooleanList:
89
88
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_insert` after coercing ``value`` to a boolean."""
90
89
  return super().safe_insert(index, _coerce_to_bool(value), in_place=in_place)
91
90
 
92
- def safe_append(self, value: Any, in_place: bool = False) -> "BooleanList":
91
+ def safe_append(self, value: Any, in_place: bool = False) -> BooleanList:
93
92
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_append` after coercing ``value`` to a boolean."""
94
93
  return super().safe_append(_coerce_to_bool(value), in_place=in_place)
95
94
 
96
- def safe_extend(self, other: Iterable, in_place: bool = True) -> "BooleanList":
95
+ def safe_extend(self, other: Iterable, in_place: bool = False) -> BooleanList:
97
96
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_extend` after coercing elements of ``other`` to booleans."""
98
- return super().safe_extend(
99
- (_coerce_to_bool(y) for y in other), in_place=in_place
100
- )
97
+ return super().safe_extend((_coerce_to_bool(y) for y in other), in_place=in_place)
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
1
4
  from copy import copy, deepcopy
2
5
  from typing import Optional, Sequence, Union
3
6
 
4
7
  import numpy
5
- import warnings
6
8
 
7
9
  from .assign_sequence import assign_sequence
8
10
  from .combine_sequences import combine_sequences
@@ -10,22 +12,20 @@ from .factorize import factorize
10
12
  from .is_list_of_type import is_list_of_type
11
13
  from .is_missing_scalar import is_missing_scalar
12
14
  from .match import match
13
- from .names import Names, _combine_names, _name_to_position, _sanitize_names
15
+ from .Names import Names, _combine_names, _name_to_position, _sanitize_names
14
16
  from .normalize_subscript import (
15
17
  NormalizedSubscript,
16
18
  SubscriptTypes,
17
19
  normalize_subscript,
18
20
  )
19
21
  from .print_truncated import print_truncated_list
20
- from .string_list import StringList
22
+ from .StringList import StringList
21
23
  from .subset_sequence import subset_sequence
22
24
 
23
25
 
24
26
  def _sanitize_codes(codes: Sequence[int], num_levels: int) -> numpy.ndarray:
25
27
  if not isinstance(codes, numpy.ndarray):
26
- replacement = numpy.ndarray(
27
- len(codes), dtype=numpy.min_scalar_type(-num_levels)
28
- ) # get a signed type.
28
+ replacement = numpy.ndarray(len(codes), dtype=numpy.min_scalar_type(-num_levels)) # get a signed type.
29
29
  for i, x in enumerate(codes):
30
30
  if is_missing_scalar(x) or x < 0:
31
31
  replacement[i] = -1
@@ -35,16 +35,12 @@ def _sanitize_codes(codes: Sequence[int], num_levels: int) -> numpy.ndarray:
35
35
  else:
36
36
  if len(codes.shape) != 1:
37
37
  raise ValueError("'codes' should be a 1-dimensional array")
38
- if not numpy.issubdtype(
39
- codes.dtype, numpy.signedinteger
40
- ): # force it to be signed.
38
+ if not numpy.issubdtype(codes.dtype, numpy.signedinteger): # force it to be signed.
41
39
  codes = codes.astype(numpy.min_scalar_type(-num_levels))
42
40
 
43
41
  for x in codes:
44
42
  if x < -1 or x >= num_levels:
45
- raise ValueError(
46
- "all entries of 'codes' should refer to an entry of 'levels'"
47
- )
43
+ raise ValueError("all entries of 'codes' should refer to an entry of 'levels'")
48
44
 
49
45
  return codes
50
46
 
@@ -67,7 +63,7 @@ def _sanitize_levels(levels: Sequence[str], check: bool = True) -> StringList:
67
63
  class FactorIterator:
68
64
  """Iterator for a :py:class:`~Factor` object."""
69
65
 
70
- def __init__(self, parent: "Factor"):
66
+ def __init__(self, parent: Factor):
71
67
  """
72
68
  Args:
73
69
  parent: The parent :py:class:`~Factor` object.
@@ -75,7 +71,7 @@ class FactorIterator:
75
71
  self._parent = parent
76
72
  self._position = 0
77
73
 
78
- def __iter__(self) -> "FactorIterator":
74
+ def __iter__(self) -> FactorIterator:
79
75
  """
80
76
  Returns:
81
77
  The iterator.
@@ -106,10 +102,10 @@ class Factor:
106
102
 
107
103
  def __init__(
108
104
  self,
109
- codes: Sequence[int],
110
- levels: Sequence[str],
105
+ codes: Union[numpy.ndarray, Sequence[int]],
106
+ levels: Union[StringList, Sequence[str]],
111
107
  ordered: bool = False,
112
- names: Optional[Names] = None,
108
+ names: Optional[Union[Names, Sequence[str]]] = None,
113
109
  _validate: bool = True,
114
110
  ):
115
111
  """Initialize a Factor object.
@@ -149,7 +145,7 @@ class Factor:
149
145
  #####>>>> Simple getters <<<<#####
150
146
  ##################################
151
147
 
152
- def _define_output(self, in_place: bool) -> "Factor":
148
+ def _define_output(self, in_place: bool) -> Factor:
153
149
  if in_place:
154
150
  return self
155
151
  else:
@@ -171,7 +167,7 @@ class Factor:
171
167
  """Alias for :py:meth:`~get_codes`."""
172
168
  return self.get_codes()
173
169
 
174
- def set_codes(self, codes: Sequence[int], in_place: bool = False) -> "Factor":
170
+ def set_codes(self, codes: Sequence[int], in_place: bool = False) -> Factor:
175
171
  """
176
172
  Args:
177
173
  codes:
@@ -187,9 +183,7 @@ class Factor:
187
183
  """
188
184
  output = self._define_output(in_place)
189
185
  if len(codes) != len(self):
190
- raise ValueError(
191
- "length of 'codes' should be equal to that of the current object"
192
- )
186
+ raise ValueError("length of 'codes' should be equal to that of the current object")
193
187
  output._codes = _sanitize_codes(codes, len(self._levels))
194
188
  return output
195
189
 
@@ -220,7 +214,7 @@ class Factor:
220
214
  """Alias for :py:meth:`~get_ordered`."""
221
215
  return self.get_ordered()
222
216
 
223
- def set_ordered(self, ordered: bool, in_place: bool = False) -> "Factor":
217
+ def set_ordered(self, ordered: bool, in_place: bool = False) -> Factor:
224
218
  """
225
219
  Args:
226
220
  ordered:
@@ -293,12 +287,7 @@ class Factor:
293
287
  Returns:
294
288
  A stringified representation of this object.
295
289
  """
296
- tmp = (
297
- "Factor(codes="
298
- + print_truncated_list(self._codes)
299
- + ", levels="
300
- + print_truncated_list(self._levels)
301
- )
290
+ tmp = "Factor(codes=" + print_truncated_list(self._codes) + ", levels=" + print_truncated_list(self._levels)
302
291
  if self._ordered:
303
292
  tmp += ", ordered=True"
304
293
  if self._names:
@@ -311,42 +300,24 @@ class Factor:
311
300
  Returns:
312
301
  A pretty-printed representation of this object.
313
302
  """
314
- message = (
315
- "Factor of length "
316
- + str(len(self._codes))
317
- + " with "
318
- + str(len(self._levels))
319
- + " level"
320
- )
303
+ message = "Factor of length " + str(len(self._codes)) + " with " + str(len(self._levels)) + " level"
321
304
  if len(self._levels) != 0:
322
305
  message += "s"
323
306
  message += "\n"
324
307
  message += (
325
308
  "values: "
326
- + print_truncated_list(
327
- self._codes, transform=lambda i: self._levels[i], include_brackets=False
328
- )
309
+ + print_truncated_list(self._codes, transform=lambda i: self._levels[i], include_brackets=False)
329
310
  + "\n"
330
311
  )
331
312
  if self._names is not None:
332
313
  message += (
333
- "names: "
334
- + print_truncated_list(
335
- self._names, transform=lambda x: x, include_brackets=False
336
- )
337
- + "\n"
338
- )
339
- message += (
340
- "levels: "
341
- + print_truncated_list(
342
- self._levels, transform=lambda x: x, include_brackets=False
314
+ "names: " + print_truncated_list(self._names, transform=lambda x: x, include_brackets=False) + "\n"
343
315
  )
344
- + "\n"
345
- )
316
+ message += "levels: " + print_truncated_list(self._levels, transform=lambda x: x, include_brackets=False) + "\n"
346
317
  message += "ordered: " + str(self._ordered)
347
318
  return message
348
319
 
349
- def __eq__(self, other: "Factor"):
320
+ def __eq__(self, other: Factor):
350
321
  """
351
322
  Args:
352
323
  other: Another ``Factor``.
@@ -357,7 +328,12 @@ class Factor:
357
328
  """
358
329
  if not isinstance(other, Factor):
359
330
  return False
360
- if len(self) != len(other) or self._levels != other._levels or self._names != other._names or self._ordered != other._ordered:
331
+ if (
332
+ len(self) != len(other)
333
+ or self._levels != other._levels
334
+ or self._names != other._names
335
+ or self._ordered != other._ordered
336
+ ):
361
337
  return False
362
338
  return (self._codes == other._codes).all()
363
339
 
@@ -384,7 +360,7 @@ class Factor:
384
360
  return None
385
361
  return self._levels[i]
386
362
 
387
- def get_slice(self, index: SubscriptTypes) -> "Factor":
363
+ def get_slice(self, index: SubscriptTypes) -> Factor:
388
364
  """
389
365
  Args:
390
366
  index:
@@ -404,7 +380,7 @@ class Factor:
404
380
  output._names = subset_sequence(self._names, index)
405
381
  return output
406
382
 
407
- def __getitem__(self, index: SubscriptTypes) -> Union[str, "Factor"]:
383
+ def __getitem__(self, index: SubscriptTypes) -> Union[str, Factor]:
408
384
  """
409
385
  If ``index`` is a scalar, this is an alias for :py:meth:`~get_value`.
410
386
 
@@ -416,9 +392,7 @@ class Factor:
416
392
  else:
417
393
  return self.get_slice(NormalizedSubscript(index))
418
394
 
419
- def set_value(
420
- self, index: Union[str, int], value: Union[str, None], in_place: bool = False
421
- ) -> "Factor":
395
+ def set_value(self, index: Union[str, int], value: Union[str, None], in_place: bool = False) -> Factor:
422
396
  """
423
397
  Args:
424
398
  index:
@@ -450,14 +424,14 @@ class Factor:
450
424
  output._codes[index] = -1
451
425
  return output
452
426
 
453
- for i, l in enumerate(output._levels):
454
- if l == value:
427
+ for i, lev in enumerate(output._levels):
428
+ if lev == value:
455
429
  output._codes[index] = i
456
430
  return output
457
431
 
458
432
  raise IndexError("failed to find level '" + str(value) + "'")
459
433
 
460
- def set_slice(self, index: SubscriptTypes, value: "Factor", in_place: bool = False):
434
+ def set_slice(self, index: SubscriptTypes, value: Factor, in_place: bool = False):
461
435
  """
462
436
  Replace items in the ``Factor`` list. The ``index`` elements in the
463
437
  current object are replaced with the corresponding values in ``value``.
@@ -508,7 +482,7 @@ class Factor:
508
482
 
509
483
  return output
510
484
 
511
- def __setitem__(self, index: SubscriptTypes, value: Union[str, "Factor"]):
485
+ def __setitem__(self, index: SubscriptTypes, value: Union[str, Factor]):
512
486
  """
513
487
  If ``index`` is a scalar, this is an alias for :py:meth:`~set_value`.
514
488
 
@@ -524,7 +498,7 @@ class Factor:
524
498
  #####>>>> Level setting <<<<#####
525
499
  #################################
526
500
 
527
- def drop_unused_levels(self, in_place: bool = False) -> "Factor":
501
+ def drop_unused_levels(self, in_place: bool = False) -> Factor:
528
502
  """Drop unused levels.
529
503
 
530
504
  Args:
@@ -567,7 +541,7 @@ class Factor:
567
541
  self,
568
542
  levels: Sequence[str],
569
543
  in_place: bool = False,
570
- ) -> "Factor":
544
+ ) -> Factor:
571
545
  """Replace the existing levels with a new list. The codes of the
572
546
  returned ``Factor`` are unchanged by this method and will index into
573
547
  the replacement ``levels``, so each element of the ``Factor`` may refer
@@ -614,12 +588,7 @@ class Factor:
614
588
  output._levels = new_levels
615
589
  return output
616
590
 
617
- def set_levels(
618
- self,
619
- levels: Union[str, Sequence[str]],
620
- remap: bool = True,
621
- in_place: bool = False
622
- ) -> "Factor":
591
+ def set_levels(self, levels: Union[str, Sequence[str]], remap: bool = True, in_place: bool = False) -> Factor:
623
592
  """
624
593
  Alias for :py:meth:`~remap_levels` if ``remap = True``, otherwise an
625
594
  alias for :py:meth:`~replace_levels`. The first alias is deprecated and
@@ -631,9 +600,7 @@ class Factor:
631
600
  else:
632
601
  return self.replace_levels(levels, in_place=in_place)
633
602
 
634
- def remap_levels(
635
- self, levels: Union[str, Sequence[str]], in_place: bool = False
636
- ) -> "Factor":
603
+ def remap_levels(self, levels: Union[str, Sequence[str]], in_place: bool = False) -> Factor:
637
604
  """Remap codes to a replacement list of levels. Each entry of the
638
605
  remapped ``Factor`` will refer to the same string across the old and
639
606
  new levels, provided that string is present in both sets of levels.
@@ -679,9 +646,7 @@ class Factor:
679
646
  lmapping[x] = len(new_levels)
680
647
  new_levels.append(x)
681
648
  if levels not in lmapping:
682
- raise ValueError(
683
- "string 'levels' should already be present among object levels"
684
- )
649
+ raise ValueError("string 'levels' should already be present among object levels")
685
650
  else:
686
651
  new_levels = levels
687
652
  if not isinstance(new_levels, StringList):
@@ -712,7 +677,7 @@ class Factor:
712
677
  #####>>>> Copying <<<<#####
713
678
  ###########################
714
679
 
715
- def __copy__(self) -> "Factor":
680
+ def __copy__(self) -> Factor:
716
681
  """
717
682
  Returns:
718
683
  A shallow copy of the ``Factor`` object.
@@ -725,7 +690,7 @@ class Factor:
725
690
  _validate=False,
726
691
  )
727
692
 
728
- def __deepcopy__(self, memo) -> "Factor":
693
+ def __deepcopy__(self, memo) -> Factor:
729
694
  """
730
695
  Returns:
731
696
  A deep copy of the ``Factor`` object.
@@ -762,8 +727,8 @@ class Factor:
762
727
  sort_levels: bool = True,
763
728
  ordered: bool = False,
764
729
  names: Optional[Sequence[str]] = None,
765
- **kwargs
766
- ) -> "Factor":
730
+ **kwargs,
731
+ ) -> Factor:
767
732
  """Convert a sequence of hashable values into a factor.
768
733
 
769
734
  Args:
@@ -799,6 +764,60 @@ class Factor:
799
764
  levels, indices = factorize(x, levels=levels, sort_levels=sort_levels, **kwargs)
800
765
  return Factor(indices, levels=levels, ordered=ordered, names=names)
801
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
+
802
821
 
803
822
  @subset_sequence.register
804
823
  def _subset_sequence_Factor(x: Factor, indices: Sequence[int]) -> Factor:
@@ -841,9 +860,7 @@ def _combine_factors(*x: Factor):
841
860
  new_levels.append(y)
842
861
  mapping.append(all_levels_map[y])
843
862
 
844
- curout = numpy.ndarray(
845
- len(f), dtype=numpy.min_scalar_type(-len(new_levels))
846
- )
863
+ curout = numpy.ndarray(len(f), dtype=numpy.min_scalar_type(-len(new_levels)))
847
864
  for i, j in enumerate(f._codes):
848
865
  if j < 0:
849
866
  curout[i] = j
@@ -1,7 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import Any, Iterable, Optional, Sequence, Union
2
4
 
3
- from .named_list import NamedList
4
- from .names import Names
5
+ from .NamedList import NamedList
6
+ from .Names import Names
5
7
  from .normalize_subscript import SubscriptTypes
6
8
 
7
9
 
@@ -10,7 +12,7 @@ def _coerce_to_float(x: Any):
10
12
  return None
11
13
  try:
12
14
  return float(x)
13
- except:
15
+ except Exception as _:
14
16
  return None
15
17
 
16
18
 
@@ -47,7 +49,7 @@ class FloatList(NamedList):
47
49
 
48
50
  def __init__(
49
51
  self,
50
- data: Optional[Iterable] = None,
52
+ data: Optional[Sequence] = None,
51
53
  names: Optional[Names] = None,
52
54
  _validate: bool = True,
53
55
  ):
@@ -74,32 +76,25 @@ class FloatList(NamedList):
74
76
  data = data._data
75
77
  original = data
76
78
  data = list(_coerce_to_float(item) for item in original)
79
+
77
80
  super().__init__(data, names, _validate=_validate)
78
81
 
79
- def set_value(
80
- self, index: Union[int, str], value: Any, in_place: bool = False
81
- ) -> "FloatList":
82
+ def set_value(self, index: Union[int, str], value: Any, in_place: bool = False) -> FloatList:
82
83
  """Calls :py:meth:`~biocutils.NamedList.NamedList.set_value` after coercing ``value`` to a float."""
83
84
  return super().set_value(index, _coerce_to_float(value), in_place=in_place)
84
85
 
85
- def set_slice(
86
- self, index: SubscriptTypes, value: Sequence, in_place: bool = False
87
- ) -> "FloatList":
86
+ def set_slice(self, index: SubscriptTypes, value: Sequence, in_place: bool = False) -> FloatList:
88
87
  """Calls :py:meth:`~biocutils.NamedList.NamedList.set_slice` after coercing ``value`` to floats."""
89
88
  return super().set_slice(index, _SubscriptCoercer(value), in_place=in_place)
90
89
 
91
- def safe_insert(
92
- self, index: Union[int, str], value: Any, in_place: bool = False
93
- ) -> "FloatList":
90
+ def safe_insert(self, index: Union[int, str], value: Any, in_place: bool = False) -> FloatList:
94
91
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_insert` after coercing ``value`` to a float."""
95
92
  return super().safe_insert(index, _coerce_to_float(value), in_place=in_place)
96
93
 
97
- def safe_append(self, value: Any, in_place: bool = False) -> "FloatList":
94
+ def safe_append(self, value: Any, in_place: bool = False) -> FloatList:
98
95
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_append` after coercing ``value`` to a float."""
99
96
  return super().safe_append(_coerce_to_float(value), in_place=in_place)
100
97
 
101
- def safe_extend(self, other: Iterable, in_place: bool = True) -> "FloatList":
98
+ def safe_extend(self, other: Iterable, in_place: bool = True) -> FloatList:
102
99
  """Calls :py:meth:`~biocutils.NamedList.NamedList.safe_extend` after coercing elements of ``other`` to floats."""
103
- return super().safe_extend(
104
- (_coerce_to_float(y) for y in other), in_place=in_place
105
- )
100
+ return super().safe_extend((_coerce_to_float(y) for y in other), in_place=in_place)