kumoai 2.13.0.dev202511271731__cp312-cp312-win_amd64.whl → 2.14.0.dev202512111731__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kumoai/__init__.py +12 -0
- kumoai/_version.py +1 -1
- kumoai/connector/utils.py +23 -2
- kumoai/experimental/rfm/__init__.py +20 -45
- kumoai/experimental/rfm/backend/__init__.py +0 -0
- kumoai/experimental/rfm/backend/local/__init__.py +42 -0
- kumoai/experimental/rfm/{local_graph_store.py → backend/local/graph_store.py} +37 -90
- kumoai/experimental/rfm/backend/local/sampler.py +313 -0
- kumoai/experimental/rfm/backend/local/table.py +109 -0
- kumoai/experimental/rfm/backend/snow/__init__.py +35 -0
- kumoai/experimental/rfm/backend/snow/table.py +117 -0
- kumoai/experimental/rfm/backend/sqlite/__init__.py +30 -0
- kumoai/experimental/rfm/backend/sqlite/table.py +101 -0
- kumoai/experimental/rfm/base/__init__.py +13 -0
- kumoai/experimental/rfm/base/column.py +66 -0
- kumoai/experimental/rfm/base/sampler.py +763 -0
- kumoai/experimental/rfm/base/source.py +18 -0
- kumoai/experimental/rfm/{local_table.py → base/table.py} +139 -139
- kumoai/experimental/rfm/{local_graph.py → graph.py} +334 -79
- kumoai/experimental/rfm/infer/__init__.py +6 -0
- kumoai/experimental/rfm/infer/dtype.py +79 -0
- kumoai/experimental/rfm/infer/pkey.py +126 -0
- kumoai/experimental/rfm/infer/time_col.py +62 -0
- kumoai/experimental/rfm/pquery/pandas_executor.py +1 -1
- kumoai/experimental/rfm/rfm.py +204 -166
- kumoai/experimental/rfm/sagemaker.py +11 -3
- kumoai/kumolib.cp312-win_amd64.pyd +0 -0
- kumoai/pquery/predictive_query.py +10 -6
- kumoai/testing/decorators.py +1 -1
- {kumoai-2.13.0.dev202511271731.dist-info → kumoai-2.14.0.dev202512111731.dist-info}/METADATA +9 -8
- {kumoai-2.13.0.dev202511271731.dist-info → kumoai-2.14.0.dev202512111731.dist-info}/RECORD +34 -22
- kumoai/experimental/rfm/local_graph_sampler.py +0 -182
- kumoai/experimental/rfm/local_pquery_driver.py +0 -689
- kumoai/experimental/rfm/utils.py +0 -344
- {kumoai-2.13.0.dev202511271731.dist-info → kumoai-2.14.0.dev202512111731.dist-info}/WHEEL +0 -0
- {kumoai-2.13.0.dev202511271731.dist-info → kumoai-2.14.0.dev202512111731.dist-info}/licenses/LICENSE +0 -0
- {kumoai-2.13.0.dev202511271731.dist-info → kumoai-2.14.0.dev202512111731.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import List, Optional, Sequence
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from kumoapi.typing import Dtype
|
|
7
|
+
|
|
8
|
+
from kumoai.experimental.rfm.backend.sqlite import Connection
|
|
9
|
+
from kumoai.experimental.rfm.base import SourceColumn, SourceForeignKey, Table
|
|
10
|
+
from kumoai.experimental.rfm.infer import infer_dtype
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SQLiteTable(Table):
|
|
14
|
+
r"""A table backed by a :class:`sqlite` database.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
connection: The connection to a :class:`sqlite` database.
|
|
18
|
+
name: The name of this table.
|
|
19
|
+
columns: The selected columns of this table.
|
|
20
|
+
primary_key: The name of the primary key of this table, if it exists.
|
|
21
|
+
time_column: The name of the time column of this table, if it exists.
|
|
22
|
+
end_time_column: The name of the end time column of this table, if it
|
|
23
|
+
exists.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
connection: Connection,
|
|
28
|
+
name: str,
|
|
29
|
+
columns: Optional[Sequence[str]] = None,
|
|
30
|
+
primary_key: Optional[str] = None,
|
|
31
|
+
time_column: Optional[str] = None,
|
|
32
|
+
end_time_column: Optional[str] = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
|
|
35
|
+
self._connection = connection
|
|
36
|
+
|
|
37
|
+
super().__init__(
|
|
38
|
+
name=name,
|
|
39
|
+
columns=columns,
|
|
40
|
+
primary_key=primary_key,
|
|
41
|
+
time_column=time_column,
|
|
42
|
+
end_time_column=end_time_column,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def _get_source_columns(self) -> List[SourceColumn]:
|
|
46
|
+
source_columns: List[SourceColumn] = []
|
|
47
|
+
with self._connection.cursor() as cursor:
|
|
48
|
+
cursor.execute(f"PRAGMA table_info({self.name})")
|
|
49
|
+
rows = cursor.fetchall()
|
|
50
|
+
|
|
51
|
+
if len(rows) == 0:
|
|
52
|
+
raise ValueError(f"Table '{self.name}' does not exist")
|
|
53
|
+
|
|
54
|
+
for _, column, type, _, _, is_pkey in rows:
|
|
55
|
+
# Determine column affinity:
|
|
56
|
+
type = type.strip().upper()
|
|
57
|
+
if re.search('INT', type):
|
|
58
|
+
dtype = Dtype.int
|
|
59
|
+
elif re.search('TEXT|CHAR|CLOB', type):
|
|
60
|
+
dtype = Dtype.string
|
|
61
|
+
elif re.search('REAL|FLOA|DOUB', type):
|
|
62
|
+
dtype = Dtype.float
|
|
63
|
+
else: # NUMERIC affinity.
|
|
64
|
+
ser = self._sample_df[column]
|
|
65
|
+
try:
|
|
66
|
+
dtype = infer_dtype(ser)
|
|
67
|
+
except Exception:
|
|
68
|
+
warnings.warn(
|
|
69
|
+
f"Data type inference for column '{column}' in "
|
|
70
|
+
f"table '{self.name}' failed. Consider changing "
|
|
71
|
+
f"the data type of the column to use it within "
|
|
72
|
+
f"this table.")
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
source_column = SourceColumn(
|
|
76
|
+
name=column,
|
|
77
|
+
dtype=dtype,
|
|
78
|
+
is_primary_key=bool(is_pkey),
|
|
79
|
+
is_unique_key=False,
|
|
80
|
+
)
|
|
81
|
+
source_columns.append(source_column)
|
|
82
|
+
|
|
83
|
+
return source_columns
|
|
84
|
+
|
|
85
|
+
def _get_source_foreign_keys(self) -> List[SourceForeignKey]:
|
|
86
|
+
source_fkeys: List[SourceForeignKey] = []
|
|
87
|
+
with self._connection.cursor() as cursor:
|
|
88
|
+
cursor.execute(f"PRAGMA foreign_key_list({self.name})")
|
|
89
|
+
for _, _, dst_table, fkey, pkey, _, _, _ in cursor.fetchall():
|
|
90
|
+
source_fkeys.append(SourceForeignKey(fkey, dst_table, pkey))
|
|
91
|
+
return source_fkeys
|
|
92
|
+
|
|
93
|
+
def _get_sample_df(self) -> pd.DataFrame:
|
|
94
|
+
with self._connection.cursor() as cursor:
|
|
95
|
+
cursor.execute(f"SELECT * FROM {self.name} "
|
|
96
|
+
f"ORDER BY rowid LIMIT 1000")
|
|
97
|
+
table = cursor.fetch_arrow_table()
|
|
98
|
+
return table.to_pandas(types_mapper=pd.ArrowDtype)
|
|
99
|
+
|
|
100
|
+
def _get_num_rows(self) -> Optional[int]:
|
|
101
|
+
return None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .source import SourceColumn, SourceForeignKey
|
|
2
|
+
from .column import Column
|
|
3
|
+
from .table import Table
|
|
4
|
+
from .sampler import SamplerOutput, Sampler
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'SourceColumn',
|
|
8
|
+
'SourceForeignKey',
|
|
9
|
+
'Column',
|
|
10
|
+
'Table',
|
|
11
|
+
'SamplerOutput',
|
|
12
|
+
'Sampler',
|
|
13
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from kumoapi.typing import Dtype, Stype
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(init=False, repr=False, eq=False)
|
|
8
|
+
class Column:
|
|
9
|
+
stype: Stype
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
name: str,
|
|
14
|
+
dtype: Dtype,
|
|
15
|
+
stype: Stype,
|
|
16
|
+
is_primary_key: bool = False,
|
|
17
|
+
is_time_column: bool = False,
|
|
18
|
+
is_end_time_column: bool = False,
|
|
19
|
+
) -> None:
|
|
20
|
+
self._name = name
|
|
21
|
+
self._dtype = Dtype(dtype)
|
|
22
|
+
self._is_primary_key = is_primary_key
|
|
23
|
+
self._is_time_column = is_time_column
|
|
24
|
+
self._is_end_time_column = is_end_time_column
|
|
25
|
+
self.stype = Stype(stype)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def name(self) -> str:
|
|
29
|
+
return self._name
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def dtype(self) -> Dtype:
|
|
33
|
+
return self._dtype
|
|
34
|
+
|
|
35
|
+
def __setattr__(self, key: str, val: Any) -> None:
|
|
36
|
+
if key == 'stype':
|
|
37
|
+
if isinstance(val, str):
|
|
38
|
+
val = Stype(val)
|
|
39
|
+
assert isinstance(val, Stype)
|
|
40
|
+
if not val.supports_dtype(self.dtype):
|
|
41
|
+
raise ValueError(f"Column '{self.name}' received an "
|
|
42
|
+
f"incompatible semantic type (got "
|
|
43
|
+
f"dtype='{self.dtype}' and stype='{val}')")
|
|
44
|
+
if self._is_primary_key and val != Stype.ID:
|
|
45
|
+
raise ValueError(f"Primary key '{self.name}' must have 'ID' "
|
|
46
|
+
f"semantic type (got '{val}')")
|
|
47
|
+
if self._is_time_column and val != Stype.timestamp:
|
|
48
|
+
raise ValueError(f"Time column '{self.name}' must have "
|
|
49
|
+
f"'timestamp' semantic type (got '{val}')")
|
|
50
|
+
if self._is_end_time_column and val != Stype.timestamp:
|
|
51
|
+
raise ValueError(f"End time column '{self.name}' must have "
|
|
52
|
+
f"'timestamp' semantic type (got '{val}')")
|
|
53
|
+
|
|
54
|
+
super().__setattr__(key, val)
|
|
55
|
+
|
|
56
|
+
def __hash__(self) -> int:
|
|
57
|
+
return hash((self.name, self.stype, self.dtype))
|
|
58
|
+
|
|
59
|
+
def __eq__(self, other: Any) -> bool:
|
|
60
|
+
if not isinstance(other, Column):
|
|
61
|
+
return False
|
|
62
|
+
return hash(self) == hash(other)
|
|
63
|
+
|
|
64
|
+
def __repr__(self) -> str:
|
|
65
|
+
return (f'{self.__class__.__name__}(name={self.name}, '
|
|
66
|
+
f'stype={self.stype}, dtype={self.dtype})')
|