Entries in key (4)

What does it mean for a table to be a flipped dictionary?

Short answer:

A table is a reference to a (column) dictionary.


The internal representation of a table is nearly identical to that of a dictionary. We can use the flip command to create a table from a dictionary:

q)t: ([] a: 1 2 3; b: 4 5 6; c: 7 8 9)
q)d: `a`b`c ! (1 2 3; 4 5 6; 7 8 9)
q)t ~ flip d
1b
q)

In fact, when a dictionary is flip'ed, the underlying core data structure remains untouched. The table itself is a simple, small object that refers to the original dictionary. Using .Q.w, we can measure how much more memory a table takes than the corresponding dictionary:

q).Q.w[]`used         // memory usage baseline
112992j
q)x:`a`b`c!3 3#til 9  // create a small dictionary
q).Q.w[]`used
113424j
q)x:flip x
q).Q.w[]`used         // memory usage delta is
113456j               // just 32 more bytes
q)

No matter how large the underlying dictionary is, creating a table is fast and still takes only 32 bytes:

q)x:`a`b`c!3 100000#til 10
q).Q.w[]`used
1686192j
q)x:flip x
q).Q.w[]`used
1686224j          // 32 = 1686224 - 1686192
q)


Now, Let's examine how a keyed table is related to a dictionary. We start by creating a simple keyed table:

q)t: ([] a: 1 2 3; b: 4 5 6; c: 7 8 9)
q)keyedTable: `a`b xkey t
q)keyedTable
a b| c
---| -
1 4| 7
2 5| 8
3 6| 9
q)keys keyedTable
`a`b
q)

Since keyedTable is a table, one might expect it to have the same type as t but instead, q presents the following surprise:

q)type t              
98h                  // type table as expected
q)type keyedTable
99h                  // *NOT* 98h
q)

Type 99h is the type number for dictionaries. If keyedTable really is a dictionary, we should be able to extract its key and value:

q)key keyedTable
a b
---
1 4
2 5
3 6
q)value keyedTable
c
-
7
8
9
q)

Indeed, keyedTable is a dictionary - one that holds unkeyed tables for both its key and its value:

q)type key keyedTable
98h
q)type value keyedTable
98h
q)

This suggests that we can create a keyed table by using the ! (dict) operator with two unkeyed tables:

q)(key keyedTable)!(value keyedTable)
a b| c
---| -
1 4| 7
2 5| 8
3 6| 9
q)

Lastly, joining the two flipped tables brings us back to the original dict.

q)(flip key keyedTable),(flip value keyedTable)
a| 1 2 3
b| 4 5 6
c| 7 8 9
q)

For more information, see Creating dictionaries and tables from C.

How can I find the data type of a given table column?

Short answer: meta tablename

For example -

q)\l sp.q        / found in $QHOME/sp.q
q)s
s | name  status city
--| -------------------
s1| smith 20     london
s2| jones 10     paris
s3| blake 30     paris
s4| clark 20     london
s5| adams 30     athens
q)meta s
c     | t f a
------| -----
s     | s
name  | s
status| i
city  | s
q)

The output of meta is a kdb table, where columns c and t indicate the column name and data type, respectively.

The key the provides an alternate method:

q)key exec city from s
`symbol
q)

If you need the type code, use the type function:

q)type exec city from s
11h
q)

See also: Type character definitions, Meta output faq.

How can I test for the existence of a file or directory in q?

Short answer: key file_handle or key dir_handle

File handle example:

q)\touch /tmp/test.txt
q)key hsym ` $ "/tmp/test.txt"
`:/tmp/test.txt
q)

The function key returns an empty list when the file does not exist:

q)hdel `:/tmp/test.txt
`:/tmp/test.txt
q)() ~ key `:/tmp/test.txt
1b
q)

key is one of the most heavily overloaded functions in q. It accepts an argument of type dictionary, keyed table, table column, list, and integer. It's easy to confuse it with keys, which turns out to be an entirely different function.

See also: ~ (match), \ (system), hdel, hsym

How can I slice a vector into a series of vectors of length n?

By passing the # (take) operator a pair as its first (i.e., left) argument:

q)2 5 # til 10
0 1 2 3 4
5 6 7 8 9

When # is used in this way, it is referred to as "reshape." We like to call this, "taking a box." Just like when you apply # with an integer left argument, taking a box stops when you have taken as many items as requested, leaving the rest behind:

q)3 3 # til 10
0 1 2
3 4 5
6 7 8

Similarly, when you over-take a box, you start over from the beginning of the source:

q)4 3 # til 10
0 1 2
3 4 5
6 7 8
9 0 1

If you want to take all the items from the source, pass 0N as the first element of the box:

q)0N 2 # til 10
0 1
2 3
4 5
6 7
8 9

If you take a whole box, but the elements from the source don't fit, you end up with a short list in the last row:

q)0N 3 # til 10
0 1 2
3 4 5
6 7 8
,9
q)

See also: cut and faq on _ (drop)