dtypeflow 1.0.0__py3-none-any.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.
- dtypeflow/__init__.py +5 -0
- dtypeflow/core/__init__.py +1 -0
- dtypeflow/core/candidate/__init__.py +40 -0
- dtypeflow/core/candidate/base.py +37 -0
- dtypeflow/core/candidate/factory.py +258 -0
- dtypeflow/core/candidate/primitive.py +79 -0
- dtypeflow/core/dataset.py +46 -0
- dtypeflow/core/dtype/__init__.py +70 -0
- dtypeflow/core/dtype/base.py +14 -0
- dtypeflow/core/dtype/hierachy.md +51 -0
- dtypeflow/core/dtype/nested/__init__.py +5 -0
- dtypeflow/core/dtype/nested/dictionary.py +14 -0
- dtypeflow/core/dtype/primitive/__init__.py +39 -0
- dtypeflow/core/dtype/primitive/base.py +6 -0
- dtypeflow/core/dtype/primitive/boolean.py +20 -0
- dtypeflow/core/dtype/primitive/float.py +40 -0
- dtypeflow/core/dtype/primitive/integer/__init__.py +31 -0
- dtypeflow/core/dtype/primitive/integer/base.py +17 -0
- dtypeflow/core/dtype/primitive/integer/signed.py +37 -0
- dtypeflow/core/dtype/primitive/integer/unsigned.py +37 -0
- dtypeflow/core/dtype/primitive/string.py +14 -0
- dtypeflow/core/override/__init__.py +41 -0
- dtypeflow/core/override/base.py +22 -0
- dtypeflow/core/override/primitive.py +158 -0
- dtypeflow/core/profile/__init__.py +39 -0
- dtypeflow/core/profile/base.py +50 -0
- dtypeflow/core/profile/primitive.py +166 -0
- dtypeflow/core/profiler.py +169 -0
- dtypeflow/core/schema.py +33 -0
- dtypeflow/core/table.py +29 -0
- dtypeflow/py.typed +0 -0
- dtypeflow/tui/__init__.py +17 -0
- dtypeflow/tui/app.py +747 -0
- dtypeflow/tui/app.tcss +220 -0
- dtypeflow/tui/compatibility.py +38 -0
- dtypeflow/tui/dto.py +44 -0
- dtypeflow/tui/service.py +213 -0
- dtypeflow/utils.py +32 -0
- dtypeflow-1.0.0.dist-info/METADATA +93 -0
- dtypeflow-1.0.0.dist-info/RECORD +43 -0
- dtypeflow-1.0.0.dist-info/WHEEL +4 -0
- dtypeflow-1.0.0.dist-info/entry_points.txt +3 -0
- dtypeflow-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from typing import ClassVar, Final, final
|
|
5
|
+
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
8
|
+
from dtypeflow.core.dtype.base import Sizeable
|
|
9
|
+
|
|
10
|
+
from .base import Primitive
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Float(Primitive, Sizeable):
|
|
14
|
+
MAX_ABSOLUTE_VALUE: ClassVar[Decimal]
|
|
15
|
+
MAX_SIGNIFICANT_DIGITS: ClassVar[int]
|
|
16
|
+
|
|
17
|
+
@override
|
|
18
|
+
def __repr__(self) -> str:
|
|
19
|
+
return f"Float(±{self.MAX_ABSOLUTE_VALUE} (≈ {self.MAX_SIGNIFICANT_DIGITS} significant digits))"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@final
|
|
23
|
+
class Float16(Float):
|
|
24
|
+
SIZE: Final[int] = 2
|
|
25
|
+
MAX_ABSOLUTE_VALUE: Final[Decimal] = Decimal(65504)
|
|
26
|
+
MAX_SIGNIFICANT_DIGITS: Final[int] = 4
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@final
|
|
30
|
+
class Float32(Float):
|
|
31
|
+
SIZE: Final[int] = 4
|
|
32
|
+
MAX_ABSOLUTE_VALUE: Final[Decimal] = Decimal("3.4028235e38")
|
|
33
|
+
MAX_SIGNIFICANT_DIGITS: Final[int] = 7
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@final
|
|
37
|
+
class Float64(Float):
|
|
38
|
+
SIZE: Final[int] = 8
|
|
39
|
+
MAX_ABSOLUTE_VALUE: Final[Decimal] = Decimal("1.7976931348623157e308")
|
|
40
|
+
MAX_SIGNIFICANT_DIGITS: Final[int] = 15
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .base import Integer as IntegerDtype
|
|
4
|
+
from .signed import (
|
|
5
|
+
Int8 as Int8Dtype,
|
|
6
|
+
Int16 as Int16Dtype,
|
|
7
|
+
Int32 as Int32Dtype,
|
|
8
|
+
Int64 as Int64Dtype,
|
|
9
|
+
Signed as SignedDtype,
|
|
10
|
+
)
|
|
11
|
+
from .unsigned import (
|
|
12
|
+
UInt8 as UInt8Dtype,
|
|
13
|
+
UInt16 as UInt16Dtype,
|
|
14
|
+
UInt32 as UInt32Dtype,
|
|
15
|
+
UInt64 as UInt64Dtype,
|
|
16
|
+
Unsigned as UnsignedDtype,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__: list[str] = [
|
|
20
|
+
"Int8Dtype",
|
|
21
|
+
"Int16Dtype",
|
|
22
|
+
"Int32Dtype",
|
|
23
|
+
"Int64Dtype",
|
|
24
|
+
"IntegerDtype",
|
|
25
|
+
"SignedDtype",
|
|
26
|
+
"UInt8Dtype",
|
|
27
|
+
"UInt16Dtype",
|
|
28
|
+
"UInt32Dtype",
|
|
29
|
+
"UInt64Dtype",
|
|
30
|
+
"UnsignedDtype",
|
|
31
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from dtypeflow.core.dtype.base import Sizeable
|
|
8
|
+
from dtypeflow.core.dtype.primitive.base import Primitive
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Integer(Primitive, Sizeable):
|
|
12
|
+
LOWER_BOUND: ClassVar[int]
|
|
13
|
+
HIGHER_BOUND: ClassVar[int]
|
|
14
|
+
|
|
15
|
+
@override
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"{self.__class__.__name__}({self.LOWER_BOUND} – {self.HIGHER_BOUND})"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Final, final
|
|
4
|
+
|
|
5
|
+
from dtypeflow.core.dtype.base import Sizeable
|
|
6
|
+
from dtypeflow.core.dtype.primitive.integer.base import Integer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Signed(Integer, Sizeable): ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@final
|
|
13
|
+
class Int8(Signed):
|
|
14
|
+
SIZE: Final[int] = 1
|
|
15
|
+
LOWER_BOUND: Final[int] = -128
|
|
16
|
+
HIGHER_BOUND: Final[int] = 127
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@final
|
|
20
|
+
class Int16(Signed):
|
|
21
|
+
SIZE: Final[int] = 2
|
|
22
|
+
LOWER_BOUND: Final[int] = -32768
|
|
23
|
+
HIGHER_BOUND: Final[int] = 32767
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@final
|
|
27
|
+
class Int32(Signed):
|
|
28
|
+
SIZE: Final[int] = 4
|
|
29
|
+
LOWER_BOUND: Final[int] = -2147483648
|
|
30
|
+
HIGHER_BOUND: Final[int] = 2147483647
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@final
|
|
34
|
+
class Int64(Signed):
|
|
35
|
+
SIZE: Final[int] = 8
|
|
36
|
+
LOWER_BOUND: Final[int] = -9223372036854775808
|
|
37
|
+
HIGHER_BOUND: Final[int] = 9223372036854775807
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Final, final
|
|
4
|
+
|
|
5
|
+
from dtypeflow.core.dtype.base import Sizeable
|
|
6
|
+
from dtypeflow.core.dtype.primitive.integer.base import Integer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Unsigned(Integer, Sizeable): ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@final
|
|
13
|
+
class UInt8(Unsigned):
|
|
14
|
+
SIZE: Final[int] = 1
|
|
15
|
+
LOWER_BOUND: Final[int] = 0
|
|
16
|
+
HIGHER_BOUND: Final[int] = 255
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@final
|
|
20
|
+
class UInt16(Unsigned):
|
|
21
|
+
SIZE: Final[int] = 2
|
|
22
|
+
LOWER_BOUND: Final[int] = 0
|
|
23
|
+
HIGHER_BOUND: Final[int] = 65535
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@final
|
|
27
|
+
class UInt32(Unsigned):
|
|
28
|
+
SIZE: Final[int] = 4
|
|
29
|
+
LOWER_BOUND: Final[int] = 0
|
|
30
|
+
HIGHER_BOUND: Final[int] = 4294967295
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@final
|
|
34
|
+
class UInt64(Unsigned):
|
|
35
|
+
SIZE: Final[int] = 8
|
|
36
|
+
LOWER_BOUND: Final[int] = 0
|
|
37
|
+
HIGHER_BOUND: Final[int] = 18446744073709551615
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import final
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from .base import Primitive
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@final
|
|
11
|
+
class String(Primitive):
|
|
12
|
+
@override
|
|
13
|
+
def __repr__(self) -> str:
|
|
14
|
+
return f"{self.__class__.__name__}()"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .base import Override
|
|
4
|
+
from .primitive import (
|
|
5
|
+
Boolean as BooleanOverride,
|
|
6
|
+
Dictionary as DictionaryOverride,
|
|
7
|
+
Float as FloatOverride,
|
|
8
|
+
Float16 as Float16Override,
|
|
9
|
+
Float32 as Float32Override,
|
|
10
|
+
Float64 as Float64Override,
|
|
11
|
+
Int8 as Int8Override,
|
|
12
|
+
Int16 as Int16Override,
|
|
13
|
+
Int32 as Int32Override,
|
|
14
|
+
Int64 as Int64Override,
|
|
15
|
+
Integer as IntegerOverride,
|
|
16
|
+
String as StringOverride,
|
|
17
|
+
UInt8 as UInt8Override,
|
|
18
|
+
UInt16 as UInt16Override,
|
|
19
|
+
UInt32 as UInt32Override,
|
|
20
|
+
UInt64 as UInt64Override,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__: list[str] = [
|
|
24
|
+
"BooleanOverride",
|
|
25
|
+
"DictionaryOverride",
|
|
26
|
+
"Float16Override",
|
|
27
|
+
"Float32Override",
|
|
28
|
+
"Float64Override",
|
|
29
|
+
"FloatOverride",
|
|
30
|
+
"Int8Override",
|
|
31
|
+
"Int16Override",
|
|
32
|
+
"Int32Override",
|
|
33
|
+
"Int64Override",
|
|
34
|
+
"IntegerOverride",
|
|
35
|
+
"Override",
|
|
36
|
+
"StringOverride",
|
|
37
|
+
"UInt8Override",
|
|
38
|
+
"UInt16Override",
|
|
39
|
+
"UInt32Override",
|
|
40
|
+
"UInt64Override",
|
|
41
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
import pyarrow as pa
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Override(ABC):
|
|
11
|
+
def __init__(self, column: str, /) -> None:
|
|
12
|
+
self._column: str = column
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def column(self) -> str:
|
|
16
|
+
return self._column
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def transform(self, *values: str | None) -> pa.Array: ...
|
|
20
|
+
|
|
21
|
+
def __repr__(self) -> str:
|
|
22
|
+
return f"{type(self).__name__}(column={self._column!r})"
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal, InvalidOperation
|
|
4
|
+
|
|
5
|
+
import pyarrow as pa
|
|
6
|
+
|
|
7
|
+
from .base import Override
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Boolean(Override):
|
|
11
|
+
def __init__(self, column: str, /, *, true_value: str, false_value: str) -> None:
|
|
12
|
+
super().__init__(column)
|
|
13
|
+
if (true_value := true_value.strip().lower()) == (
|
|
14
|
+
false_value := false_value.strip().lower()
|
|
15
|
+
):
|
|
16
|
+
msg = f"true_value and false_value must differ for column {column!r}: {true_value!r}."
|
|
17
|
+
raise ValueError(msg)
|
|
18
|
+
self._true_value: str = true_value
|
|
19
|
+
self._false_value: str = false_value
|
|
20
|
+
|
|
21
|
+
def _coerce(self, value: str | None, /) -> bool | None:
|
|
22
|
+
if value is None or not value.strip():
|
|
23
|
+
return None
|
|
24
|
+
if (value := value.strip().lower()) not in (
|
|
25
|
+
self._true_value,
|
|
26
|
+
self._false_value,
|
|
27
|
+
):
|
|
28
|
+
msg = f"{value!r} is neither {self._true_value!r} nor {self._false_value!r} for column {self.column!r}."
|
|
29
|
+
raise ValueError(msg)
|
|
30
|
+
return value == self._true_value
|
|
31
|
+
|
|
32
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
33
|
+
return pa.array([self._coerce(value) for value in values], type=pa.bool_())
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Integer(Override):
|
|
37
|
+
def _coerce(self, value: str | None, /) -> int | None:
|
|
38
|
+
if value is None or not value.strip():
|
|
39
|
+
return None
|
|
40
|
+
stripped = value.strip()
|
|
41
|
+
try:
|
|
42
|
+
return int(Decimal(stripped))
|
|
43
|
+
except InvalidOperation:
|
|
44
|
+
msg = f"{stripped!r} is not a valid number for column {self.column!r}."
|
|
45
|
+
raise ValueError(msg) from None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Int8(Integer):
|
|
49
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
50
|
+
return pa.array([self._coerce(value) for value in values], type=pa.int8())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Int16(Integer):
|
|
54
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
55
|
+
return pa.array([self._coerce(value) for value in values], type=pa.int16())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Int32(Integer):
|
|
59
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
60
|
+
return pa.array([self._coerce(value) for value in values], type=pa.int32())
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Int64(Integer):
|
|
64
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
65
|
+
return pa.array([self._coerce(value) for value in values], type=pa.int64())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class UInt8(Integer):
|
|
69
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
70
|
+
return pa.array([self._coerce(value) for value in values], type=pa.uint8())
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class UInt16(Integer):
|
|
74
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
75
|
+
return pa.array([self._coerce(value) for value in values], type=pa.uint16())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class UInt32(Integer):
|
|
79
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
80
|
+
return pa.array([self._coerce(value) for value in values], type=pa.uint32())
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class UInt64(Integer):
|
|
84
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
85
|
+
return pa.array([self._coerce(value) for value in values], type=pa.uint64())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Float(Override):
|
|
89
|
+
def _coerce(self, value: str | None, /) -> float | None:
|
|
90
|
+
if value is None or not value.strip():
|
|
91
|
+
return None
|
|
92
|
+
return float(value.strip())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Float16(Float):
|
|
96
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
97
|
+
return pa.array([self._coerce(value) for value in values], type=pa.float16())
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class Float32(Float):
|
|
101
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
102
|
+
return pa.array([self._coerce(value) for value in values], type=pa.float32())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Float64(Float):
|
|
106
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
107
|
+
return pa.array([self._coerce(value) for value in values], type=pa.float64())
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class String(Override):
|
|
111
|
+
def _coerce(self, value: str | None, /) -> str | None:
|
|
112
|
+
if value is None or not value.strip():
|
|
113
|
+
return None
|
|
114
|
+
return value.strip()
|
|
115
|
+
|
|
116
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
117
|
+
return pa.array([self._coerce(value) for value in values], type=pa.string())
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Dictionary(String):
|
|
121
|
+
def __init__(self, column: str, /, *categories: str, ordered: bool = False) -> None:
|
|
122
|
+
super().__init__(column)
|
|
123
|
+
if not categories:
|
|
124
|
+
msg = f"categories must be provided for column {column!r}."
|
|
125
|
+
raise ValueError(msg)
|
|
126
|
+
if len(set(categories)) != len(categories):
|
|
127
|
+
msg = f"categories must not contain duplicates for column {column!r}: {categories!r}."
|
|
128
|
+
raise ValueError(msg)
|
|
129
|
+
self._ordered: bool = ordered
|
|
130
|
+
self._categories: tuple[str, ...] = categories
|
|
131
|
+
|
|
132
|
+
def _dictionary_index_type(self, num_unique: int, /) -> pa.DataType:
|
|
133
|
+
if num_unique <= 2**8:
|
|
134
|
+
return pa.int8()
|
|
135
|
+
if num_unique <= 2**16:
|
|
136
|
+
return pa.int16()
|
|
137
|
+
if num_unique <= 2**32:
|
|
138
|
+
return pa.int32()
|
|
139
|
+
return pa.int64()
|
|
140
|
+
|
|
141
|
+
def transform(self, *values: str | None) -> pa.Array:
|
|
142
|
+
coerced: list[str | None] = [self._coerce(value) for value in values]
|
|
143
|
+
if missing := (
|
|
144
|
+
{value for value in coerced if value is not None} - set(self._categories)
|
|
145
|
+
):
|
|
146
|
+
msg = f"values {sorted(missing)!r} for column {self.column!r} are not in the provided categories {self._categories!r}."
|
|
147
|
+
raise ValueError(msg)
|
|
148
|
+
rank: dict[str, int] = {
|
|
149
|
+
category: index for (index, category) in enumerate(self._categories)
|
|
150
|
+
}
|
|
151
|
+
return pa.DictionaryArray.from_arrays(
|
|
152
|
+
pa.array(
|
|
153
|
+
[None if value is None else rank[value] for value in coerced],
|
|
154
|
+
type=self._dictionary_index_type(len(self._categories)),
|
|
155
|
+
),
|
|
156
|
+
pa.array(self._categories, type=pa.string()),
|
|
157
|
+
ordered=self._ordered,
|
|
158
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .base import Profile
|
|
4
|
+
from .primitive import (
|
|
5
|
+
Boolean as BooleanProfile,
|
|
6
|
+
Float as FloatProfile,
|
|
7
|
+
Float16 as Float16Profile,
|
|
8
|
+
Float32 as Float32Profile,
|
|
9
|
+
Float64 as Float64Profile,
|
|
10
|
+
Int8 as Int8Profile,
|
|
11
|
+
Int16 as Int16Profile,
|
|
12
|
+
Int32 as Int32Profile,
|
|
13
|
+
Int64 as Int64Profile,
|
|
14
|
+
Integer as IntegerProfile,
|
|
15
|
+
String as StringProfile,
|
|
16
|
+
UInt8 as UInt8Profile,
|
|
17
|
+
UInt16 as UInt16Profile,
|
|
18
|
+
UInt32 as UInt32Profile,
|
|
19
|
+
UInt64 as UInt64Profile,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__: list[str] = [
|
|
23
|
+
"BooleanProfile",
|
|
24
|
+
"Float16Profile",
|
|
25
|
+
"Float32Profile",
|
|
26
|
+
"Float64Profile",
|
|
27
|
+
"FloatProfile",
|
|
28
|
+
"Int8Profile",
|
|
29
|
+
"Int16Profile",
|
|
30
|
+
"Int32Profile",
|
|
31
|
+
"Int64Profile",
|
|
32
|
+
"IntegerProfile",
|
|
33
|
+
"Profile",
|
|
34
|
+
"StringProfile",
|
|
35
|
+
"UInt8Profile",
|
|
36
|
+
"UInt16Profile",
|
|
37
|
+
"UInt32Profile",
|
|
38
|
+
"UInt64Profile",
|
|
39
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from dtypeflow.core.candidate import Candidate
|
|
8
|
+
from dtypeflow.core.dtype import Dtype
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Profile(ABC):
|
|
12
|
+
DTYPE: ClassVar[type[Dtype]]
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
*values: str | None,
|
|
17
|
+
uniques: tuple[int, ...],
|
|
18
|
+
non_nan: tuple[int, ...],
|
|
19
|
+
nan: tuple[int, ...],
|
|
20
|
+
) -> None:
|
|
21
|
+
self._values: tuple[str | None, ...] = values
|
|
22
|
+
self._non_nan_indices: tuple[int, ...] = non_nan
|
|
23
|
+
self._nan_indices: tuple[int, ...] = nan
|
|
24
|
+
self._uniques: tuple[int, ...] = uniques
|
|
25
|
+
self._candidates: tuple[Candidate, ...] | None = None
|
|
26
|
+
|
|
27
|
+
def uniques(self, *, n: int | None = None) -> tuple[str, ...]:
|
|
28
|
+
if n is None:
|
|
29
|
+
return tuple(self._values[index] for index in self._uniques)
|
|
30
|
+
return tuple(self._values[index] for index in self._uniques[:n])
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def num_unique(self) -> int:
|
|
34
|
+
return len(self._uniques)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def total(self) -> int:
|
|
38
|
+
return len(self._non_nan_indices)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def candidates(self) -> tuple[Candidate, ...]:
|
|
42
|
+
if self._candidates is None:
|
|
43
|
+
self._candidates: tuple[Candidate, ...] = self._compute_candidates()
|
|
44
|
+
return self._candidates
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def _compute_candidates(self) -> tuple[Candidate, ...]: ...
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def __repr__(self) -> str: ...
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from typing import TYPE_CHECKING, ClassVar, Final, cast
|
|
5
|
+
|
|
6
|
+
from dtypeflow.core.candidate import candidate_factory
|
|
7
|
+
from dtypeflow.core.dtype import (
|
|
8
|
+
BooleanDtype,
|
|
9
|
+
Dtype,
|
|
10
|
+
Float16Dtype,
|
|
11
|
+
Float32Dtype,
|
|
12
|
+
Float64Dtype,
|
|
13
|
+
FloatDtype,
|
|
14
|
+
Int8Dtype,
|
|
15
|
+
Int16Dtype,
|
|
16
|
+
Int32Dtype,
|
|
17
|
+
Int64Dtype,
|
|
18
|
+
IntegerDtype,
|
|
19
|
+
StringDtype,
|
|
20
|
+
UInt8Dtype,
|
|
21
|
+
UInt16Dtype,
|
|
22
|
+
UInt32Dtype,
|
|
23
|
+
UInt64Dtype,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .base import Profile
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from dtypeflow.core.candidate import Candidate
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Boolean(Profile):
|
|
33
|
+
DTYPE: Final[type[Dtype]] = BooleanDtype
|
|
34
|
+
|
|
35
|
+
def _compute_candidates(self) -> tuple[Candidate, ...]:
|
|
36
|
+
return candidate_factory(
|
|
37
|
+
self.DTYPE, num_unique=self.num_unique, total=self.total,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def __repr__(self) -> str:
|
|
41
|
+
return f"{type(self).__name__}(num_unique={self.num_unique})"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Integer(Profile):
|
|
45
|
+
DTYPE: ClassVar[type[Dtype]] = IntegerDtype
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def min(self) -> int:
|
|
49
|
+
return min(int(value) for value in self.uniques())
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def max(self) -> int:
|
|
53
|
+
return max(int(value) for value in self.uniques())
|
|
54
|
+
|
|
55
|
+
def _compute_candidates(self) -> tuple[Candidate, ...]:
|
|
56
|
+
return candidate_factory(
|
|
57
|
+
self.DTYPE,
|
|
58
|
+
num_unique=self.num_unique,
|
|
59
|
+
total=self.total,
|
|
60
|
+
min_value=self.min,
|
|
61
|
+
max_value=self.max,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def __repr__(self) -> str:
|
|
65
|
+
return f"{type(self).__name__}(min={self.min}, max={self.max}, num_unique={self.num_unique})"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class UInt8(Integer):
|
|
69
|
+
DTYPE: Final[type[Dtype]] = UInt8Dtype
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class UInt16(Integer):
|
|
73
|
+
DTYPE: Final[type[Dtype]] = UInt16Dtype
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class UInt32(Integer):
|
|
77
|
+
DTYPE: Final[type[Dtype]] = UInt32Dtype
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class UInt64(Integer):
|
|
81
|
+
DTYPE: Final[type[Dtype]] = UInt64Dtype
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Int8(Integer):
|
|
85
|
+
DTYPE: Final[type[Dtype]] = Int8Dtype
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Int16(Integer):
|
|
89
|
+
DTYPE: Final[type[Dtype]] = Int16Dtype
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Int32(Integer):
|
|
93
|
+
DTYPE: Final[type[Dtype]] = Int32Dtype
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Int64(Integer):
|
|
97
|
+
DTYPE: Final[type[Dtype]] = Int64Dtype
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class Float(Profile):
|
|
101
|
+
DTYPE: ClassVar[type[Dtype]] = FloatDtype
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def min(self) -> Decimal:
|
|
105
|
+
return min(Decimal(value) for value in self.uniques())
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def max(self) -> Decimal:
|
|
109
|
+
return max(Decimal(value) for value in self.uniques())
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def precision(self) -> int:
|
|
113
|
+
return max(len(Decimal(value).as_tuple().digits) for value in self.uniques())
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def scale(self) -> int:
|
|
117
|
+
exponents: list[int] = [
|
|
118
|
+
cast("int", Decimal(value).as_tuple().exponent) for value in self.uniques()
|
|
119
|
+
]
|
|
120
|
+
return max([0, *(-exponent for exponent in exponents)])
|
|
121
|
+
|
|
122
|
+
def _compute_candidates(self) -> tuple[Candidate, ...]:
|
|
123
|
+
return candidate_factory(
|
|
124
|
+
self.DTYPE,
|
|
125
|
+
num_unique=self.num_unique,
|
|
126
|
+
total=self.total,
|
|
127
|
+
min_value=self.min,
|
|
128
|
+
max_value=self.max,
|
|
129
|
+
precision=self.precision,
|
|
130
|
+
scale=self.scale,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def __repr__(self) -> str:
|
|
134
|
+
return f"{type(self).__name__}(min={self.min}, max={self.max}, precision={self.precision}, scale={self.scale}, num_unique={self.num_unique})"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class Float16(Float):
|
|
138
|
+
DTYPE: Final[type[Dtype]] = Float16Dtype
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class Float32(Float):
|
|
142
|
+
DTYPE: Final[type[Dtype]] = Float32Dtype
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class Float64(Float):
|
|
146
|
+
DTYPE: Final[type[Dtype]] = Float64Dtype
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class String(Profile):
|
|
150
|
+
DTYPE: Final[type[Dtype]] = StringDtype
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def min_length(self) -> int:
|
|
154
|
+
return min(len(value) for value in self.uniques())
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def max_length(self) -> int:
|
|
158
|
+
return max(len(value) for value in self.uniques())
|
|
159
|
+
|
|
160
|
+
def _compute_candidates(self) -> tuple[Candidate, ...]:
|
|
161
|
+
return candidate_factory(
|
|
162
|
+
self.DTYPE, num_unique=self.num_unique, total=self.total,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def __repr__(self) -> str:
|
|
166
|
+
return f"{type(self).__name__}(min_length={self.min_length}, max_length={self.max_length}, num_unique={self.num_unique})"
|