diff --git a/README.md b/README.md index 74f9492..ef86761 100644 --- a/README.md +++ b/README.md @@ -93,14 +93,15 @@ KX only officially supports versions of PyKX built by KX, i.e. versions of PyKX PyKX depends on the following third-party Python packages: -- `pandas>=1.2, < 2.0; python_version=='3.8'` -- `pandas>=1.2, < 2.2.0; python_version>'3.8'` +- `pandas>=1.2, <2.0; python_version=='3.8'` +- `pandas>=1.2, <=2.2.3; python_version>'3.8'` - `numpy~=1.22, <2.0; python_version<'3.11'` - `numpy~=1.23, <2.0; python_version=='3.11'` - `numpy~=1.26, <2.0; python_version=='3.12'` - `pytz>=2022.1` - `toml~=0.10.2` - `dill>=0.2.0` +- `requests>=2.25.0` They are installed automatically by `pip` when PyKX is installed. @@ -206,4 +207,4 @@ If you have any issues or questions you can post them to [community.kx.com](http ## Customer Support * Inquires or feedback: [`pykx@kx.com`](mailto:pykx@kx.com) -* Support for Licensed Subscribers: [support.kx.com](https://support.kx.com/support/home) +* Support for Licensed Subscribers: [support.kx.com](https://client.support.kx.com/) diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 5956fea..1288c3f 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -28,7 +28,7 @@ requirements: run: - python - numpy>=1.22,<2.0 - - pandas>=1.2, <2.2.0 # [py>38] + - pandas>=1.2, <=2.2.3 # [py>38] - pandas<2.0 # [py==38] - pytz>=2022.1 - toml>=0.10.2 diff --git a/docs/examples/charting.ipynb b/docs/examples/charting.ipynb index dc21cb8..9820119 100644 --- a/docs/examples/charting.ipynb +++ b/docs/examples/charting.ipynb @@ -183,7 +183,7 @@ "id": "2e76c5d1-7dd3-482c-90cb-c263d31ad808", "metadata": {}, "source": [ - "To make the column values compatible with most Matplotlib charts, first convert them to NumPy objects using the `.np()` function." + "To make the column values compatible with most [Matplotlib charts](https://matplotlib.org/), first convert them to NumPy objects using the `.np()` function." ] }, { @@ -215,7 +215,7 @@ "source": [ "## Plotly\n", "\n", - "Plotly allows you to pass `vector` objects as the `color` argument. Set this parameter using the `sym` column to obtain the scatter chart below.\n" + "[Plotly](https://plotly.com/python/) allows you to pass `vector` objects as the `color` argument. Set this parameter using the `sym` column to obtain the scatter chart below.\n" ] }, { @@ -290,7 +290,7 @@ "source": [ "## Seaborn\n", "\n", - "Seaborn allows you to set `data` as a PyKX table name without conversions and then call the `x` and `y` parameters using only the column names of that table.\n", + "[Seaborn](https://seaborn.pydata.org/) allows you to set `data` as a PyKX table name without conversions and then call the `x` and `y` parameters using only the column names of that table.\n", "\n", "The bar chart below demonstrates this by setting the data as the table object and using the column names for all parameters, without any conversions." ] @@ -314,6 +314,7 @@ ], "source": [ "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", "\n", "sns.catplot(\n", " kind='bar',\n", diff --git a/docs/examples/interface-overview.ipynb b/docs/examples/interface-overview.ipynb index 939450c..ca797b9 100644 --- a/docs/examples/interface-overview.ipynb +++ b/docs/examples/interface-overview.ipynb @@ -8,9 +8,9 @@ "\n", "_The purpose of this notebook is to introduce you to PyKX capabilities and functionality._\n", "\n", - "For the best experience, visit [what is PyKX](https://code.kx.com/pykx/2.5/getting-started/what_is_pykx.html) and the [quickstart guide](https://code.kx.com/pykx/2.5/getting-started/quickstart.html) first.\n", + "For the best experience, visit [what is PyKX](../getting-started/what_is_pykx.html) and the [quickstart guide](../getting-started/quickstart.html) first.\n", "\n", - "To follow along, we recommend to download the notebook. \n", + "To follow along, we recommend to download the notebook. \n", "\n", "Now let's go through the following sections:\n", "\n", diff --git a/docs/examples/jupyter-integration.ipynb b/docs/examples/jupyter-integration.ipynb index c908059..ddc8dfd 100644 --- a/docs/examples/jupyter-integration.ipynb +++ b/docs/examples/jupyter-integration.ipynb @@ -446,7 +446,7 @@ "id": "dec08871-a771-4848-8283-f88206f54785", "metadata": {}, "source": [ - "In this state, you can execute Python code as well, but those cells must include `%%python`." + "In this state, you can execute Python code as well, but those cells must include `%%py`." ] }, { @@ -456,7 +456,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%python\n", + "%%py\n", "for fruit in ['apple', 'orange', 'banana']:\n", " print(fruit)" ] @@ -476,7 +476,7 @@ "metadata": {}, "outputs": [], "source": [ - "%%python\n", + "%%py\n", "kx.util.jupyter_qfirst_disable()" ] }, diff --git a/docs/examples/streaming/index.md b/docs/examples/streaming/index.md index 83331c4..01d5fba 100644 --- a/docs/examples/streaming/index.md +++ b/docs/examples/streaming/index.md @@ -10,7 +10,7 @@ tags: PyKX, q, streaming, basic _This page outlines the steps taken and functionality shown in demonstrating your first PyKX streaming application_ -To run this example please download the [zip](./real-time-pykx.zip) file containing the notebook or visit our github repository [here](https://github.com/pykx/docs/examples/streaming) to view the code directly. +To run this example please download the [zip](./real-time-pykx.zip) file containing the notebook or visit our github repository [here](https://github.com/KxSystems/pykx/tree/main/docs/examples/streaming) to view the code directly. In this example we will generate a real-time and historical analysis system which completes the following actions: diff --git a/docs/getting-started/installing.md b/docs/getting-started/installing.md index adb021b..168492a 100644 --- a/docs/getting-started/installing.md +++ b/docs/getting-started/installing.md @@ -254,14 +254,15 @@ This command should display the installed version of PyKX. PyKX depends on the following third-party Python packages: - - `numpy~=1.22, <2.0; python_version<'3.11'` - - `numpy~=1.23, <2.0; python_version=='3.11'` - - `numpy~=1.26, <2.0; python_version=='3.12'` - - `pandas>=1.2, < 2.0; python_version=='3.8'` - - `pandas>=1.2, < 2.2.0; python_version>'3.8'` - - `pytz>=2022.1` - - `toml~=0.10.2` - - `dill>=0.2.0` + - `pandas>=1.2, <2.0; python_version=='3.8'` + - `pandas>=1.2, <=2.2.3; python_version>'3.8'` + - `numpy~=1.22, <2.0; python_version<'3.11'` + - `numpy~=1.23, <2.0; python_version=='3.11'` + - `numpy~=1.26, <2.0; python_version=='3.12'` + - `pytz>=2022.1` + - `toml~=0.10.2` + - `dill>=0.2.0` + - `requests>=2.25.0` **Note**: All are installed automatically by `#!bash pip` when you install PyKX. diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 3fd2757..f46487f 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -122,7 +122,7 @@ You can generate PyKX objects in three ways. Click on the tabs below to follow t ## 3. Interact with PyKX objects -You can interact with PyKX objects in a variety of ways, for example, through [indexing using Pythonic syntax](../user-guide/fundamentals/indexing.md), passing [PyKX objects to q/NumPy](../user-guide/fundamentals/creating.md#converting-pykx-objects-to-pythonic-types) functions, [querying via SQL/qSQL](https://code.kx.com/pykx/user-guide/fundamentals/querying.html) syntax or by [using the q functionality](https://code.kx.com/pykx/user-guide/advanced/context_interface.html) via the context interface. Each way is described in more depth under the the User guide > Fundamentals section. For now, we recommend a few examples: +You can interact with PyKX objects in a variety of ways, for example, through [indexing using Pythonic syntax](../user-guide/fundamentals/indexing.md), passing [PyKX objects to q/NumPy](../user-guide/fundamentals/creating.md#converting-pykx-objects-to-pythonic-types) functions, [querying via Python/SQL/qSQL](..//user-guide/fundamentals/query/index.md) syntax or by [using the q functionality](../user-guide/advanced/context_interface.md) via the context interface. Each way is described in more depth under the the User guide > Fundamentals section. For now, we recommend a few examples: * Create a PyKX list and interact with it using indexing and slices: diff --git a/docs/help/issues.md b/docs/help/issues.md index 54da94b..f9c712f 100644 --- a/docs/help/issues.md +++ b/docs/help/issues.md @@ -120,3 +120,44 @@ q)f[] q)f[::] /equivalent 7 ``` + +## Pandas + +### Known issues +#### Changes in `DataFrame.equals` behavior from Pandas 2.2.0 + +In Pandas 2.2.0, a difference was introduced in how `DataFrame.equals` handles DataFrames with different `_mgr` types. + +**Example:** +```python +>>> import pandas as pd +>>> import pykx as kx + +>>> df1 = pd.DataFrame({'cl': ['foo']}) +>>> df2 = kx.q('([] cl:enlist `foo)').pd() + +>>> df2.equals(df1) +True + +>>> df1.equals(df2) # Prior to Pandas 2.2.0, this would also evaluate to True +False +``` + +**Cause:** +Pandas now checks the type of the `_mgr` (dataframes manager) property. PyKX uses a custom `_mgr` implementation for performance optimization. + +```python +>>> type(df1._mgr) + + +>>> type(df2._mgr) + +``` + +**Workaround:** +Comparing the full contents of DataFrames irrespective of `_mgr` types works regardless of order of df1 and df2 in the comparison. To do so, use one of the following approaches: +```python +>>> assert (df2 == df1).all().all() # Element-wise comparison +>>> pd.testing.assert_frame_equal(df2, df1) # Pandas' built-in test +>>> assert df1.compare(df2).empty # Check if there are no differences +``` diff --git a/docs/help/support.md b/docs/help/support.md index 4ba8b27..5363819 100644 --- a/docs/help/support.md +++ b/docs/help/support.md @@ -19,4 +19,4 @@ If you have any issues or questions you can post them to the following locations ## Customer Support * Inquires or feedback: [`pykx@kx.com`](mailto:pykx@kx.com) -* Support for Licensed Subscribers: [support.kx.com](https://support.kx.com/support/home) +* Support for Licensed Subscribers: [support.kx.com](https://client.support.kx.com/) diff --git a/docs/help/troubleshooting.md b/docs/help/troubleshooting.md index 5679479..74cff45 100644 --- a/docs/help/troubleshooting.md +++ b/docs/help/troubleshooting.md @@ -54,10 +54,10 @@ It usually indicates that your license was not correctly written to disk or a li Please consider reinstalling your license using pykx.util.install_license On disk license: - b'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYG\nZasG5s8vevfAI2ttndt//Y2th\nrryoQRm9Dy+DIIcmSufwomL+\nPMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJY\npB\n66lq9OiZCEdq2GFJLCn2T\nNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS\n3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+\nLaCnJM0yqNjW9xGyog5ml\nbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF\n' + b'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYGZasG5s8vevfAI2ttndt//Y2thrryoQRm9Dy+DIIcmSufwomL+PMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJYpB66lq9OiZCEdq2GFJLCn2TNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+LaCnJM0yqNjW9xGyog5mlbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF' Supplied string content: - b'8n\nD+HkcJ93xW4oOEtH\nIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BL\nA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyY\ne+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1\n/BOXsD\n2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng2\n83NKe13huPU\nohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+r\nJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+\n" + b'8nD+HkcJ93xW4oOEtHIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BLA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyYe+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1/BOXsD2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng283NKe13huPUohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+rJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+' False ``` @@ -67,7 +67,7 @@ It usually indicates that your license was not correctly written to disk or a li ```python >>> import pykx as kx - >>> license_string = 'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYG\nZasG5s8vevfAI2ttndt//Y2th\nrryoQRm9Dy+DIIcmSufwomL+\nPMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJY\npB\n66lq9OiZCEdq2GFJLCn2T\nNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS\n3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+\nLaCnJM0yqNjW9xGyog5ml\nbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF\n' + >>> license_string = 'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYGZasG5s8vevfAI2ttndt//Y2thrryoQRm9Dy+DIIcmSufwomL+PMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJYpB66lq9OiZCEdq2GFJLCn2TNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+LaCnJM0yqNjW9xGyog5mlbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF' >>> kx.license.check(license_string, format = 'STRING') True ``` @@ -76,16 +76,16 @@ It usually indicates that your license was not correctly written to disk or a li ```python >>> import pykx as kx - >>> license_string = '8n\nD+HkcJ93xW4oOEtH\nIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BL\nA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyY\ne+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1\n/BOXsD\n2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng2\n83NKe13huPU\nohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+r\nJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+\n' + >>> license_string = '8nD+HkcJ93xW4oOEtHIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BLA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyYe+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1/BOXsD2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng283NKe13huPUohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+rJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+' >>> kx.license.check(license_string, format = 'STRING') Supplied license information does not match. Please consider reinstalling your license using pykx.util.install_license On disk license: - b'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYG\nZasG5s8vevfAI2ttndt//Y2th\nrryoQRm9Dy+DIIcmSufwomL+\nPMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJY\npB\n66lq9OiZCEdq2GFJLCn2T\nNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS\n3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+\nLaCnJM0yqNjW9xGyog5ml\nbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF\n' + b'Atc/wy/gMjZgIdn1KlT3JVWfVmPk55dtb0YJVes5V4ed9Zxt9UVr8G/A1Q3aWiQEkfjGbwvlJU3GXpUergObvzxGN1iyYGZasG5s8vevfAI2ttndt//Y2thrryoQRm9Dy+DIIcmSufwomL+PMJkZacYc9DM6ipnQsL0KvLwLXLrQC1fBLV2pZHCdYC/nX/KM6uslgip4EoTxZTcx1pQPyTx56QKD4K4JBNimO929w/0+v4Hy2x+DIS3n89vpGmtVvjjFRQtsF6Sjnd+6RnFGk13hRL/DlqHTv2XbZgVv++YOCIc7G55KL6PVJYpB66lq9OiZCEdq2GFJLCn2TNWGJPT2s1YDAKsAPI5W3PqJkC2UeV17gPG4gxlCSHr0kfacINbEJ0kSTm/UsuEBZ5B/jvR/jU7rFErcd9PECeQA1kXB19fa4hgvbd+SxWTPxMUKbiHThHk6X0Bi3T7WAQ+sZWsEWwkMncd+mOGS3D+bRav2nfOpKckj8rCdvYum3U8PDv6IHP=S+LaCnJM0yqNjW9xGyog5mlbX2k3mBRyBjbJH/1OWTcIg7uDYxxoMtDOCJjeBdSqI=aK+5FVTVarfowvudv7QsMGeohGaJMyczNWVPPjsbyvsxbAwdXvJUuP0jcFCFVeF' Supplied string content: - b'8n\nD+HkcJ93xW4oOEtH\nIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BL\nA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyY\ne+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1\n/BOXsD\n2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng2\n83NKe13huPU\nohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+r\nJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+\n' + b'8nD+HkcJ93xW4oOEtHIZxeWkA1glv5wJ5wE2Fsmbc4lg2ntT9JpsclE1hFeG/Ox/jM4=6GjXD2VNpiCAJ80DNVcXuDB+IPEnP22DMGvBIolJt2pdy9kooGZNQpr6svIkRWX/0m/SbydbQOQUVvfNTxsDjZvvsCiGkdQtygs3sDEJbxsT+KfjqJ7Sd6RQ/47HJHG4JyIWdhmvEBVGSLBa5mdAaCLWdCrga3hHZbW3F4e/l3K4nOQvU91WEiMd6PT061r66AOYmjGACCXqmQ9kSsJfMTXPRi9M2i93Oyv895kFVKdZCLCdKdaow790RcjwnKjFFOERGcge=lZdRtp2BLA+JbixvTIKTObmfqr7uPYsGQLfXSFnQCq7jbt3yxv1ZPjvjYLPTx7YKIvgo+ITG6vyYe+cfwaW1g0tlvFTcVSVb/sxUvvLCLiWMdxGjt5JUxV3GaSm9ysHVk5MrTDpp/5qqXes1/BOXsD2DmS/QSZr/Mt+Vc2baKuxPw1w5YnGVuY6vHxHffABzkn+WPcguabr86JcmIAcC0zc2TLkbufBPJewYka9PIt1Ng283NKe13huPUohnryYVIMPyjrTWpDid+yC5kSGVeP0/5+rJvLmFZUB/n0RUjgMZU5V++GPU1QnCBa+' False ``` diff --git a/docs/index.md b/docs/index.md index 751c225..9f86e17 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,7 +14,7 @@ To begin your journey with PyKX, follow the sections below: !!! home-page "[Get Started](./getting-started/installing.md)" - This section is for new users. Includes everything you need to begin your journey with PyKX: [installation instructions](../docs/getting-started/installing.md) and a [quickstart guide](../docs/getting-started/quickstart.md). + This section is for new users. Includes everything you need to begin your journey with PyKX: [installation instructions](./getting-started/installing.md) and a [quickstart guide](./getting-started/quickstart.md). !!! home-page "[Learn](./getting-started/what_is_pykx.md)" @@ -38,4 +38,4 @@ To begin your journey with PyKX, follow the sections below: !!! home-page "[Help and Support](./help/troubleshooting.md)" - Get assistance with any questions or issues you might have. This section includes [troubleshooting](../docs/help/troubleshooting.md) guides, [FAQs](../docs/help/faq.md), and [support](../docs/help/support.md) contact information. \ No newline at end of file + Get assistance with any questions or issues you might have. This section includes [troubleshooting](./help/troubleshooting.md) guides, [FAQs](./help/faq.md), and [support](./help/support.md) contact information. diff --git a/docs/learn/objects.md b/docs/learn/objects.md index 3c39de5..af138f3 100644 --- a/docs/learn/objects.md +++ b/docs/learn/objects.md @@ -1,7 +1,7 @@ --- title: PyKX Objects and Attributes description: Objects explained for PyKX -date: Septmeber 2024 +date: September 2024 author: KX Systems, Inc., tags: PyKX, object --- diff --git a/docs/pykx-under-q/api.md b/docs/pykx-under-q/api.md index 5ced6a8..20a3d9e 100644 --- a/docs/pykx-under-q/api.md +++ b/docs/pykx-under-q/api.md @@ -47,6 +47,7 @@ q)\l pykx.q [print print a Python object directly to stdout](#pykxprint) [repr evaluate the Python function repr() on supplied Python object](#pykxrepr) [debugInfo print useful process debug information to q session](#pykxdebuginfo) +[loadPy load a .py/.p script executed according to Python script execution logic](#pykxloadpy) **Data Conversions:** [setdefault define the default conversion for KX objects to Python](#pykxsetdefault) @@ -333,11 +334,37 @@ q)`dash in key `.pykx 1b ``` -**Parameter:** +## `.pykx.loadPy` -|Name|Type|Description| -|---|---|---| -|ext||| + +_Load and execute a .p/.py file manually_ + +```q +.pykx.loadPy["file.py"] +``` + +**Parameters:** + +name | type | description +---------|----------|------------- +`fname` | `string` | The name of the python file to be executed + +**Returns:** + +type | description | +-----|-------------| +`::` | Returns generic null on successful execution otherwise returns the error message raised + +**Example:** + +```q +q)\cat file.py +"def func(x):" +" return x+1" +q).pykx.loadPy["file.py"] +q).pykx.get[`func][10]` +11 +``` ## `.pykx.print` @@ -843,10 +870,11 @@ type | description | [Pandas](https://pandas.pydata.org/docs/user_guide/index.html) | `"pd", "pandas", "Pandas"` | [Python](https://docs.python.org/3/library/datatypes.html) | `"py", "python", "Python"` | [PyArrow](https://arrow.apache.org/docs/python/index.html) | `"pa", "pyarrow", "PyArrow"` | - K | `"k", "q"` | + [K](type_conversions.md) | `"k", "q"` | raw | `"raw"` | default | `"default"` | + ```q // Default value on startup is "default" q).pykx.util.defaultConv diff --git a/docs/pykx-under-q/intro.md b/docs/pykx-under-q/intro.md index e3e1e17..c02b7c5 100644 --- a/docs/pykx-under-q/intro.md +++ b/docs/pykx-under-q/intro.md @@ -163,6 +163,23 @@ q).pykx.get[`func] {[f;x].pykx.util.pykx[f;x]}[foreign]enlist ``` +In some cases the `#!python .p` file being loaded does not contain syntax which can be parsed by q (used by `q)\l test.p` example), as such there is available a `#!q .pykx.loadPy` function: + +```q +$ cat test.p +def func(x, + y +): -> None + return x+y +$ q pykx.q +q)\l test.p +'SyntaxError('unexpected EOF while parsing... +q).pykx.loadPy["test.p"] +q)f:.pykx.get[`func;<] +q)f[1;2] +3 +``` + ### Interact with PyKX objects #### Foreign objects @@ -760,6 +777,7 @@ x x1 0.2296615 0.1959907 0.6919531 0.375638 ``` + ## Next steps - Use the [pykx.q Library Reference Card](../pykx-under-q/api.md). diff --git a/docs/release-notes/changelog.md b/docs/release-notes/changelog.md index 47f3f70..e786ada 100644 --- a/docs/release-notes/changelog.md +++ b/docs/release-notes/changelog.md @@ -4,6 +4,79 @@ The changelog presented here outlines changes to PyKX when operating within a Python environment specifically, if you require changelogs associated with PyKX operating under a q environment see [here](./underq-changelog.md). +## PyKX 3.0.1 + +#### Release Date + +2024-12-04 + +!!! Note + + PyKX 3.0.1 is currently not available for Mac x86/ARM for all Python versions. Updated builds will be provided once available. To install PyKX 3.0.1 on Mac please install from source [here](https://github.com/kxsystems/pykx). + +### Additions + +- Addition of the property `#!python day` to `#!python kx.Column` objects to allow users to retrieve the day of month of a timestamp. + + ```python + >>> import pykx as kx + >>> tab = kx.Table(data={ + ... 'a': kx.random.random(100, kx.TimestampAtom.inf), + ... 'b': kx.random.random([100, 3], 10.0) + ... }) + >>> tab.exec(kx.Column('a').day) + pykx.IntVector(pykx.q('7 10 12..')) + ``` + +### Fixes and Improvements + +- Added warning to `kx.q.system.load` and context registration when attempting to load path with a space. Can be suppressed by enabling `PYKX_SUPPRESS_WARNINGS`. +- Changed `%%python` heading to `%%py` when calling Python code during `jupyter_qfirst` mode so as not to conflict with inbuilt Jupyter cell magics. +- Fixed `kx.license.check(format='string')` to remove newline characters during comparison. +- Configuration file `.pykx-config` now supports use of boolean toml configuration + + ```python + $ cat ~/.pykx-config + [default] + PYKX_QDEBUG = true + $ python + >>> import pykx as kx + >>> kx.config.pykx_qdebug + True + ``` + +- Reintroduced unsetting/setting of `PYKX_SKIP_UNDERQ` in `PyKXReimport` these had been removed in the 3.0.0 release. +- Added type checking for the `cast` flag when calling `kx.toq()` or creating a `kx.K` variable such as `kx.FloatVector()` or `kx.DatetimeAtom()`. +- Removed the need to enable `PYKX_BETA_FEATURES` to use `pykx_threading`. +- Fixed a memory leak when calling `pickle.loads` on a `PyKX` object which previously had been called with `pickle.dumps`. +- Removal of column `type` from the return of `#!python dtypes` method for `#!python kx.Table` objects, previously this had raised a deprecation warning + +=== "Behavior prior to change" + + ```python + >>> tab = kx.q('([] a:1 2 3j;b:4 5 6i)') + >>> tab.dtypes + pykx.Table(pykx.q(' + columns datatypes type + ----------------------------------- + a "kx.LongAtom" "kx.LongAtom" + b "kx.IntAtom" "kx.IntAtom" + ')) + ``` + +=== "Behavior post change" + + ```python + >>> tab = kx.q('([] a:1 2 3j;b:4 5 6i)') + >>> tab.dtypes + pykx.Table(pykx.q(' + columns datatypes + --------------------- + a "kx.LongAtom" + b "kx.IntAtom" + ')) + ``` + ## PyKX 3.0.0 #### Release Date @@ -1066,12 +1139,21 @@ /usr/python/site-packages/pykx/config.py: RuntimeWarning: '-t' argument unsupported in QARGS.. ``` -- PyKX Pandas dependency for Python 3.8 has been clamped to `<2.0` due to support being dropped for it by Pandas after 2.0.3. - ### Upgrade considerations - PyKX 3.0.0 is a major version update which includes changes requiring review before upgrading from 2.5.*. A page details these changes in full [here](../upgrades/2030.md) +## PyKX 2.5.5 + +#### Release Date + +2024-11-28 + +### Fixes and Improvements + +- PyKX Pandas dependency has been raised to allow `<=2.2.3` for Python>3.8 +- PyKX Pandas dependency for Python 3.8 has been clamped to `<2.0` due to support being dropped for it by Pandas after 2.0.3. + ## PyKX 2.5.4 #### Release Date diff --git a/docs/release-notes/underq-changelog.md b/docs/release-notes/underq-changelog.md index 749ee50..e5f5d7a 100644 --- a/docs/release-notes/underq-changelog.md +++ b/docs/release-notes/underq-changelog.md @@ -6,6 +6,28 @@ This changelog provides updates from PyKX 2.0.0 and above, for information relat The changelog presented here outlines changes to PyKX when operating within a q environment specifically, if you require changelogs associated with PyKX operating within a Python environment see [here](./changelog.md). +## PyKX 3.0.1 + +#### Release Date + +2024-12-04 + +### Additions + +- Added a function `.pykx.loadPy` which can be used to execute/load a Python file following expected Python syntax, this removes limitations which exist due to use of q parsing syntax when loading a Python file using `system"l /path/to/file.p"` for example + + ```q + q)\cat /path/to/file.p + "def func(x: int," + " y: int" + "): -> None" + " return x+y" + q).pykx.loadPy["/path/to/file.p"] + q)f:.pykx.get[`func;<] + q)f[10;20] + 30 + ``` + ## PyKX 3.0.0 #### Release Date diff --git a/docs/upgrades/2030.md b/docs/upgrades/2030.md index 39a0d98..9edd74d 100644 --- a/docs/upgrades/2030.md +++ b/docs/upgrades/2030.md @@ -139,3 +139,13 @@ The links below outline the full before and after behaviour. - [Null Conversions](../user-guide/fundamentals/nulls_and_infinities.md#null-conversions). - [Infinite Conversions](../user-guide/fundamentals/nulls_and_infinities.md#infinite-conversions). + +## Pandas 2.2.X Update + +PyKX now works with Pandas 2.2.X, introducing some breaking changes in behavior. Specifically, the `.equals` method now checks the `_mgr` type of DataFrames, which can result in unilateral behavior when comparing PyKX and Pandas objects. + +These changes may affect compatibility with code written for earlier versions of Pandas. + +The link below outline the full details of the changes and their implications. + +- [Pandas Changes](../help/issues.md#pandas). \ No newline at end of file diff --git a/docs/user-guide/advanced/Pandas_API.ipynb b/docs/user-guide/advanced/Pandas_API.ipynb index 73b9836..3a6dccb 100644 --- a/docs/user-guide/advanced/Pandas_API.ipynb +++ b/docs/user-guide/advanced/Pandas_API.ipynb @@ -23,85 +23,85 @@ "\n", "| **DataFrame properties**| **PyKX supported?** | **PyKX API documentation link** | \n", "|----------------------|-----------------|-----------------------------|\n", - "| [columns](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.columns.html) | :material-check: | [link](Pandas_API.ipynb#tablecolumns) | \n", - "| [dtypes](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dtypes.html) | :material-check: | [link](https://code.kx.com/pykx/2.2/user-guide/advanced/Pandas_API.html#tabledtypes) |\n", - "| [empty](https://https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.empty.html) | :material-check: | [link](https://code.kx.com/pykx/2.2/user-guide/advanced/Pandas_API.html#tableempty) |\n", - "| [ndim](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.ndim.html) | :material-check: | [link](https://code.kx.com/pykx/2.2/user-guide/advanced/Pandas_API.html#tablendim) |\n", - "| [shape](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.shape.html) | :material-check: | [link](https://code.kx.com/pykx/2.2/user-guide/advanced/Pandas_API.html#tableshape) |\n", - "| [size](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.size.html) | :material-check: | [link](https://code.kx.com/pykx/2.2/user-guide/advanced/Pandas_API.html#tablesize) |\n", + "| [columns](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.columns.html) | :material-check: | [link](Pandas_API.html#tablecolumns) | \n", + "| [dtypes](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dtypes.html) | :material-check: | [link](Pandas_API.html#tabledtypes) |\n", + "| [empty](https://https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.empty.html) | :material-check: | [link](Pandas_API.html#tableempty) |\n", + "| [ndim](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.ndim.html) | :material-check: | [link](Pandas_API.html#tablendim) |\n", + "| [shape](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.shape.html) | :material-check: | [link](Pandas_API.html#tableshape) |\n", + "| [size](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.size.html) | :material-check: | [link](Pandas_API.html#tablesize) |\n", "\n", "### Analytic functionality\n", "\n", "| **DataFrame method** | **PyKX supported?** | **PyKX API documentation link** |\n", "|----------------------|-----------------|-----------------------------|\n", - "| [abs](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.abs.html) | :material-check: | [link](Pandas_API.ipynb#tableabs) |\n", - "| [agg](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.agg.html) | :material-check: | [link](Pandas_API.ipynb#tableagg) |\n", - "| [apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html) | :material-check: | [link](Pandas_API.ipynb#tableapply) |\n", - "| [applymap](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.applymap.html) | :material-check: | [link](Pandas_API.ipynb#tableapplymap) |\n", - "| [groupby](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html) | :material-check: | [link](Pandas_API.ipynb#tablegroupby) |\n", - "| [idxmax](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmax.html) | :material-check: | [link](Pandas_API.ipynb#tableidxmax) |\n", - "| [idxmin](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmin.html) | :material-check: | [link](Pandas_API.ipynb#tableidxmin) |\n", - "| [kurt](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.kurt.html) | :material-check: | [link](Pandas_API.ipynb#tablekurt) |\n", - "| [max](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.max.html) | :material-check: | [link](Pandas_API.ipynb#tablemax) |\n", - "| [map](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.map.html) | :material-check: | [link](Pandas_API.ipynb#tablemap) |\n", - "| [mean](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mean.html) | :material-check: | [link](Pandas_API.ipynb#tablemean) |\n", - "| [median](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.median.html) | :material-check: | [link](Pandas_API.ipynb#tablemedian) |\n", - "| [min](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.min.html) | :material-check: | [link](Pandas_API.ipynb#tablemin) |\n", - "| [mode](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mode.html) | :material-check: | [link](Pandas_API.ipynb#tablemode) |\n", - "| [sem](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sem.html) | :material-check: | [link](Pandas_API.ipynb#tablesem) |\n", - "| [sum](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sum.html) | :material-check: | [link](Pandas_API.ipynb#tablesum) |\n", - "| [skew](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.skew.html) | :material-check: | [link](Pandas_API.ipynb#tableskew) |\n", - "| [std](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.std.html) | :material-check: | [link](Pandas_API.ipynb#tablestd) |\n", - "| [prod](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.prod.html) | :material-check: | [link](Pandas_API.ipynb#tableprod) |\n", + "| [abs](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.abs.html) | :material-check: | [link](Pandas_API.html#tableabs) |\n", + "| [agg](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.agg.html) | :material-check: | [link](Pandas_API.html#tableagg) |\n", + "| [apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html) | :material-check: | [link](Pandas_API.html#tableapply) |\n", + "| [applymap](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.applymap.html) | :material-check: | [link](Pandas_API.html#tableapplymap) |\n", + "| [groupby](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html) | :material-check: | [link](Pandas_API.html#tablegroupby) |\n", + "| [idxmax](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmax.html) | :material-check: | [link](Pandas_API.html#tableidxmax) |\n", + "| [idxmin](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.idxmin.html) | :material-check: | [link](Pandas_API.html#tableidxmin) |\n", + "| [kurt](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.kurt.html) | :material-check: | [link](Pandas_API.html#tablekurt) |\n", + "| [max](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.max.html) | :material-check: | [link](Pandas_API.html#tablemax) |\n", + "| [map](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.map.html) | :material-check: | [link](Pandas_API.html#tablemap) |\n", + "| [mean](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mean.html) | :material-check: | [link](Pandas_API.html#tablemean) |\n", + "| [median](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.median.html) | :material-check: | [link](Pandas_API.html#tablemedian) |\n", + "| [min](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.min.html) | :material-check: | [link](Pandas_API.html#tablemin) |\n", + "| [mode](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.mode.html) | :material-check: | [link](Pandas_API.html#tablemode) |\n", + "| [sem](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sem.html) | :material-check: | [link](Pandas_API.html#tablesem) |\n", + "| [sum](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sum.html) | :material-check: | [link](Pandas_API.html#tablesum) |\n", + "| [skew](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.skew.html) | :material-check: | [link](Pandas_API.html#tableskew) |\n", + "| [std](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.std.html) | :material-check: | [link](Pandas_API.html#tablestd) |\n", + "| [prod](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.prod.html) | :material-check: | [link](Pandas_API.html#tableprod) |\n", "\n", "### Querying and data interrogation\n", "\n", "| **DataFrame method** | **PyKX supported?** | **PyKX API documentation link** |\n", "|----------------------|-----------------|-----------------------------|\n", - "| [all](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.all.html) | :material-check: | [link](Pandas_API.ipynb#tableall) |\n", - "| [any](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.any.html) | :material-check: | [link](Pandas_API.ipynb#tableany) |\n", - "| [at](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.at.html) | :material-check: | [link](Pandas_API.ipynb#tableat) |\n", - "| [count](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.count.html) | :material-check: | [link](Pandas_API.ipynb#tablecount) |\n", - "| [get](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.get.html) | :material-check: | [link](Pandas_API.ipynb#tableget) |\n", - "| [head](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.head.html) | :material-check: | [link](Pandas_API.ipynb#tablehead) |\n", - "| [iloc](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) | :material-check: | [link](Pandas_API.ipynb#tableiloc) |\n", - "| [isna](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isna.html) | :material-check: | [link](Pandas_API.ipynb#tableisna) |\n", - "| [isnull](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isnull.html) | :material-check: | [link](Pandas_API.ipynb#tableisnull) |\n", - "| [loc](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html) | :material-check: | [link](Pandas_API.ipynb#tableloc) |\n", - "| [notna](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.notna.html) | :material-check: | [link](Pandas_API.ipynb#tablenotna) |\n", - "| [notnull](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.notnull.html) | :material-check: | [link](Pandas_API.ipynb#tablenotnull) |\n", - "| [sample](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sample.html) | :material-check: | [link](Pandas_API.ipynb#tablesample) |\n", - "| [select_dtypes](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.select_dtypes.html) | :material-check: | [link](Pandas_API.ipynb#tableselect_dtypes) |\n", - "| [tail](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.tail.html) | :material-check: | [link](Pandas_API.ipynb#tabletail) |\n", + "| [all](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.all.html) | :material-check: | [link](Pandas_API.html#tableall) |\n", + "| [any](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.any.html) | :material-check: | [link](Pandas_API.html#tableany) |\n", + "| [at](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.at.html) | :material-check: | [link](Pandas_API.html#tableat) |\n", + "| [count](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.count.html) | :material-check: | [link](Pandas_API.html#tablecount) |\n", + "| [get](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.get.html) | :material-check: | [link](Pandas_API.html#tableget) |\n", + "| [head](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.head.html) | :material-check: | [link](Pandas_API.html#tablehead) |\n", + "| [iloc](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) | :material-check: | [link](Pandas_API.html#tableiloc) |\n", + "| [isna](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isna.html) | :material-check: | [link](Pandas_API.html#tableisna) |\n", + "| [isnull](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.isnull.html) | :material-check: | [link](Pandas_API.html#tableisnull) |\n", + "| [loc](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html) | :material-check: | [link](Pandas_API.html#tableloc) |\n", + "| [notna](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.notna.html) | :material-check: | [link](Pandas_API.html#tablenotna) |\n", + "| [notnull](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.notnull.html) | :material-check: | [link](Pandas_API.html#tablenotnull) |\n", + "| [sample](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sample.html) | :material-check: | [link](Pandas_API.html#tablesample) |\n", + "| [select_dtypes](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.select_dtypes.html) | :material-check: | [link](Pandas_API.html#tableselect_dtypes) |\n", + "| [tail](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.tail.html) | :material-check: | [link](Pandas_API.html#tabletail) |\n", "\n", "### Data preprocessing\n", "\n", "| **DataFrame method** | **PyKX supported?** | **PyKX API documentation link** |\n", "|----------------------|-----------------|-----------------------------|\n", - "| [add_prefix](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.add_prefix.html) | :material-check: | [link](Pandas_API.ipynb#tableas_prefix) |\n", - "| [add_suffix](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.add_suffix.html) | :material-check: | [link](Pandas_API.ipynb#tableas_suffix) |\n", - "| [astype](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.astype.html) | :material-check: | [link](Pandas_API.ipynb#tableastype) |\n", - "| [drop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html) | :material-check: | [link](Pandas_API.ipynb#tabledrop) |\n", - "| [drop_duplicates](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html) | :material-check: | [link](Pandas_API.ipynb#tabledrop_duplicates) |\n", - "| [pop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pop.html) | :material-check: | [link](Pandas_API.ipynb#tablepop) |\n", - "| [rename](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rename.html) | :material-check: | [link](Pandas_API.ipynb#tablerename) |\n", - "| [reset_index](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.reset_index.html) | :material-check: | [link](Pandas_API.ipynb#tablereset_index) |\n", - "| [set_index](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.set_index.html) | :material-check: | [link](Pandas_API.ipynb#tableset_index) |\n", + "| [add_prefix](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.add_prefix.html) | :material-check: | [link](Pandas_API.html#tableas_prefix) |\n", + "| [add_suffix](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.add_suffix.html) | :material-check: | [link](Pandas_API.html#tableas_suffix) |\n", + "| [astype](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.astype.html) | :material-check: | [link](Pandas_API.html#tableastype) |\n", + "| [drop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html) | :material-check: | [link](Pandas_API.html#tabledrop) |\n", + "| [drop_duplicates](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html) | :material-check: | [link](Pandas_API.html#tabledrop_duplicates) |\n", + "| [pop](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pop.html) | :material-check: | [link](Pandas_API.html#tablepop) |\n", + "| [rename](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.rename.html) | :material-check: | [link](Pandas_API.html#tablerename) |\n", + "| [reset_index](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.reset_index.html) | :material-check: | [link](Pandas_API.html#tablereset_index) |\n", + "| [set_index](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.set_index.html) | :material-check: | [link](Pandas_API.html#tableset_index) |\n", "\n", "### Data joins/merge\n", "\n", "| **DataFrame method** | **PyKX supported?** | **PyKX API documentation link** |\n", "|----------------------|-----------------|-----------------------------|\n", - "| [merge](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html) | :material-check: | [link](Pandas_API.ipynb#tablemerge) |\n", - "| [merge_asof](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge_asof.html) | :material-check: | [link](Pandas_API.ipynb#tablemerge_asof) |\n", + "| [merge](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html) | :material-check: | [link](Pandas_API.html#tablemerge) |\n", + "| [merge_asof](https://pandas.pydata.org/docs/reference/api/pandas.merge_asof.html) | :material-check: | [link](Pandas_API.html#tablemerge_asof) |\n", "\n", "### Data sorting\n", "\n", "| **DataFrame method** | **PyKX supported?** | **PyKX API documentation link** | \n", "|----------------------|-----------------|-----------------------------|\n", - "| [sort_values](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html) | :material-check: | [link](Pandas_API.ipynb#tablesort_values) |\n", - "| [nlargest](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.nlargest.html) | :material-check: | [link](Pandas_API.ipynb#tablenlargest) |\n", - "| [nsmallest](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.nsmallest.html) | :material-check: | [link](Pandas_API.ipynb#tablensmallest) |\n", + "| [sort_values](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html) | :material-check: | [link](Pandas_API.html#tablesort_values) |\n", + "| [nlargest](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.nlargest.html) | :material-check: | [link](Pandas_API.html#tablenlargest) |\n", + "| [nsmallest](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.nsmallest.html) | :material-check: | [link](Pandas_API.html#tablensmallest) |\n", "\n", "### Unsupported functionality\n", "\n", @@ -2533,7 +2533,7 @@ "id": "30738846", "metadata": {}, "source": [ - "Calculate the idxmax across the rows of a table using only columns thar are of a numeric data type" + "Calculate the idxmax across the rows of a table using only columns that are of a numeric data type" ] }, { @@ -2599,7 +2599,7 @@ "id": "fb531e00", "metadata": {}, "source": [ - "Calculate the idxmin across the rows of a table using only columns thar are of a numeric data type" + "Calculate the idxmin across the rows of a table using only columns that are of a numeric data type" ] }, { diff --git a/docs/user-guide/advanced/performance.md b/docs/user-guide/advanced/performance.md index ea38f75..0db62c2 100644 --- a/docs/user-guide/advanced/performance.md +++ b/docs/user-guide/advanced/performance.md @@ -3,7 +3,7 @@ title: PyKX Performance description: How to optimize PyKX date: July 2024 author: KX Systems, Inc., -tags: PyKX, q, perfromance, paralellization, secondary q threads, multithreading, peach +tags: PyKX, q, performance, parallelization, secondary q threads, multithreading, peach --- # Performance tips diff --git a/docs/user-guide/advanced/remote-functions.md b/docs/user-guide/advanced/remote-functions.md index 10c3118..6a7c99c 100644 --- a/docs/user-guide/advanced/remote-functions.md +++ b/docs/user-guide/advanced/remote-functions.md @@ -3,7 +3,7 @@ title: PyKX Remote Functions description: How to execute Python functions on q servers in PyKX date: July 2024 author: KX Systems, Inc., -tags: PyKX, q, perfromance, paralellization, secondary q threads, multithreading, peach +tags: PyKX, q, performance, parallelization, secondary q threads, multithreading, peach --- # Remote Function Execution diff --git a/docs/user-guide/advanced/streaming/complex.md b/docs/user-guide/advanced/streaming/complex.md index 7e4657b..3bb7fb4 100644 --- a/docs/user-guide/advanced/streaming/complex.md +++ b/docs/user-guide/advanced/streaming/complex.md @@ -35,9 +35,9 @@ In the [basic infrastructure](basic.md) section we made use of the function [`#! While the single-call basic infrastructure is useful, you might want to load these process types on separate virtual/physical machines. For example, you might consider loading your RDB on a process with significantly higher RAM requirements to your HDB, where user queries are limited in expected RAM by well-controlled APIs. -A full breakdown of the APIs for each of these process types is provided in the dropdown for the API documentation below. +A full breakdown of the APIs for each of these process types is provided in the dropdown for the API documentation below. -To manually generate a [basic infrastructure](basic.md) using the individual APIs, follow the steps bellow: +To manually generate a [basic infrastructure](basic.md) using the individual APIs, follow the steps below: 1. Start the Tickerplant process by defining the `#!python trade` and `#!python aggregate` tables: diff --git a/docs/user-guide/fundamentals/nulls_and_infinities.md b/docs/user-guide/fundamentals/nulls_and_infinities.md index 750a7c6..7924ec7 100644 --- a/docs/user-guide/fundamentals/nulls_and_infinities.md +++ b/docs/user-guide/fundamentals/nulls_and_infinities.md @@ -43,7 +43,7 @@ pykx.GUIDAtom(pykx.q('00000000-0000-0000-0000-000000000000')) pykx.SymbolAtom(pykx.q('`')) ``` -Unsupported values return a `#!python NotImplemetedError` as below: +Unsupported values return a `#!python NotImplementedError` as below: ```python >>> import pykx as kx diff --git a/docs/user-guide/fundamentals/query/pyquery.md b/docs/user-guide/fundamentals/query/pyquery.md index 0400d2c..e129603 100644 --- a/docs/user-guide/fundamentals/query/pyquery.md +++ b/docs/user-guide/fundamentals/query/pyquery.md @@ -487,7 +487,7 @@ By default this parameter has a value `None` which is equivalent to not filterin #### by -The [by phrase](https://code.kx.com/q/basics/qsql/#aggregates) allows you to apply aggregations or manipulate data grouping the data `by` specific conditions. +The [by phrase](https://code.kx.com/q/basics/qsql/#aggregates) allows you to apply aggregations or manipulate data grouping the data `by` specific conditions. By default this parameter has a value `None` which is equivalent to not grouping your data. This parameter is supported for `select`, `exec` and `update` type queries. @@ -510,7 +510,7 @@ date sym | price .. ')) ``` -Adding an aggregation function allows this aggregation to be run on a column within the `by` phrase +Adding an aggregation function allows this aggregation to be run on a column within the `by` phrase. ```python >>> trades.select(columns=kx.Column('price').max(), by=kx.Column('date') & kx.Column('sym')) @@ -549,7 +549,7 @@ AAPL 2022.01.02 140.0383 54 56.09317 ??? Note "What happens without a columns clause" - Using `by` without an associated `columns` clause will return the last row in the table for each column in the `by` phrase. + Using `by` without an associated `columns` clause will return the last row in the table for each column in the `by` phrase. ```python >>> trades.select(by=kx.Column('sym')) @@ -672,7 +672,7 @@ Using `&` on two `Column` objects will return a `QueryPhrase` which describes th >>> qp =(kx.Column('sym') == 'GOOG') & (kx.Column('price') > 500) >>> type(qp) ->>> qp.phrase +>>> qp._phrase [[pykx.Operator(pykx.q('=')), 'sym', [pykx.SymbolAtom(pykx.q('`GOOG'))]], [pykx.Operator(pykx.q('>')), 'price', pykx.LongAtom(pykx.q('500'))]] >>> trades.select(where=qp) pykx.Table(pykx.q(' @@ -697,7 +697,7 @@ Using `|` on two `Column` objects will return a `Column` object. >>> c =(kx.Column('price') < 100) | (kx.Column('price') > 500) >>> type(c) ->>> c.phrase +>>> c._value [pykx.Operator(pykx.q('|')), [pykx.Operator(pykx.q('<')), 'price', pykx.LongAtom(pykx.q('100'))], [pykx.Operator(pykx.q('>')), 'price', pykx.LongAtom(pykx.q('500'))]] >>> trades.select(where=c) pykx.Table(pykx.q(' @@ -712,9 +712,9 @@ MSFT 2022.01.03 539.6816 ')) ``` -!!! Note "`or` / `|` operator restriction" +!!! Note "`or` operator `|` restriction" - `Column` objects can not apply `or` or `|` off a `QueryPhrase`. Presently these are restricted only to operations on two `kx.Column` phrases. + `Column` objects can not apply `|` off a `QueryPhrase`. Presently these are restricted only to operations on two `kx.Column` objects. #### Python operators @@ -787,7 +787,7 @@ The following drop-down provides a list of the supported methods, with full deta ??? Note "Supported methods" - [`abs`](../../../api/columns.md#pykx.wrappers.Column.abs), [`acos`](../../../api/columns.md#pykx.wrappers.Column.acos), [`asc`](../../../api/columns.md#pykx.wrappers.Column.asc), [`asin`](../../../api/columns.md#pykx.wrappers.Column.asin), [`atan`](../../../api/columns.md#pykx.wrappers.Column.atan), [`avg`](../../../api/columns.md#pykx.wrappers.Column.avg), [`avgs`](../../../api/columns.md#pykx.wrappers.Column.avgs), [`ceiling`](../../../api/columns.md#pykx.wrappers.Column.ceiling), [`cor`](../../../api/columns.md#pykx.wrappers.Column.cor), [`cos`](../../../api/columns.md#pykx.wrappers.Column.cos), [`count`](../../../api/columns.md#pykx.wrappers.Column.count), [`cov`](../../../api/columns.md#pykx.wrappers.Column.cov), [`cross`](../../../api/columns.md#pykx.wrappers.Column.cross), [`deltas`](../../../api/columns.md#pykx.wrappers.Column.deltas), [`desc`](../../../api/columns.md#pykx.wrappers.Column.desc), [`dev`](../../../api/columns.md#pykx.wrappers.Column.dev), [`differ`](../../../api/columns.md#pykx.wrappers.Column.differ), [`distinct`](../../../api/columns.md#pykx.wrappers.Column.distinct), [`div`](../../../api/columns.md#pykx.wrappers.Column.div), [`exp`](../../../api/columns.md#pykx.wrappers.Column.exp), [`fills`](../../../api/columns.md#pykx.wrappers.Column.fills), [`first`](../../../api/columns.md#pykx.wrappers.Column.first), [`floor`](../../../api/columns.md#pykx.wrappers.Column.floor), [`null`](../../../api/columns.md#pykx.wrappers.Column.null), [`iasc`](../../../api/columns.md#pykx.wrappers.Column.iasc), [`idesc`](../../../api/columns.md#pykx.wrappers.Column.idesc), [`inter`](../../../api/columns.md#pykx.wrappers.Column.inter), [`isin`](../../../api/columns.md#pykx.wrappers.Column.isin), [`last`](../../../api/columns.md#pykx.wrappers.Column.last), [`like`](../../../api/columns.md#pykx.wrappers.Column.like), [`log`](../../../api/columns.md#pykx.wrappers.Column.log), [`lower`](../../../api/columns.md#pykx.wrappers.Column.lower), [`ltrim`](../../../api/columns.md#pykx.wrappers.Column.ltrim), [`mavg`](../../../api/columns.md#pykx.wrappers.Column.mavg), [`max`](../../../api/columns.md#pykx.wrappers.Column.max), [`maxs`](../../../api/columns.md#pykx.wrappers.Column.maxs), [`mcount`](../../../api/columns.md#pykx.wrappers.Column.mcount), [`md5`](../../../api/columns.md#pykx.wrappers.Column.md5), [`mdev`](../../../api/columns.md#pykx.wrappers.Column.mdev), [`med`](../../../api/columns.md#pykx.wrappers.Column.med), [`min`](../../../api/columns.md#pykx.wrappers.Column.min), [`mins`](../../../api/columns.md#pykx.wrappers.Column.mins), [`mmax`](../../../api/columns.md#pykx.wrappers.Column.mmax), [`mmin`](../../../api/columns.md#pykx.wrappers.Column.mmin), [`mod`](../../../api/columns.md#pykx.wrappers.Column.mod), [`msum`](../../../api/columns.md#pykx.wrappers.Column.msum), [`neg`](../../../api/columns.md#pykx.wrappers.Column.neg), [`prd`](../../../api/columns.md#pykx.wrappers.Column.prd), [`prds`](../../../api/columns.md#pykx.wrappers.Column.prds), [`prev`](../../../api/columns.md#pykx.wrappers.Column.prev), [`rank`](../../../api/columns.md#pykx.wrappers.Column.rank), [`ratios`](../../../api/columns.md#pykx.wrappers.Column.ratios), [`reciprocal`](../../../api/columns.md#pykx.wrappers.Column.reciprocal), [`reverse`](../../../api/columns.md#pykx.wrappers.Column.reverse), [`rotate`](../../../api/columns.md#pykx.wrappers.Column.rotate), [`rtrim`](../../../api/columns.md#pykx.wrappers.Column.rtrim), [`scov`](../../../api/columns.md#pykx.wrappers.Column.scov), [`sdev`](../../../api/columns.md#pykx.wrappers.Column.sdev), [`signum`](../../../api/columns.md#pykx.wrappers.Column.signum), [`sin`](../../../api/columns.md#pykx.wrappers.Column.sin), [`sqrt`](../../../api/columns.md#pykx.wrappers.Column.sqrt), [`string`](../../../api/columns.md#pykx.wrappers.Column.string), [`sum`](../../../api/columns.md#pykx.wrappers.Column.sum), [`sums`](../../../api/columns.md#pykx.wrappers.Column.sums), [`svar`](../../../api/columns.md#pykx.wrappers.Column.svar), [`tan`](../../../api/columns.md#pykx.wrappers.Column.tan), [`trim`](../../../api/columns.md#pykx.wrappers.Column.trim), [`union`](../../../api/columns.md#pykx.wrappers.Column.union), [`upper`](../../../api/columns.md#pykx.wrappers.Column.upper), [`var`](../../../api/columns.md#pykx.wrappers.Column.var), [`wavg`](../../../api/columns.md#pykx.wrappers.Column.wavg), [`within`](../../../api/columns.md#pykx.wrappers.Column.within), [`wsum`](../../../api/columns.md#pykx.wrappers.Column.wsum), [`xbar`](../../../api/columns.md#pykx.wrappers.Column.xbar), [`xexp`](../../../api/columns.md#pykx.wrappers.Column.xexp), [`xlog`](../../../api/columns.md#pykx.wrappers.Column.xlog), [`xprev`](../../../api/columns.md#pykx.wrappers.Column.xprev), [`hour`](../../../api/columns.md#pykx.wrappers.Column.hour), [`minute`](../../../api/columns.md#pykx.wrappers.Column.minute), [`date`](../../../api/columns.md#pykx.wrappers.Column.date), [`year`](../../../api/columns.md#pykx.wrappers.Column.year), [`month`](../../../api/columns.md#pykx.wrappers.Column.month), [`second`](../../../api/columns.md#pykx.wrappers.Column.second), [`add`](../../../api/columns.md#pykx.wrappers.Column.add), [`name`](../../../api/columns.md#pykx.wrappers.Column.name), [`average`](../../../api/columns.md#pykx.wrappers.Column.average), [`cast`](../../../api/columns.md#pykx.wrappers.Column.cast), [`correlation`](../../../api/columns.md#pykx.wrappers.Column.correlation), [`covariance`](../../../api/columns.md#pykx.wrappers.Column.covariance), [`divide`](../../../api/columns.md#pykx.wrappers.Column.divide), [`drop`](../../../api/columns.md#pykx.wrappers.Column.drop), [`fill`](../../../api/columns.md#pykx.wrappers.Column.fill), [`index_sort`](../../../api/columns.md#pykx.wrappers.Column.index_sort), [`join`](../../../api/columns.md#pykx.wrappers.Column.join), [`len`](../../../api/columns.md#pykx.wrappers.Column.len), [`modulus`](../../../api/columns.md#pykx.wrappers.Column.modulus), [`multiply`](../../../api/columns.md#pykx.wrappers.Column.multiply), [`next_item`](../../../api/columns.md#pykx.wrappers.Column.next_item), [`previous_item`](../../../api/columns.md#pykx.wrappers.Column.previous_item), [`product`](../../../api/columns.md#pykx.wrappers.Column.product), [`products`](../../../api/columns.md#pykx.wrappers.Column.products), [`sort`](../../../api/columns.md#pykx.wrappers.Column.sort), [`subract`](../../../api/columns.md#pykx.wrappers.Column.subract), [`take`](../../../api/columns.md#pykx.wrappers.Column.take), [`value`](../../../api/columns.md#pykx.wrappers.Column.value) and [`variance`](../../../api/columns.md#pykx.wrappers.Column.variance). + [`abs`](../../../api/columns.md#pykx.wrappers.Column.abs), [`acos`](../../../api/columns.md#pykx.wrappers.Column.acos), [`asc`](../../../api/columns.md#pykx.wrappers.Column.asc), [`asin`](../../../api/columns.md#pykx.wrappers.Column.asin), [`atan`](../../../api/columns.md#pykx.wrappers.Column.atan), [`avg`](../../../api/columns.md#pykx.wrappers.Column.avg), [`avgs`](../../../api/columns.md#pykx.wrappers.Column.avgs), [`ceiling`](../../../api/columns.md#pykx.wrappers.Column.ceiling), [`cor`](../../../api/columns.md#pykx.wrappers.Column.cor), [`cos`](../../../api/columns.md#pykx.wrappers.Column.cos), [`count`](../../../api/columns.md#pykx.wrappers.Column.count), [`cov`](../../../api/columns.md#pykx.wrappers.Column.cov), [`cross`](../../../api/columns.md#pykx.wrappers.Column.cross), [`deltas`](../../../api/columns.md#pykx.wrappers.Column.deltas), [`desc`](../../../api/columns.md#pykx.wrappers.Column.desc), [`dev`](../../../api/columns.md#pykx.wrappers.Column.dev), [`differ`](../../../api/columns.md#pykx.wrappers.Column.differ), [`distinct`](../../../api/columns.md#pykx.wrappers.Column.distinct), [`div`](../../../api/columns.md#pykx.wrappers.Column.div), [`exp`](../../../api/columns.md#pykx.wrappers.Column.exp), [`fills`](../../../api/columns.md#pykx.wrappers.Column.fills), [`first`](../../../api/columns.md#pykx.wrappers.Column.first), [`floor`](../../../api/columns.md#pykx.wrappers.Column.floor), [`null`](../../../api/columns.md#pykx.wrappers.Column.null), [`iasc`](../../../api/columns.md#pykx.wrappers.Column.iasc), [`idesc`](../../../api/columns.md#pykx.wrappers.Column.idesc), [`inter`](../../../api/columns.md#pykx.wrappers.Column.inter), [`isin`](../../../api/columns.md#pykx.wrappers.Column.isin), [`last`](../../../api/columns.md#pykx.wrappers.Column.last), [`like`](../../../api/columns.md#pykx.wrappers.Column.like), [`log`](../../../api/columns.md#pykx.wrappers.Column.log), [`lower`](../../../api/columns.md#pykx.wrappers.Column.lower), [`ltrim`](../../../api/columns.md#pykx.wrappers.Column.ltrim), [`mavg`](../../../api/columns.md#pykx.wrappers.Column.mavg), [`max`](../../../api/columns.md#pykx.wrappers.Column.max), [`maxs`](../../../api/columns.md#pykx.wrappers.Column.maxs), [`mcount`](../../../api/columns.md#pykx.wrappers.Column.mcount), [`md5`](../../../api/columns.md#pykx.wrappers.Column.md5), [`mdev`](../../../api/columns.md#pykx.wrappers.Column.mdev), [`med`](../../../api/columns.md#pykx.wrappers.Column.med), [`min`](../../../api/columns.md#pykx.wrappers.Column.min), [`mins`](../../../api/columns.md#pykx.wrappers.Column.mins), [`mmax`](../../../api/columns.md#pykx.wrappers.Column.mmax), [`mmin`](../../../api/columns.md#pykx.wrappers.Column.mmin), [`mod`](../../../api/columns.md#pykx.wrappers.Column.mod), [`msum`](../../../api/columns.md#pykx.wrappers.Column.msum), [`neg`](../../../api/columns.md#pykx.wrappers.Column.neg), [`prd`](../../../api/columns.md#pykx.wrappers.Column.prd), [`prds`](../../../api/columns.md#pykx.wrappers.Column.prds), [`prev`](../../../api/columns.md#pykx.wrappers.Column.prev), [`rank`](../../../api/columns.md#pykx.wrappers.Column.rank), [`ratios`](../../../api/columns.md#pykx.wrappers.Column.ratios), [`reciprocal`](../../../api/columns.md#pykx.wrappers.Column.reciprocal), [`reverse`](../../../api/columns.md#pykx.wrappers.Column.reverse), [`rotate`](../../../api/columns.md#pykx.wrappers.Column.rotate), [`rtrim`](../../../api/columns.md#pykx.wrappers.Column.rtrim), [`scov`](../../../api/columns.md#pykx.wrappers.Column.scov), [`sdev`](../../../api/columns.md#pykx.wrappers.Column.sdev), [`signum`](../../../api/columns.md#pykx.wrappers.Column.signum), [`sin`](../../../api/columns.md#pykx.wrappers.Column.sin), [`sqrt`](../../../api/columns.md#pykx.wrappers.Column.sqrt), [`string`](../../../api/columns.md#pykx.wrappers.Column.string), [`sum`](../../../api/columns.md#pykx.wrappers.Column.sum), [`sums`](../../../api/columns.md#pykx.wrappers.Column.sums), [`svar`](../../../api/columns.md#pykx.wrappers.Column.svar), [`tan`](../../../api/columns.md#pykx.wrappers.Column.tan), [`trim`](../../../api/columns.md#pykx.wrappers.Column.trim), [`union`](../../../api/columns.md#pykx.wrappers.Column.union), [`upper`](../../../api/columns.md#pykx.wrappers.Column.upper), [`var`](../../../api/columns.md#pykx.wrappers.Column.var), [`wavg`](../../../api/columns.md#pykx.wrappers.Column.wavg), [`within`](../../../api/columns.md#pykx.wrappers.Column.within), [`wsum`](../../../api/columns.md#pykx.wrappers.Column.wsum), [`xbar`](../../../api/columns.md#pykx.wrappers.Column.xbar), [`xexp`](../../../api/columns.md#pykx.wrappers.Column.xexp), [`xlog`](../../../api/columns.md#pykx.wrappers.Column.xlog), [`xprev`](../../../api/columns.md#pykx.wrappers.Column.xprev), [`hour`](../../../api/columns.md#pykx.wrappers.Column.hour), [`minute`](../../../api/columns.md#pykx.wrappers.Column.minute), [`date`](../../../api/columns.md#pykx.wrappers.Column.date), [`year`](../../../api/columns.md#pykx.wrappers.Column.year), [`month`](../../../api/columns.md#pykx.wrappers.Column.month), [`day`](../../../api/columns.md#pykx.wrappers.Column.day), [`second`](../../../api/columns.md#pykx.wrappers.Column.second), [`add`](../../../api/columns.md#pykx.wrappers.Column.add), [`name`](../../../api/columns.md#pykx.wrappers.Column.name), [`average`](../../../api/columns.md#pykx.wrappers.Column.average), [`cast`](../../../api/columns.md#pykx.wrappers.Column.cast), [`correlation`](../../../api/columns.md#pykx.wrappers.Column.correlation), [`covariance`](../../../api/columns.md#pykx.wrappers.Column.covariance), [`divide`](../../../api/columns.md#pykx.wrappers.Column.divide), [`drop`](../../../api/columns.md#pykx.wrappers.Column.drop), [`fill`](../../../api/columns.md#pykx.wrappers.Column.fill), [`index_sort`](../../../api/columns.md#pykx.wrappers.Column.index_sort), [`join`](../../../api/columns.md#pykx.wrappers.Column.join), [`len`](../../../api/columns.md#pykx.wrappers.Column.len), [`modulus`](../../../api/columns.md#pykx.wrappers.Column.modulus), [`multiply`](../../../api/columns.md#pykx.wrappers.Column.multiply), [`next_item`](../../../api/columns.md#pykx.wrappers.Column.next_item), [`previous_item`](../../../api/columns.md#pykx.wrappers.Column.previous_item), [`product`](../../../api/columns.md#pykx.wrappers.Column.product), [`products`](../../../api/columns.md#pykx.wrappers.Column.products), [`sort`](../../../api/columns.md#pykx.wrappers.Column.sort), [`subtract`](../../../api/columns.md#pykx.wrappers.Column.subtract), [`take`](../../../api/columns.md#pykx.wrappers.Column.take), [`value`](../../../api/columns.md#pykx.wrappers.Column.value) and [`variance`](../../../api/columns.md#pykx.wrappers.Column.variance). The following provides a complex example of a user generated query to calculate trade statistics and time-weighted average spread information associated with a Trade and Quote tables making use of the following methods. @@ -797,7 +797,7 @@ The following provides a complex example of a user generated query to calculate - [`max`](../../../api/columns.md#pykx.wrappers.Column.max) - [`min`](../../../api/columns.md#pykx.wrappers.Column.min) - [`isin`](../../../api/columns.md#pykx.wrappers.Column.isin) -- [`within`](../../../api/columns.md#pykx.wrappers.Column.witin) +- [`within`](../../../api/columns.md#pykx.wrappers.Column.within) - [`avg`](../../../api/columns.md#pykx.wrappers.Column.avg) - [`name`](../../../api/columns.md#pykx.wrappers.Column.name) diff --git a/docs/user-guide/fundamentals/text.md b/docs/user-guide/fundamentals/text.md index 1fcd316..11940c7 100644 --- a/docs/user-guide/fundamentals/text.md +++ b/docs/user-guide/fundamentals/text.md @@ -34,7 +34,7 @@ pykx.SymbolAtom(pykx.q('`test string')) pykx.CharVector(pykx.q('"test string"')) ``` -Alternatively, you use the automatic conversion function `#!python pykx.toq` which takes an incoming Python type and converts it to its analagous PyKX type. The following table shows the mapping between the two types: +Alternatively, you use the automatic conversion function `#!python pykx.toq` which takes an incoming Python type and converts it to its analogous PyKX type. The following table shows the mapping between the two types: | **Python Type**| **PyKX Type** | |-------------|-----------------------------------| diff --git a/pyproject.toml b/pyproject.toml index fa7634e..1a5cb6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ doc = [ "pymdown-extensions>=9.3", "matplotlib", "seaborn", - "kaleido", + "kaleido~=0.2.0", ] debug = [ "find-libpython~=0.2", diff --git a/src/pykx/__init__.py b/src/pykx/__init__.py index 6fbea2b..2aa5e91 100644 --- a/src/pykx/__init__.py +++ b/src/pykx/__init__.py @@ -241,6 +241,10 @@ def _register(self, ('.q', '.k'), ('', '_') )]) + if " " in str(path) and not suppress_warnings: + warn("""Space detected in path being loaded. + PyKX will change directory to the path parent and then load directly before returning to current working directory. + To turn off this warning set PYKX_SUPPRESS_WARNINGS to True.""", exceptions.PyKXWarning) # noqa: E501 if name is None: # defaults to filename at end of path sans extension name = path.stem prev_ctx = self._call('string system"d"', wait=True) @@ -318,7 +322,7 @@ def paths(self, paths: List[Union[str, Path]]): from .db import DB from .tick import TICK from .ipc import AsyncQConnection, QConnection, QFuture, RawQConnection, SecureQConnection, SyncQConnection # noqa -from .config import qargs, qhome, qlic +from .config import qargs, qhome, qlic, suppress_warnings from .wrappers import * from .wrappers import CharVector, K diff --git a/src/pykx/_wrappers.pyx b/src/pykx/_wrappers.pyx index 48f56db..a16853b 100644 --- a/src/pykx/_wrappers.pyx +++ b/src/pykx/_wrappers.pyx @@ -179,7 +179,9 @@ cpdef k_unpickle(x): cdef bytes as_bytes = x.tobytes() cdef core.K kx = core.kpn(as_bytes, len(x)) kx.t = 4 # Convert from char vector to byte vector - return factory(core.d9(kx), False) + res = factory(core.d9(kx), False) + core.r0(kx) + return res # We pickle to a Numpy array instead of bytes to benefit from Numpy's highly performant pickling. diff --git a/src/pykx/config.py b/src/pykx/config.py index fdc8112..ebdd6cf 100644 --- a/src/pykx/config.py +++ b/src/pykx/config.py @@ -46,7 +46,11 @@ def _get_config_value(param, default): def _is_enabled(param, cmdflag=False, deprecated=False): - env_config = _get_config_value(param, '').lower() in ('1', 'true') + val = _get_config_value(param, '') + if isinstance(val, bool): + env_config = val + else: + env_config = val.lower() in ('1', 'true') if deprecated and env_config: warn('The environment variable ' + param + ' is deprecated.\n' 'See https://code.kx.com/pykx/user-guide/configuration.html\n' diff --git a/src/pykx/core.pyx b/src/pykx/core.pyx index 79a7a39..4bb1299 100644 --- a/src/pykx/core.pyx +++ b/src/pykx/core.pyx @@ -9,7 +9,7 @@ import sys from . import beta_features from .util import add_to_config, num_available_cores -from .config import tcore_path_location, _is_enabled, _license_install, pykx_threading, _check_beta, _get_config_value, pykx_lib_dir, ignore_qhome, lic_path +from .config import tcore_path_location, _is_enabled, _license_install, pykx_threading, _get_config_value, pykx_lib_dir, ignore_qhome, lic_path def _normalize_qargs(user_args: List[str]) -> Tuple[bytes]: @@ -362,7 +362,6 @@ if not pykx_threading: raise PyKXException( # nocov f'Non-zero qinit return code {qinit_return_code} despite successful pre-check') # nocov else: - _check_beta('PYKX Threading') beta_features.append('PyKX Threading') _libq_path_py = bytes(str(find_core_lib('q')), 'utf-8') _tcore_path = tcore_path_location diff --git a/src/pykx/license.py b/src/pykx/license.py index 091bce4..61de202 100644 --- a/src/pykx/license.py +++ b/src/pykx/license.py @@ -5,7 +5,7 @@ from typing import Optional from . import licensed -from .config import qlic +from .config import lic_type, qlic __all__ = [ @@ -27,7 +27,7 @@ def _init(_q): def check(license: str, *, format: Optional[str] = 'FILE', - license_type: Optional[str] = 'kc.lic' + license_type: Optional[str] = lic_type ) -> bool: """ Validate the license key information you provided matches the license @@ -87,6 +87,9 @@ def check(license: str, if format not in ('file', 'string'): raise Exception('Unsupported option provided for format parameter') + if license_type not in ('kx.lic', 'kc.lic', 'k4.lic'): + raise Exception(f'License type {license_type} not supported.') + license_located = False installed_lic = qlic/license_type if os.path.exists(installed_lic): @@ -98,19 +101,24 @@ def check(license: str, return False with open(installed_lic, 'rb') as f: - license_content = base64.encodebytes(f.read()) + license_content = base64.encodebytes(f.read()).decode('utf-8') + license_content = license_content.replace('\n', '') + license_content = bytes(license_content, 'utf-8') if format == 'file': license_path = Path(os.path.expanduser(license)) if os.path.exists(license_path): with open(str(license_path), 'rb') as f: - license = base64.encodebytes(f.read()) + license = base64.encodebytes(f.read()).decode('utf-8') + license = license.replace('\n', '') else: print(f'Unable to locate license {license_path} for comparison') return False if isinstance(license, str): + license = license.replace('\n', '') license = bytes(license, 'utf-8') + if not license_content == license: print('Supplied license information does not match.\n' 'Please consider reinstalling your license using pykx.util.install_license\n\n' diff --git a/src/pykx/pandas_api/pandas_meta.py b/src/pykx/pandas_api/pandas_meta.py index be3fdd2..bdd6cc9 100644 --- a/src/pykx/pandas_api/pandas_meta.py +++ b/src/pykx/pandas_api/pandas_meta.py @@ -1,5 +1,3 @@ -import warnings - from . import api_return from ..exceptions import QError @@ -116,14 +114,11 @@ def columns(self): @property def dtypes(self): - warnings.warn("dtypes column 'type' is deprecated, please use 'datatypes'", - DeprecationWarning) return q(''' {a:0!x; - d:flip `columns`datatypes!( + flip `columns`datatypes!( a[`c]; - {$[x~"kx.List";x;x,$[y in .Q.a;"Atom";"Vector"]]}'[y `$/:lower a`t;a`t]); - d[`type]:d[`datatypes];d + {$[x~"kx.List";x;x,$[y in .Q.a;"Atom";"Vector"]]}'[y `$/:lower a`t;a`t]) } ''', q.meta(self), _type_mapping) diff --git a/src/pykx/pykx.q b/src/pykx/pykx.q index b5136ea..752b559 100644 --- a/src/pykx/pykx.q +++ b/src/pykx/pykx.q @@ -1660,16 +1660,53 @@ safeReimport:{[x] // @name .pykx.enableJupyter // @overview // Enable qfirst mode in a Jupyter Notebook. - .pykx.enableJupyter:{.pykx.import[`pykx;`:util.jupyter_qfirst_enable][];} // @kind function // @name .pykx.disableJupyter // @overview // Disable qfirst mode in a Jupyter Notebook and return to Python first execution. - .pykx.disableJupyter:{.pykx.import[`pykx;`:util.jupyter_qfirst_disable][];} +// @kind function +// @name .pykx.loadPy +// @category api +// @overview +// _Load and execute a .p/.py file manually_ +// +// ```q +// .pykx.loadPy["file.py"] +// ``` +// +// **Parameters:** +// +// name | type | description +// ---------|----------|------------- +// `fname` | `string` | The name of the python file to be executed +// +// **Returns:** +// +// type | description | +// -----|-------------| +// `::` | Returns generic null on successful execution otherwise returns the error message raised +// +// **Example:** +// +// ```q +// q)\cat file.py +// "def func(x):" +// " return x+1" +// q).pykx.loadPy["file.py"] +// q).pykx.get[`func][10]` +// 11 +// ``` +loadPy:{[file] + if[10h<>type file;'"Parameter 'file' must be of type string"]; + if[not last["." vs file]in("py";enlist"p");'"File extension must be .py/.p"]; + .pykx.pyexec"exec(open('",ssr[file;"\\";"\\\\"],"').read())" + } + + // @kind function // @name .pykx.debugInfo // @category api diff --git a/src/pykx/reimporter.py b/src/pykx/reimporter.py index e08ffe3..fbeacd7 100644 --- a/src/pykx/reimporter.py +++ b/src/pykx/reimporter.py @@ -33,6 +33,7 @@ def __init__(self): self.envlist = ('PYKX_DEFAULT_CONVERSION', 'PYKX_UNDER_Q', 'PYKX_UNDER_PYTHON', + 'PYKX_SKIP_UNDERQ', 'PYKX_Q_LOADED_MARKER', 'PYKX_LOADED_UNDER_Q', 'QHOME', diff --git a/src/pykx/serialize.py b/src/pykx/serialize.py index 2ffd134..183f1c5 100644 --- a/src/pykx/serialize.py +++ b/src/pykx/serialize.py @@ -22,7 +22,7 @@ def __init__(self, obj: Any, mode: int = 6, wait: int = 0): - 1: sync - 2: response\n More information about the serialization of `K` objects can be found - [here](https://code.kx.com/pykx/2.0/user-guide/advanced/serialization.html). + [here](https://code.kx.com/pykx/user-guide/advanced/serialization.html). Note: To access the memory view of the serialized object you can use the `data` property. If you need a copy of the data instead you can use the `copy` method. diff --git a/src/pykx/system.py b/src/pykx/system.py index 3c10e22..9914a21 100644 --- a/src/pykx/system.py +++ b/src/pykx/system.py @@ -5,6 +5,7 @@ from . import help, Q, wrappers as k from .exceptions import PyKXWarning, QError +from .config import suppress_warnings __all__ = ['SystemCommands'] @@ -260,10 +261,12 @@ def load(self, path): path = path[:-1] print(path) return self._q._call(f'\\l {path}', wait=True) - warn('Detected a space in supplied path\n' - f' Path: \'{path}\'\n' - 'q system loading does not support spaces, attempting load ' - 'using alternative load operation', PyKXWarning) + if not suppress_warnings: + warn('Detected a space in supplied path\n' + f' Path: \'{path}\'\n' + 'q system loading does not support spaces, attempting load ' + 'using alternative load operation.\n' + 'To turn off this warning set PYKX_SUPPRESS_WARNINGS to True.', PyKXWarning) full_path = os.path.abspath(path) load_path = Path(full_path) folder = load_path.parent.as_posix() diff --git a/src/pykx/toq.pyx b/src/pykx/toq.pyx index cb0baa8..a35b5bf 100644 --- a/src/pykx/toq.pyx +++ b/src/pykx/toq.pyx @@ -661,6 +661,8 @@ def from_int(x: Any, elif ktype is None and isinstance(x, (bool, np.bool_)): ktype = k.BooleanAtom + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if cast and type(x) is not int: x = cast_to_python_int(x) @@ -719,6 +721,8 @@ def from_float(x: Any, Returns: An instance of a `pykx.NonIntegralNumericAtom` subclass. """ + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if cast and type(x) is not float: x = cast_to_python_float(x) @@ -1357,6 +1361,8 @@ def from_numpy_ndarray(x: np.ndarray, ktype = _resolve_ndarray_k_type(x, ktype) + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if cast: try: if _dtype_to_ktype[x.dtype] != ktype: @@ -2014,6 +2020,8 @@ def from_datetime_date(x: Any, Returns: An instance of a subclass of `pykx.TemporalFixedAtom`. """ + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if (cast is None or cast) and type(x) is not datetime.date: x = cast_to_python_date(x) @@ -2085,6 +2093,8 @@ def from_datetime_datetime(x: Any, Returns: An instance of a subclass of `pykx.TemporalFixedAtom`. """ + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if (cast is None or cast) and type(x) is not datetime.datetime: x = cast_to_python_datetime(x) @@ -2153,6 +2163,8 @@ def from_datetime_timedelta(x: Any, Returns: An instance of a subclass of `pykx.TemporalSpanAtom`. """ + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if (cast is None or cast) and type(x) is not datetime.timedelta: x = cast_to_python_timedelta(x) @@ -2637,6 +2649,8 @@ def _from_iterable(x: Any, handle_nulls: bool = False, strings_as_char: bool = False, ): + if not isinstance(cast, (bool, type(None))): + raise TypeError("Cast must be of type Boolean") if type(x) is np.ndarray: return from_numpy_ndarray(x, ktype, diff --git a/src/pykx/util.py b/src/pykx/util.py index 22b3ba3..86f4147 100644 --- a/src/pykx/util.py +++ b/src/pykx/util.py @@ -508,7 +508,7 @@ def q_information(): def _run_all_cell_with_magics(lines): - if "%%python" == lines[0].strip(): + if "%%py" == lines[0].strip(): return lines[1:] elif "%%q" in lines[0].strip(): return lines @@ -534,7 +534,7 @@ def qfirst_modify(state): elif _run_all_cell_with_magics not in ipython.input_transformers_cleanup and state == "q": ipython.input_transformers_cleanup.append(_run_all_cell_with_magics) print("""PyKX now running in 'jupyter_qfirst' mode. All cells by default will be run as q code. -Include '%%python' at the beginning of each cell to run as python code. """) # noqa +Include '%%py' at the beginning of each cell to run as python code. """) # noqa else: print(f"PyKX already running in '{state}' mode") except NameError: diff --git a/src/pykx/wrappers.py b/src/pykx/wrappers.py index 50d19ce..7a4ff87 100644 --- a/src/pykx/wrappers.py +++ b/src/pykx/wrappers.py @@ -9475,6 +9475,28 @@ def year(self): """ return self.call('`year$') + @property + def day(self): + """ + Retrieve the day of the month information from a temporal column + + + Examples: + + Retrieve day of month information from a column + + ```python + >>> import pykx as kx + >>> tab = kx.Table(data={ + ... 'a': kx.random.random(100, kx.TimestampAtom.inf), + ... 'b': kx.random.random([100, 3], 10.0) + ... }) + >>> tab.exec(kx.Column('a').day) + pykx.IntVector(pykx.q('7 10 12..')) + ``` + """ + return self.call('`dd$') + @property def month(self): """ @@ -9483,7 +9505,7 @@ def month(self): Examples: - Retrieve year information from a column + Retrieve month information from a column ```python >>> import pykx as kx diff --git a/tests/qcumber_tests/extra_functions.quke b/tests/qcumber_tests/extra_functions.quke index a12de5a..3ca2b88 100644 --- a/tests/qcumber_tests/extra_functions.quke +++ b/tests/qcumber_tests/extra_functions.quke @@ -87,3 +87,26 @@ feature .pykx.print (::)~.pykx.print til 10; (::)~.pykx.print .pykx.import[`numpy;`:array][til 10] ) + +feature .pykx.loadPy + should error under various conditions + expect to error if the file is not a string + @[{.pykx.loadPy x;0b};`file;{x like"Parameter 'file'*"}] + expect to error if the file does not have the correct extension + @[{.pykx.loadPy x;0b};"file.t";{x like"File extension must be .py/.p"}] + expect to error if loading .p file directly without loadPy + @[{system"l ",x;0b};"tests/test_files/func1.p";{x like"SyntaxError*"}] + + should allow loading of various files + expect loading of .py files to work using .pykx.loadPy + .pykx.loadPy"tests/test_files/func0.py"; + all( + 99h~type .pykx.get[`dict0]`; + 11~.pykx.get[`func0;<][10] + ) + expect loading of .p files to work using .pykx.loadPy + .pykx.loadPy"tests/test_files/func1.p"; + all( + 99h~type .pykx.get[`dict1]`; + 11~.pykx.get[`func1;<][10] + ) diff --git a/tests/test_compress_encrypt.py b/tests/test_compress_encrypt.py index d557e54..ddec740 100644 --- a/tests/test_compress_encrypt.py +++ b/tests/test_compress_encrypt.py @@ -39,7 +39,6 @@ def test_compress_encrypt_errors(kx): @pytest.mark.isolate def test_compression(): - os.environ['PYKX_BETA_FEATURES'] = 'True' import pykx as kx compress = kx.Compress(kx.CompressionAlgorithm.ipc) compress.global_init() @@ -57,7 +56,6 @@ def test_compression(): @pytest.mark.isolate def test_compression_4_1(): os.environ['PYKX_4_1_ENABLED'] = 'True' - os.environ['PYKX_BETA_FEATURES'] = 'True' import pykx as kx compress = kx.Compress(kx.CompressionAlgorithm.zstd, level=0) compress.global_init() @@ -66,7 +64,6 @@ def test_compression_4_1(): @pytest.mark.isolate def test_encrypt(): - os.environ['PYKX_BETA_FEATURES'] = 'True' import pykx as kx encrypt = kx.Encrypt('tests/test_files/testkek.key', 'password') encrypt.load_key() @@ -77,7 +74,6 @@ def test_encrypt(): @pytest.mark.isolate def test_encrypt_path(): - os.environ['PYKX_BETA_FEATURES'] = 'True' import pykx as kx encrypt = kx.Encrypt(Path('tests/test_files/testkek.key'), 'password') encrypt.load_key() diff --git a/tests/test_config.py b/tests/test_config.py index 74d30ec..53808ed 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -28,6 +28,22 @@ def test_missing_profile(capsys): assert "Unable to locate specified 'PYKX_PROFILE': 'default' in file" in out +@pytest.mark.isolate +def test_boolean_config(): + config = ''' + [default] + PYKX_SUPPRESS_WARNINGS = true + PYKX_QDEBUG = 'true' + ''' + with TemporaryDirectory() as tmp_dir: + os.chdir(tmp_dir) + with open('.pykx-config', 'w+') as f: + f.writelines(config) + import pykx as kx + assert kx.config.suppress_warnings + assert kx.config.pykx_qdebug + + @pytest.mark.isolate def test_qargs_single(): os.environ['QARGS'] = '-p 5050' diff --git a/tests/test_db.py b/tests/test_db.py index 1717b31..33e7712 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -305,7 +305,6 @@ def test_subview(kx): @pytest.mark.isolate def test_q_lo_move_dir(): os.environ['PYKX_4_1_ENABLED'] = 'True' - os.environ['PYKX_BETA_FEATURES'] = 'True' curr_dir = os.getcwd() import pykx as kx db = kx.DB(path='db') # noqa: F841 @@ -317,7 +316,6 @@ def test_q_lo_move_dir(): @pytest.mark.isolate def test_q_lo_keep_dir(): os.environ['PYKX_4_1_ENABLED'] = 'True' - os.environ['PYKX_BETA_FEATURES'] = 'True' curr_dir = os.getcwd() import pykx as kx db = kx.DB(path='db', change_dir=False) # noqa: F841 diff --git a/tests/test_files/func0.py b/tests/test_files/func0.py new file mode 100644 index 0000000..f44918a --- /dev/null +++ b/tests/test_files/func0.py @@ -0,0 +1,8 @@ +def func0(x): + return x+1 + + +dict0 = { + 'x': 1, + 'y': [1, 2, 3] +} diff --git a/tests/test_files/func1.p b/tests/test_files/func1.p new file mode 100644 index 0000000..ed23126 --- /dev/null +++ b/tests/test_files/func1.p @@ -0,0 +1,7 @@ +def func1(x): + return x+1 + +dict1 = { + 'x': 1, + 'y': [1, 2, 3] +} diff --git a/tests/test_license.py b/tests/test_license.py index fef7472..1e9d22b 100644 --- a/tests/test_license.py +++ b/tests/test_license.py @@ -221,6 +221,9 @@ def test_check_license_success_file(kx): def test_check_license_success_b64(kx): with open(os.environ['QLIC'] + '/kc.lic', 'rb') as f: license = base64.encodebytes(f.read()) + license = license.decode() + license = license.replace('\n', '') + license = bytes(license, 'utf-8') assert kx.license.check(license, format='STRING') @@ -252,3 +255,16 @@ def test_install_license_invalid_file(kx): kx.license.install('/test/location.lic', force=True) except Exception as e: assert pattern.match(str(e)) + + +def test_string_conversions(kx): + licFile = kx.qlic/kx.config.lic_type + with open(licFile, 'rb') as f: + lic_contents = base64.encodebytes(f.read()).decode('utf-8') + lic_contents = lic_contents.replace('\n', '') + + assert kx.license.check(kx.qlic/kx.config.lic_type) + assert kx.license.check(lic_contents, format='string') + with pytest.raises(Exception) as err: + kx.license.check(lic_contents, format='string', license_type='blah.lic') + assert "License type" in str(err) diff --git a/tests/test_pandas_api.py b/tests/test_pandas_api.py index 28ad96a..95da630 100644 --- a/tests/test_pandas_api.py +++ b/tests/test_pandas_api.py @@ -30,9 +30,7 @@ def test_df_columns(q): def test_df_dtypes(q): df = q('([] til 10; 10?0Ng; 10?1f;0f,til 9;10?("abc";"def");10?1e)') - with pytest.warns(DeprecationWarning, - match=r"dtypes column 'type' is deprecated, please use 'datatypes'"): - assert all(df.dtypes.columns == ['columns', 'datatypes', 'type']) + assert all(df.dtypes.columns == ['columns', 'datatypes']) assert q('{x~y}', q('("kx.LongAtom";"kx.GUIDAtom";"kx.FloatAtom";"kx.List";"kx.CharVector";"kx.RealAtom")'), # noqa: E501 df.dtypes['datatypes']) diff --git a/tests/test_system.py b/tests/test_system.py index 4fc3c8b..d971da2 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -1,5 +1,8 @@ import os +from pathlib import Path from platform import system +import shutil +import warnings # Do not import pykx here - use the `kx` fixture instead! import pytest @@ -521,3 +524,39 @@ def test_system_display_size_ipc(q_port): ' ba..\n4 | fo..\n..') with pytest.raises(kx.QError): q.system.display_size = [10, 10] + + +@pytest.mark.isolate +def test_suppress_warning_false(): + os.environ['PYKX_SUPPRESS_WARNINGS'] = 'False' + import pykx as kx + path = Path('Test Folder') + os.makedirs(path, exist_ok=True) + with open(path/'test.q', 'w') as f: + f.write('j:"junk"') + with warnings.catch_warnings(record=True) as w: + kx.q.system.load('Test Folder/test.q') + msg_string = "" + for i in w: + message = str(i.message) + msg_string = msg_string + message + assert "space" in msg_string + shutil.rmtree('Test Folder') + + +@pytest.mark.isolate +def test_suppress_warning_true(): + os.environ['PYKX_SUPPRESS_WARNINGS'] = 'True' + import pykx as kx + path = Path('Test Folder') + os.makedirs(path, exist_ok=True) + with open(path/'test.q', 'w') as f: + f.write('j:"junk"') + with warnings.catch_warnings(record=True) as w: + kx.q.system.load('Test Folder/test.q') + msg_string = "" + for i in w: + message = str(i.message) + msg_string = msg_string + message + assert "space" not in msg_string + shutil.rmtree('Test Folder') diff --git a/tests/test_toq.py b/tests/test_toq.py index 8527667..61c8213 100644 --- a/tests/test_toq.py +++ b/tests/test_toq.py @@ -1701,3 +1701,44 @@ def test_pandas_timedelta(kx): assert kx.toq(kx.q('16:36:29.214').pd()) == kx.q('0D16:36:29.214000000') assert kx.toq(kx.q('16:36:29.214344').pd()) == kx.q('0D16:36:29.214344000') assert kx.toq(kx.q('16:36:29.214344678').pd()) == kx.q('0D16:36:29.214344678') + + +def test_cast_setting(kx): + with pytest.raises(TypeError) as err: + kx.IntAtom(9, cast="Test") + assert "must be of type" in str(err) + + testVal = "9" + assert kx.IntAtom(9) == kx.IntAtom(testVal, cast=True) + with pytest.raises(TypeError): + kx.IntAtom(testVal, cast=False) + + testVal = "9.3" + assert kx.FloatAtom(9.3) == kx.FloatAtom(testVal, cast=True) + with pytest.raises(TypeError): + kx.FloatAtom(testVal, cast=False) + + assert kx.DateAtom(np.datetime64('2024-11-06T14:38:03')) == date(2024, 11, 6) + with pytest.raises(TypeError): + kx.DateAtom(np.datetime64('2024-11-06T14:38:03'), cast=False) + + assert kx.TimespanAtom(np.timedelta64(1, 'D'), cast=True) == timedelta(days=1) + with pytest.raises(TypeError): + kx.DateAtom(np.timedelta64(1, 'D'), cast=False) + + assert kx.TimeAtom(np.timedelta64(1, 'h'), cast=True) == time(1, 0, 0) + with pytest.raises(AttributeError): + kx.TimeAtom(np.timedelta64(1, 'h'), cast=False) + + assert kx.TimestampAtom(np.datetime64('2024-11-06T16:43:50'), cast=True) == datetime(2024, 11, 6, 16, 43, 50) # noqa: E501 + with pytest.raises(Exception) as e: + kx.TimestampAtom(np.datetime64('2024-11-06T16:43:50'), cast=False) + assert "numpy" in str(e) + + testVal = [b'a', b'ab', 1.3, [1.3, 1.2], 'x'] + assert (kx.K(np.array(testVal, dtype=object), cast=False) == [b'a', b'ab', 1.3, [1.3, 1.2], 'x']).all() # noqa: E501 + with pytest.raises(KeyError): + kx.K(np.array(testVal, dtype=object), cast=True) + + nparray = np.array([1, 2, 3]) + assert (kx.toq(nparray, kx.FloatVector, cast=True) == kx.FloatVector(kx.q('1 2 3f'))).all() diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index e7122b6..01b617d 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -5383,6 +5383,13 @@ def test_all_timetypes(kx, q_port): assert all(td['a'] == td_a_roundtrip) +def test_column_day(kx): + tab = kx.Table(data={ + 'a': kx.random.random(100, kx.TimestampAtom.inf), + 'b': kx.random.random([100, 3], 10.0)}) + assert isinstance(tab.exec(kx.Column('a').day), kx.IntVector) + + @pytest.mark.unlicensed def test_datetime64(kx): df = pd.DataFrame(data={'a': np.array([9999, 1577899899], dtype='datetime64[s]')})