(Series) A side-by-side comparison of q/kdb+ and python for qbies (q newbies).
This cheat sheet is a list of code snippets that do the same thing in
q and python -- something I wished existed when I first came to q/KDB+ from python.
Note that in both languages, the code shown is sometimes not the most elegant or
succinct; that's on purpose because I wanted to show the same constructs in both languages.
For a comprehensive intro to q, check out Q for Mortals.
Note q code snippets are edited to have line breaks for readability (my
opinion; ymmv). Multiline statements do not work at the prompt, but they do work in scripts. See docs.
Versions: q (3.6), python (3.7)
Basics
q)d:(`a`b`c)!1 2 3
q)d:(
((enlist `a)!(enlist 1)),
((enlist `b)!(enlist 2)),
((enlist `c)!(enlist 3))
)
q)d
a| 1
b| 2
c| 3
q)d`a
1
q)d`x
0N
|
>>> d = dict(a=1, b=2, c=3)
>>> d = {
... 'a': 1,
... 'b': 2,
... 'c': 3,
... }
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['a']
1
>>> d['x']
KeyError: 'd'
|
Note the last d`x does not return an exception; 0N is a null long.
Get / Look Up
q)d`a
q)d `a
q)d[`a]
q)d@`a
1
q)d`a`b
q)d `a`b
q)d[`a`b]
q)d@`a`b
1 2
|
>>> d['a']
1
>>> [d[k] for k in [1, 2]]
[1, 2]
|
Find / Reverse Look Up
q)e:(`j`k`l`m)!(1 2 2 3)
q)e
j| 1
k| 2
l| 2
m| 3
q)e?2 // returns first match
`k
q)e?3 2
`m`k
|
>>> e = dict(zip(list('jklm'), [1, 2, 2, 3]))
>>> e
{'j': 1, 'k': 2, 'l': 2, 'm': 3}
>>> rev_e = {v: k for k, v in list(e.items())}
>>> rev_e[2]
'l'
>>> [rev_e[k] for k in [3, 2]]
['m', 'l']
|
On python side, do reversed(list(e.items())) to achieve same result as q.
Subset
Get a smaller dictionary.
q)`j`m#e
j| 1
m| 3
q)(enlist `k)#e
k| 2
|
>>> {k: e[k] for k in 'jm'}
{'j': 1, 'm': 3}
>>> {k: e[k] for k in 'k'}
{'k': 2}
|
Set
q)e[`x]:42
q)e
l| 2
m| 3
x| 42
|
>>> e['x'] = 42
>>> e
{'l': 2, 'm': 3, 'x': 42}
|
Delete
q)e:`j`k`l`m!(1 2 2 3)
q)e:`j`k _ e
q)e
l| 2
m| 3
q)e:`l _ e
q)e
m| 3
|
>>> e = dict(zip(list('jklm'), [1, 2, 2, 3]))
>>> [e.pop(item) for item in 'jk']
>>> e
{'l': 2, 'm': 3}
>>> del e['j']
>>> e
{'l': 2, 'm': 3}
|
If your dictionary is keyed by numbers, you're out of luck.
q)e:(0 1)!`j`k
q)e
0| j
1| k
q)e:1 _ e // deletes one item
q)e
1| k
|
>>> e = {0: 'j', 1: 'k'}
>>> e
{0: 'j', 1: 'k'}
>>> del e[1] # deletes the key 1
>>> e
{0: 'j'}
|
Join Dictionaries
Since python 3.9, d1 | d2 also works.
q)d1:(enlist `x)!(enlist 1)
q)d2:(enlist `y)!(enlist 2)
q)d1,d2
x| 1
y| 2
q)d3:(enlist `x)!(enlist 1)
q)d4:(enlist `x)!(enlist 2)
q)d3,d4
x| 2
|
>>> d1 = dict(x=1)
>>> d2 = dict(y=2)
>>> {**d1, **d2}
{'x': 1, 'y': 2}
>>> d3 = dict(x=1)
>>> d4 = dict(x=2)
>>> {**d1, **d2}
{'x': 2}
|
Dictionary from table
q)t:([]k:`x`y; v:1 2)
q)exec k!v from t
x| 1
y| 2
// Note duplicated key
q)t:([]k:`x`y`y; v:1 2 3)
q)exec k!v from t
x| 1
y| 2
y| 3
|
>>> df = pd.DataFrame(dict(k=list('xy'), v=[1, 2]))
>>> dict(zip(df.k, df.v))
{'x':1, 'y':2}
# Last key wins
>>> df = pd.DataFrame(dict(k=list('xyy'), v=[1, 2, 3]))
>>> dict(zip(df.k, df.v))
{'x':1, 'y':3}
|
Gotchas
Duplicated keys
q)d:(`j`j)!1 2
q)d
j| 1
j| 2
q)d`j // only the first
1
|
>>> {'j': 1, 'j': 2} # last one wins
{'j': 2}
>>> dict([('j', 1), ('j', 2)]) # last one wins
{'j': 2}
>>> dict(j=1, j=2)
SyntaxError: keyword argument repeated
|
q dictionaries have types
Except when they don't.
q)longDict:(`j`k`l)!1 2 3
q)longDict[`m]:4.2 / not allowed
'type
q)mixedDict:(`j`k`l)!(1; 0b; `s)
q)mixedDict[`m]:4.2 / allowed
q)mixedDict
j| 1
k| 0b
l| `s
m| 4.2
|
>>> d = dict(j=1, k=2, l=3)
>>> d['m'] = 4.2
>>> d
{'j': 1, 'k': 2, 'l': 3, 'm': 4.2}
|
Missing keys
The last example (boolean) is arguably the most dangerous, because there are no
nulls for the boolean type.
q)d:(`j`k`l)!1 2 3
q)d`notHere
0N
q)d:(`j`k`l)!`a`b`c
q)d`notHere
`
q)d:(`j`k`l)!101b
q)d`notHere
0b
|
>>> d['not_here']
KeyError
|