KxSystems / pykx

PyKX is a Python first interface to the worlds fastest time-series database kdb+ and it's underlying vector programming language q.
https://code.kx.com/pykx
Other
49 stars 12 forks source link

Is there a way to suppress empty output (::) when using the magic %%q in Jupyter notebook? #7

Closed flcong closed 11 months ago

flcong commented 11 months ago

Is your feature request related to a problem? Please describe. When using the %%q magic with in jupyter notebook, it seems that the output from each line of code is printed, including ::. See, e.g., the example on the documentation. Is there a way to suppress empty output (::)?

1697212278056

Describe the solution you'd like I expect either of the following (or add an option in the %%q magic):

Describe alternatives you've considered I tried to add ; to each line of code, but still a :: is printed for each line of code ended with ;.

cmccarthy1 commented 11 months ago

Hi @flcong ,

Thanks for submitting this, the team and I will take a look at this and aim to introduce a fix for this in a later release. I believe the correct solution for this will be to output only the last line of code but will survey other users as to desired behaviour/providing an opt-in solution to provide both while removing the returned ::

flcong commented 11 months ago

Hi @flcong ,

Thanks for submitting this, the team and I will take a look at this and aim to introduce a fix for this in a later release. I believe the correct solution for this will be to output only the last line of code but will survey other users as to desired behaviour/providing an opt-in solution to provide both while removing the returned ::

I agree. Thanks a lot!

flcong commented 11 months ago

It seems to be an easy fix, by modifying the following line:

https://github.com/KxSystems/pykx/blob/1cddd767d5a324b34020facb9be3569c6101e949/src/pykx/nbextension.py#L106-L107

Probably for completeness, another option to the q magic can be added, e.g. --verbatim, which triggers printing output of each line.

Happy to create a PR.

flcong commented 11 months ago

By the way, not sure why, but pykx.q('1') == pykx.q('::') and pykx.q('1') != pykx.q('::') both return pykx.BooleanAtom(pykx.q('0b')). Yet, pykx.q('::') == pykx.q('::') returns pykx.BooleanAtom(pykx.q('1b')) and pykx.q('::') != pykx.q('::') returns pykx.BooleanAtom(pykx.q('0b'))

flcong commented 11 months ago

As an interim solution, I mimics https://github.com/KxSystems/pykx/blob/main/src/pykx/nbextension.py) to define a new magic qq. In my usecase, I don't mind adding the definition at the top of each notebook, so I just add the following code to define a new magic command %%qq to do the same stuff. I also don't use any options of the PyKX q magic command, so I remove those code.

import pykx as kx
from IPython.core.magic import register_cell_magic

@register_cell_magic
def qq(instructions, code):
    ld = kx.SymbolAtom('.Q.pykxld')
    _q = kx.q
    code = [kx.CharVector(x) for x in code.split('\n')][:-1]
    ret = _q(
        "{[ld;code;file] value (@';\"q\";enlist[file],/:value(ld;code))}",
        ld,
        code,
        b'jupyter_cell.q'
    )
    if not ret[-1] == kx.q('::'):
        print(_q('{x y}', ret, kx.q.count(ret)-1)
rianoc-kx commented 11 months ago

https://code.kx.com/pykx/2.1/release-notes/changelog.html#fixes-and-improvements_1

>>> import pykx
>>> pykx.q('1') != pykx.q('::')
pykx.BooleanAtom(pykx.q('1b'))
>>> pykx.q('1') == pykx.q('::')
pykx.BooleanAtom(pykx.q('0b'))
>>> pykx.__version__
'2.1.1'
rianoc-kx commented 11 months ago

If you want cells with multiple outputs you can use:

import pykx as kx
from IPython.core.magic import register_cell_magic

@register_cell_magic
def qq(instructions, code):
    ld = kx.SymbolAtom('.Q.pykxld')
    _q = kx.q
    code = [kx.CharVector(x) for x in code.split('\n')][:-1]
    ret = _q(
        "{[ld;code;file] {x where not (::)~/:x}value (@';\"q\";enlist[file],/:value(ld;code))}",
        ld,
        code,
        b'jupyter_cell.q'
    )
    if not kx.licensed:
        ret = ret.py()
    for i in range(len(ret)):
        print(_q('{x y}', ret, i))     

.i.e For the cell

%%qq
a:1+2
a
b:3+3
b
c:2+2

You will see

3
6

Your example code would print nothing as the last item returns (::).

Our planned change will suppress (::) prints but will allow multiple lines to display. This will be done to match how q would behave if you loaded the same code from the cell from a .q file with \l

This change and some other improvements to Jupyter magic %%q will be included in the next release of PyKX - we will notify you when it is available.

flcong commented 11 months ago

If you want cells with multiple outputs you can use:

import pykx as kx
from IPython.core.magic import register_cell_magic

@register_cell_magic
def qq(instructions, code):
    ld = kx.SymbolAtom('.Q.pykxld')
    _q = kx.q
    code = [kx.CharVector(x) for x in code.split('\n')][:-1]
    ret = _q(
        "{[ld;code;file] {x where not (::)~/:x}value (@';\"q\";enlist[file],/:value(ld;code))}",
        ld,
        code,
        b'jupyter_cell.q'
    )
    if not kx.licensed:
        ret = ret.py()
    for i in range(len(ret)):
        print(_q('{x y}', ret, i))     

.i.e For the cell

%%qq
a:1+2
a
b:3+3
b
c:2+2

You will see

3
6

Your example code would print nothing as the last item returns (::).

Our planned change will suppress (::) prints but will allow multiple lines to display. This will be done to match how q would behave if you loaded the same code from the cell from a .q file with \l

This change and some other improvements to Jupyter magic %%q will be included in the next release of PyKX - we will notify you when it is available.

Thanks! That makes sense!

cmccarthy1 commented 11 months ago

Hi @flcong,

The fix for this has been merged into the main branch and released on PyPI in version 2.2 of the library. Please reopen the issue if you still see any problems. Thanks again for raising this it's a really nice improvement to the UX for the q development side of the library.

Conor