Skip to content

Commit

Permalink
Added implementation of round (#21)
Browse files Browse the repository at this point in the history
* Added round implementation, tests and documentation

* Typo in round documentation

* Type checking with isinstance

* fix linting issues

* fix linting issues

* reworked  round implementation and tests

* Refactored column updates and fixed typos
  • Loading branch information
nipsn authored Jan 22, 2024
1 parent 4df1ac7 commit dad1e0b
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
62 changes: 62 additions & 0 deletions docs/user-guide/advanced/Pandas_API.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2472,6 +2472,68 @@
"tab.abs(numeric_only=True)"
]
},
{
"cell_type": "markdown",
"id": "0c056fd9-fe7b-43d5-b1c7-7ceec3cae5ff",
"metadata": {},
"source": [
"### Table.round()\n",
"\n",
"```\n",
"Table.round(self, decimals: Union[int, Dict[str, int]] = 0)\n",
"```\n",
"\n",
"Round a Table to a variable number of decimal places.\n",
"\n",
"**Parameters:**\n",
"\n",
"| Name | Type | Description | Default |\n",
"| :--------------: | :-----------------: | :------------------------------------------------------------ | :-----: |\n",
"| decimals | int or Dict | Number of decimal places to round each column to. If an int is given, round each real or float column to the same number of places. Otherwise, dict rounds to variable numbers of places. Column names should be in the keys if decimals parameter is a dict-like and the decimals to round should be the value. Any columns not included in decimals will be left as is. Elements of decimals which are not columns of the input will be ignored.| 0 |\n",
"\n",
"**Returns:**\n",
"\n",
"| Type | Description |\n",
"| :--------: | :--------------------------------------------------------------------------------------- |\n",
"| Table | A Table with the affected columns rounded to the specified number of decimal places. |"
]
},
{
"cell_type": "markdown",
"id": "1b629def",
"metadata": {},
"source": [
"If an integer is provided it rounds every float column to set decimals."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "08c182c9",
"metadata": {},
"outputs": [],
"source": [
"tab.round(1)"
]
},
{
"cell_type": "markdown",
"id": "28853fc0",
"metadata": {},
"source": [
"If a dict whose keys are the column names and its values are the decimals to round set column is provided, it will round them accordingly.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7640df4c",
"metadata": {},
"outputs": [],
"source": [
"tab.round({\"price\": 1, \"traded\": 0})"
]
},
{
"cell_type": "markdown",
"id": "cbcdf84e",
Expand Down
46 changes: 46 additions & 0 deletions src/pykx/pandas_api/pandas_meta.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Dict, Union

from . import api_return
from ..exceptions import QError

Expand Down Expand Up @@ -104,6 +106,28 @@ def inner(*args, **kwargs):
'': b'kx.List'}


# Define the mapping between the returns of kx.*Vector.t and the associated typechar
_typenum_to_typechar_mapping = {0: '',
1: 'b',
2: 'g',
4: 'x',
5: 'h',
6: 'i',
7: 'j',
8: 'e',
9: 'f',
10: 'c',
11: 's',
12: 'p',
14: 'd',
15: 'z',
16: 'n',
17: 'u',
18: 'v',
19: 't',
13: 'm'}


class PandasMeta:
# Dataframe properties
@property
Expand Down Expand Up @@ -265,6 +289,28 @@ def abs(self, numeric_only=False):
tab = _get_numeric_only_subtable(self)
return q.abs(tab)

@api_return
def round(self, decimals: Union[int, Dict[str, int]] = 0):
tab = self
if 'Keyed' in str(type(tab)):
tab = q.value(tab)

affected_cols = _get_numeric_only_subtable(tab).columns.py()
type_dict = {col: _typenum_to_typechar_mapping[tab[col].t] for col in affected_cols}

cast_back = q('{string[y][0]$x}')

if isinstance(decimals, int):
dec_dict = {col: decimals for col in affected_cols}
else:
dec_dict = {col: decimals[col] for col in affected_cols}

rounded = {col: [cast_back(round(elem, dec_dict[col]), type_dict[col])
for elem in tab[col]]
for col in dec_dict}

return q.qsql.update(tab, columns=rounded)

@convert_result
def all(self, axis=0, bool_only=False, skipna=True):
res, cols = preparse_computations(self, axis, skipna, bool_only=bool_only)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_pandas_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,41 @@ def test_pandas_abs(kx, q):
tab.abs()


def test_pandas_round(kx, q):
q_tab = q('([]c1:4 5 10 15 20 25h;'
'c2:4 5 10 15 20 25i;'
'c3:4 5 10 15 20 25j;'
'c4:0 0.10 0.25 0.30 0.45 0.50e;'
'c5:0 0.10 0.25 0.30 0.45 0.50f;'
'c6:`a`b`c`d`e`f)')
p_tab = q_tab.pd()

pd.testing.assert_frame_equal(p_tab.round(),
q_tab.round().pd())

pd.testing.assert_frame_equal(q_tab.round(0).pd(),
q_tab.round().pd())

pd.testing.assert_frame_equal(p_tab.round(2),
q_tab.round(2).pd())

pd.testing.assert_frame_equal(p_tab.round(-1),
q_tab.round(-1).pd())

dict_test = {'c1': -2,
'c2': -1,
'c3': -0,
'c4': 1,
'c5': 2,
'c6': 3,
'c7': 4}

q_res = q_tab.round(dict_test)
pd.testing.assert_frame_equal(p_tab.round(dict_test), q_res.pd())

pd.testing.assert_frame_equal(q_tab.dtypes.pd(), q_res.dtypes.pd())


def test_pandas_min(q):
tab = q('([] sym: 100?`foo`bar`baz`qux; price: 250.0f - 100?500.0f; ints: 100 - 100?200)')
df = tab.pd()
Expand Down

0 comments on commit dad1e0b

Please sign in to comment.