qutePandas 1.1.0__py3-none-any.whl → 1.1.2__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.
- qutePandas/__init__.py +41 -11
- qutePandas/apply/apply.py +7 -1
- qutePandas/cleaning/fillna.py +20 -7
- qutePandas/core/dataframe.py +0 -3
- qutePandas/indexing/iloc.py +31 -10
- {qutepandas-1.1.0.dist-info → qutepandas-1.1.2.dist-info}/METADATA +1 -1
- {qutepandas-1.1.0.dist-info → qutepandas-1.1.2.dist-info}/RECORD +9 -9
- {qutepandas-1.1.0.dist-info → qutepandas-1.1.2.dist-info}/WHEEL +1 -1
- {qutepandas-1.1.0.dist-info → qutepandas-1.1.2.dist-info}/top_level.txt +0 -0
qutePandas/__init__.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"""
|
|
2
|
-
qutePandas - A pandas-like library for q/kdb+
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
import os
|
|
2
|
+
import sys
|
|
6
3
|
|
|
7
|
-
def
|
|
8
|
-
"""
|
|
4
|
+
def _setup_pykx_environment():
|
|
5
|
+
"""
|
|
6
|
+
Sets up the environment for PyKX BEFORE any PyKX imports.
|
|
7
|
+
This must run before importing any module that uses PyKX.
|
|
8
|
+
"""
|
|
9
9
|
root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
10
10
|
env_path = os.path.join(root, ".env")
|
|
11
11
|
|
|
@@ -20,17 +20,47 @@ def _setup_environment():
|
|
|
20
20
|
if len(parts) == 2:
|
|
21
21
|
key, value = parts
|
|
22
22
|
value = value.strip().strip('"').strip("'")
|
|
23
|
+
# Only set if not already in environment
|
|
23
24
|
if key.strip() and key.strip() not in os.environ:
|
|
24
25
|
os.environ[key.strip()] = value
|
|
25
|
-
|
|
26
|
+
|
|
27
|
+
# Check for valid license files in priority order
|
|
26
28
|
qutepandas_home = os.path.expanduser("~/.qutepandas")
|
|
27
29
|
local_kdb = os.path.join(root, "kdb_lic")
|
|
30
|
+
|
|
31
|
+
license_found = False
|
|
32
|
+
valid_license_path = None
|
|
33
|
+
|
|
28
34
|
if os.path.exists(os.path.join(local_kdb, "kc.lic")):
|
|
29
|
-
|
|
35
|
+
valid_license_path = local_kdb
|
|
36
|
+
license_found = True
|
|
30
37
|
elif os.path.exists(os.path.join(qutepandas_home, "kc.lic")):
|
|
31
|
-
|
|
38
|
+
valid_license_path = qutepandas_home
|
|
39
|
+
license_found = True
|
|
40
|
+
|
|
41
|
+
if license_found and valid_license_path:
|
|
42
|
+
os.environ['QLIC'] = valid_license_path
|
|
43
|
+
os.environ.pop('PYKX_UNLICENSED', None)
|
|
44
|
+
elif 'QLIC' in os.environ:
|
|
45
|
+
qlic_path = os.environ['QLIC']
|
|
46
|
+
if not (os.path.exists(os.path.join(qlic_path, "kc.lic")) or
|
|
47
|
+
os.path.exists(os.path.join(qlic_path, "k4.lic"))):
|
|
48
|
+
del os.environ['QLIC']
|
|
49
|
+
if 'QHOME' in os.environ:
|
|
50
|
+
del os.environ['QHOME']
|
|
51
|
+
license_found = False
|
|
52
|
+
|
|
53
|
+
if not license_found and 'PYKX_UNLICENSED' not in os.environ:
|
|
54
|
+
os.environ['PYKX_UNLICENSED'] = 'true'
|
|
55
|
+
|
|
56
|
+
if 'PYKX_RELEASE_GIL' not in os.environ:
|
|
57
|
+
os.environ['PYKX_RELEASE_GIL'] = 'true'
|
|
58
|
+
|
|
59
|
+
if 'PYKX_ENFORCE_EMBEDDED_IMPORT' not in os.environ:
|
|
60
|
+
os.environ['PYKX_ENFORCE_EMBEDDED_IMPORT'] = '0'
|
|
61
|
+
|
|
62
|
+
_setup_pykx_environment()
|
|
32
63
|
|
|
33
|
-
_setup_environment()
|
|
34
64
|
from .core.dataframe import DataFrame
|
|
35
65
|
from .core.connection import connect, get_license_info, install_license
|
|
36
66
|
from .core.display import py, np, pd, pa, pt, print
|
|
@@ -59,4 +89,4 @@ from .introspection.dtypes import dtypes
|
|
|
59
89
|
|
|
60
90
|
from .indexing import loc, iloc
|
|
61
91
|
|
|
62
|
-
__version__ = "1.
|
|
92
|
+
__version__ = "1.1.2"
|
qutePandas/apply/apply.py
CHANGED
|
@@ -2,6 +2,10 @@ import pykx as kx
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from ..utils import _ensure_q_table, _handle_return
|
|
4
4
|
|
|
5
|
+
_VECTORIZED_AXIS1 = {
|
|
6
|
+
'sum': '{sum (0^) each value flip x}',
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
def apply(df, func, axis=0, return_type='q'):
|
|
7
11
|
"""
|
|
@@ -39,7 +43,9 @@ def apply(df, func, axis=0, return_type='q'):
|
|
|
39
43
|
return pd.Series(ret) if return_type == 'p' else ret
|
|
40
44
|
|
|
41
45
|
if isinstance(func, str):
|
|
42
|
-
if axis == 1:
|
|
46
|
+
if axis == 1 and func in _VECTORIZED_AXIS1:
|
|
47
|
+
result = kx.q(_VECTORIZED_AXIS1[func], q_table)
|
|
48
|
+
elif axis == 1:
|
|
43
49
|
result = kx.q(f"{{({func}) each x}}", q_table)
|
|
44
50
|
else:
|
|
45
51
|
result = kx.q(f"{{({func}) each flip x}}", q_table)
|
qutePandas/cleaning/fillna.py
CHANGED
|
@@ -2,16 +2,23 @@ import pykx as kx
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from ..utils import _ensure_q_table, _handle_return
|
|
4
4
|
|
|
5
|
-
def fillna(df,
|
|
5
|
+
def fillna(df, col_or_values, fill_value=None, return_type='q'):
|
|
6
6
|
"""
|
|
7
|
-
Fills null values
|
|
7
|
+
Fills null values in specified columns.
|
|
8
|
+
|
|
9
|
+
Can be called in two ways:
|
|
10
|
+
fillna(df, values_dict, return_type='q')
|
|
11
|
+
fillna(df, col_name, fill_value, return_type='q')
|
|
8
12
|
|
|
9
13
|
Parameters
|
|
10
14
|
----------
|
|
11
15
|
df : pandas.DataFrame or pykx.Table
|
|
12
16
|
Input DataFrame.
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
col_or_values : str or dict
|
|
18
|
+
If str, the column name to fill (requires fill_value).
|
|
19
|
+
If dict, a mapping of column names to fill values.
|
|
20
|
+
fill_value : scalar, optional
|
|
21
|
+
The value to fill nulls with when col_or_values is a column name.
|
|
15
22
|
return_type : str, default 'q'
|
|
16
23
|
Desired return type ('p' or 'q').
|
|
17
24
|
|
|
@@ -21,8 +28,14 @@ def fillna(df, values, return_type='q'):
|
|
|
21
28
|
DataFrame with nulls filled.
|
|
22
29
|
"""
|
|
23
30
|
try:
|
|
24
|
-
if
|
|
25
|
-
|
|
31
|
+
if isinstance(col_or_values, str):
|
|
32
|
+
if fill_value is None:
|
|
33
|
+
raise ValueError("fill_value is required when col_or_values is a column name")
|
|
34
|
+
values = {col_or_values: fill_value}
|
|
35
|
+
elif isinstance(col_or_values, dict):
|
|
36
|
+
values = col_or_values
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError("col_or_values must be a column name (str) or a dictionary")
|
|
26
39
|
|
|
27
40
|
q_table = _ensure_q_table(df)
|
|
28
41
|
result = q_table
|
|
@@ -33,4 +46,4 @@ def fillna(df, values, return_type='q'):
|
|
|
33
46
|
|
|
34
47
|
return _handle_return(result, return_type)
|
|
35
48
|
except Exception as e:
|
|
36
|
-
raise RuntimeError(f"Failed to fillna: {e}")
|
|
49
|
+
raise RuntimeError(f"Failed to fillna: {e}")
|
qutePandas/core/dataframe.py
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import pykx as kx
|
|
2
2
|
import pandas as pd
|
|
3
|
-
import os
|
|
4
3
|
import atexit
|
|
5
4
|
from ..utils import _handle_return
|
|
6
5
|
|
|
7
|
-
os.environ['PYKX_ENFORCE_EMBEDDED_IMPORT'] = '0'
|
|
8
|
-
|
|
9
6
|
def DataFrame(data, columns=None):
|
|
10
7
|
"""
|
|
11
8
|
Creates a qutePandas DataFrame (internal pykx Table).
|
qutePandas/indexing/iloc.py
CHANGED
|
@@ -22,10 +22,15 @@ def iloc(df, rows=None, cols=None, return_type='q'):
|
|
|
22
22
|
Subset of the inputs.
|
|
23
23
|
"""
|
|
24
24
|
table = _ensure_q_table(df)
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
count = kx.q("count", table).py()
|
|
27
27
|
all_cols = kx.q("cols", table).py()
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
# Track whether we can use the fast sublist path for contiguous row slices
|
|
30
|
+
_use_sublist = False
|
|
31
|
+
_slice_start = 0
|
|
32
|
+
_slice_count = 0
|
|
33
|
+
|
|
29
34
|
if rows is None:
|
|
30
35
|
row_indices = None
|
|
31
36
|
elif isinstance(rows, int):
|
|
@@ -33,7 +38,14 @@ def iloc(df, rows=None, cols=None, return_type='q'):
|
|
|
33
38
|
if q_rows < 0: q_rows += count
|
|
34
39
|
row_indices = [q_rows]
|
|
35
40
|
elif isinstance(rows, slice):
|
|
36
|
-
|
|
41
|
+
start, stop, step = rows.indices(count)
|
|
42
|
+
if step == 1:
|
|
43
|
+
_use_sublist = True
|
|
44
|
+
_slice_start = start
|
|
45
|
+
_slice_count = stop - start
|
|
46
|
+
row_indices = None # Not needed — sublist handles it
|
|
47
|
+
else:
|
|
48
|
+
row_indices = list(range(start, stop, step))
|
|
37
49
|
else:
|
|
38
50
|
row_indices = list(rows)
|
|
39
51
|
row_indices = [r + count if r < 0 else r for r in row_indices]
|
|
@@ -47,27 +59,36 @@ def iloc(df, rows=None, cols=None, return_type='q'):
|
|
|
47
59
|
target_cols = [all_cols[i] for i in range(*cols.indices(len(all_cols)))]
|
|
48
60
|
else:
|
|
49
61
|
target_cols = [all_cols[i] for i in cols]
|
|
50
|
-
|
|
51
|
-
if row_indices is None and target_cols is None:
|
|
62
|
+
|
|
63
|
+
if not _use_sublist and row_indices is None and target_cols is None:
|
|
52
64
|
return _handle_return(table, return_type)
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
# Fast path: contiguous row slice via q's sublist (no Python list overhead)
|
|
67
|
+
if _use_sublist:
|
|
68
|
+
sublist_args = kx.LongVector([_slice_start, _slice_count])
|
|
69
|
+
if target_cols is not None:
|
|
70
|
+
syms = kx.SymbolVector(target_cols)
|
|
71
|
+
cols_dict = kx.q('!', syms, syms)
|
|
72
|
+
q_res = kx.q('{?[sublist[y;x];();0b;z]}', table, sublist_args, cols_dict)
|
|
73
|
+
else:
|
|
74
|
+
q_res = kx.q('{sublist[y;x]}', table, sublist_args)
|
|
75
|
+
elif row_indices is not None and target_cols is not None:
|
|
55
76
|
q_query = '{?[x y;();0b;z]}'
|
|
56
77
|
syms = kx.SymbolVector(target_cols)
|
|
57
78
|
cols_dict = kx.q('!', syms, syms)
|
|
58
79
|
q_res = kx.q(q_query, table, kx.LongVector(row_indices), cols_dict)
|
|
59
|
-
|
|
80
|
+
|
|
60
81
|
elif row_indices is not None:
|
|
61
82
|
q_query = '{x y}'
|
|
62
83
|
q_res = kx.q(q_query, table, kx.LongVector(row_indices))
|
|
63
|
-
|
|
84
|
+
|
|
64
85
|
elif target_cols is not None:
|
|
65
86
|
q_query = '{?[x;();0b;y]}'
|
|
66
87
|
syms = kx.SymbolVector(target_cols)
|
|
67
88
|
cols_dict = kx.q('!', syms, syms)
|
|
68
89
|
q_res = kx.q(q_query, table, cols_dict)
|
|
69
|
-
|
|
90
|
+
|
|
70
91
|
else:
|
|
71
92
|
q_res = table
|
|
72
93
|
|
|
73
|
-
return _handle_return(q_res, return_type)
|
|
94
|
+
return _handle_return(q_res, return_type)
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
qutePandas/__init__.py,sha256=
|
|
1
|
+
qutePandas/__init__.py,sha256=USg_a44fbQDKf5Ads1vgqyHNqU6AKrVM5eFfEJhLXrc,3143
|
|
2
2
|
qutePandas/utils.py,sha256=X0GODOBxuAFOtjjFb6IGlzjDhhrkwH_6ErIMypiFhUM,1026
|
|
3
3
|
qutePandas/apply/__init__.py,sha256=tdjeqTQI6jH530lTmpd9dkUPese6B22uI2P4ATKoGzc,92
|
|
4
|
-
qutePandas/apply/apply.py,sha256=
|
|
4
|
+
qutePandas/apply/apply.py,sha256=5Fa4I7_zkILk0TMJioWoAFh9kgrb-jVI4PMBcjIkWmQ,2465
|
|
5
5
|
qutePandas/apply/apply_col.py,sha256=h6nAsAXLIC4WcMD-amW3bXkaNEe30SgKEBfUr3eKTng,1597
|
|
6
6
|
qutePandas/cleaning/__init__.py,sha256=6tRzTPgCEumZ7c_TvsMit3pRxZhdzf1Pd4y23ozv_2k,204
|
|
7
7
|
qutePandas/cleaning/dropna.py,sha256=SGIPg8vk4PB1jtnH8hDComuMz8omdraJhOPzKTRiUgI,739
|
|
8
8
|
qutePandas/cleaning/dropna_col.py,sha256=hpR4BN-ZuYdz222aUlYKyrEA7QmYomAMqTK6ebVB0ro,831
|
|
9
|
-
qutePandas/cleaning/fillna.py,sha256=
|
|
9
|
+
qutePandas/cleaning/fillna.py,sha256=9PDZCM1hyLtxFkVfQz7NJu1xrjDSW-nPQU47hysfDTI,1689
|
|
10
10
|
qutePandas/cleaning/remove_duplicates.py,sha256=zf6TTbkuSh5S4ZCmgznPLl-erw-umVP7K0EqOyv9wUM,757
|
|
11
11
|
qutePandas/core/__init__.py,sha256=o8WuiBhI5jBqKXTL80kBTNcxZo1aO45dQx2Z6UwWg50,311
|
|
12
12
|
qutePandas/core/connection.py,sha256=CsBWI2jwIO1kNicA42AefmlRgVx5WqayCJPhHzJHhFc,4275
|
|
13
|
-
qutePandas/core/dataframe.py,sha256=
|
|
13
|
+
qutePandas/core/dataframe.py,sha256=ehfz8lMIV_Yj1f9bMpY25TH3EMbR02Dvji9lhGg395E,3209
|
|
14
14
|
qutePandas/core/display.py,sha256=ubSZM8yf0kARuiSXpUHJ0WmIt4syjkp-VuV2NAXbiGc,5058
|
|
15
15
|
qutePandas/grouping/__init__.py,sha256=GOBRAWDuh3PnKHEVDzmEqoG_GIfU4G6Gtk0aeSG92lE,116
|
|
16
16
|
qutePandas/grouping/groupby_avg.py,sha256=F8Gc6XdOKiWyNKOoDURgYeeIxIduzbds2mHCfxEtvw4,1129
|
|
17
17
|
qutePandas/grouping/groupby_sum.py,sha256=9XZ2tITeCMRtdnpwH2CpoQh3Y3NDC-ufwiDg6EQUJOA,1128
|
|
18
18
|
qutePandas/indexing/__init__.py,sha256=2MmNlO54FOdIaaPSzegum3QmeO-Ts8ya3xSCodQjTIo,45
|
|
19
|
-
qutePandas/indexing/iloc.py,sha256=
|
|
19
|
+
qutePandas/indexing/iloc.py,sha256=g0LYKZAqdVk6Xz--RUdb-VtECnzFRET28qh6KL5SRgE,3117
|
|
20
20
|
qutePandas/indexing/loc.py,sha256=Xn_043CrKyJBTXhRUYWtU0ZF1yikIGxTI0gTJAYIEfA,1511
|
|
21
21
|
qutePandas/introspection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
qutePandas/introspection/dtypes.py,sha256=h3qImsu3JBuELp2zeIfwyOlFTsC513U_qSRNuYzeXWw,705
|
|
@@ -29,7 +29,7 @@ qutePandas/transformation/__init__.py,sha256=ECPmFXZq-uh5ajkgAVQbdc8flVzrcwkhEqw
|
|
|
29
29
|
qutePandas/transformation/cast.py,sha256=VD8D0gPs9GJX52bqu_LKt7BHU69Iv-scOq-E2Q_MGQU,2226
|
|
30
30
|
qutePandas/transformation/drop_col.py,sha256=4hdGjitDbYSfxj-4jo73NmTa2qKed-NjGj0jGBfiANY,1509
|
|
31
31
|
qutePandas/transformation/rename.py,sha256=MwWjbhaZCUdQgkObaMPIB8fUY240x_EdD41cwszCa4o,948
|
|
32
|
-
qutepandas-1.1.
|
|
33
|
-
qutepandas-1.1.
|
|
34
|
-
qutepandas-1.1.
|
|
35
|
-
qutepandas-1.1.
|
|
32
|
+
qutepandas-1.1.2.dist-info/METADATA,sha256=C49oFk4j_KXUAABQfcYnZeSJaxmBKhly2t_Cv_aaF_Y,2540
|
|
33
|
+
qutepandas-1.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
34
|
+
qutepandas-1.1.2.dist-info/top_level.txt,sha256=O_6KkoxW8KjgZX5o7sYwYVb8XPtNBA7PRSTE08qda1k,11
|
|
35
|
+
qutepandas-1.1.2.dist-info/RECORD,,
|
|
File without changes
|