1

I have a q table in which no. of non keyed columns is variable. Also, these column names contain an integer in their names. I want to perform some function on these columns without actually using their actual names

How can I achieve this ?

For Example:

 table:   

 a |  col10  col20 col30 

 1 |    2      3     4
 2 |    5      7     8

 // Assume that I have numbers 10, 20 ,30 obtained from column names

    I want something like **update NewCol:10*col10+20*col20+30*col30 from table**

     except that no.of columns is not fixed so are their inlcluded numbers
chrisaycock
  • 36,470
  • 14
  • 88
  • 125
pikachuchameleon
  • 665
  • 3
  • 10
  • 27

4 Answers4

7

We want to use a functional update (simple example shown here: http://www.timestored.com/kdb-guides/functional-queries-dynamic-sql#functional-update)

For this particular query we want to generate the computation tree of the select clause, i.e. the last part of the functional update statement. The easiest way to do that is to parse a similar statement then recreate that format:

q)/ create our table
q)t:([] c10:1 2 3; c20:10 20 30; c30:7 8 9; c40:0.1*4 5 6)
q)t
c10 c20 c30 c40
---------------
1   10  7   0.4
2   20  8   0.5
3   30  9   0.6

q)parse "update r:(10*c10)+(20*col20)+(30*col30) from t"
!
`t
()
0b
(,`r)!,(+;(*;10;`c10);(+;(*;20;`col20);(*;30;`col30)))
q)/ notice the last value, the parse tree
q)/ we want to recreate that using code
q){(*;x;`$"c",string x)} 10
*
10
`c10
q){(+;x;y)} over {(*;x;`$"c",string x)} each 10 20
+
(*;10;`c10)
(*;20;`c20)
q)makeTree:{{(+;x;y)} over {(*;x;`$"c",string x)} each x}

/ now write as functional update
q)![t;();0b; enlist[`res]!enlist makeTree 10 20 30]
c10 c20 c30 c40 res
-------------------
1   10  7   0.4 420
2   20  8   0.5 660
3   30  9   0.6 900

q)update r:(10*c10)+(20*c20)+(30*c30) from t
c10 c20 c30 c40 r
-------------------
1   10  7   0.4 420
2   20  8   0.5 660
3   30  9   0.6 900
Ryan Hamilton
  • 2,601
  • 16
  • 17
  • Hi, I've tried using the same trick on this table. But it still shows error. t:([] a1:1 3;a2:2 4); col:`a1`a2;const:1 2; ![t;();0b;enlist['New]! (enlist +,{(*;x;y)}'[const;col])]. Can you help me with this ? – pikachuchameleon Jun 18 '14 at 15:49
2

I think functional select (as suggested by @Ryan) is the way to go if the table is quite generic, i.e. column names might varies and number of columns is unknown.

Yet I prefer the way @JPC uses vector to solve the multiplication and summation problem, i.e. update res:sum 10 20 30*(col10;col20;col30) from table

Let combine both approach together with some extreme cases:

q)show t:1!flip(`a,`$((10?2 3 4)?\:.Q.a),'string 10?10)!enlist[til 100],0N 100#1000?10
a | vltg4 pnwz8 mifz5 pesq7 fkcx4 bnkh7 qvdl5 tl5 lr2 lrtd8
--| -------------------------------------------------------
0 | 3     3     0     7     9     5     4     0   0   0
1 | 8     4     0     4     1     6     0     6   1   7
2 | 4     7     3     0     1     0     3     3   6   4
3 | 2     4     2     3     8     2     7     3   1   7
4 | 3     9     1     8     2     1     0     2   0   2
5 | 6     1     4     5     3     0     2     6   4   2
..
q)show n:"I"$string[cols get t]inter\:.Q.n
4 8 5 7 4 7 5 5 2 8i
q)show c:cols get t
`vltg4`pnwz8`mifz5`pesq7`fkcx4`bnkh7`qvdl5`tl5`lr2`lrtd8
q)![t;();0b;enlist[`res]!enlist({sum x*y};n;enlist,c)]
a | vltg4 pnwz8 mifz5 pesq7 fkcx4 bnkh7 qvdl5 tl5 lr2 lrtd8 res
--| -----------------------------------------------------------
0 | 3     3     0     7     9     5     4     0   0   0     176
1 | 8     4     0     4     1     6     0     6   1   7     226
2 | 4     7     3     0     1     0     3     3   6   4     165
3 | 2     4     2     3     8     2     7     3   1   7     225
4 | 3     9     1     8     2     1     0     2   0   2     186
5 | 6     1     4     5     3     0     2     6   4   2     163
..
WooiKent Lee
  • 1,301
  • 6
  • 4
1

You can create a functional form query as @Ryan Hamilton indicated, and overall that will be the best approach since it is very flexible. But if you're just looking to add these up, multiplied by some weight, I'm a fan of going through other avenues.

EDIT: missed that you said the number in the columns name could vary, in which case you can easily adjust this. If the column names are all prefaced by the same number of letters, just drop those and then parse the remaining into int or what have you. Otherwise if the numbers are embedded within text, check out this other question

//Create our table with a random number of columns (up to 9 value columns) and 1 key column
q)show t:1!flip (`$"c",/:string til n)!flip -1_(n:2+first 1?10) cut neg[100]?100
c0| c1 c2 c3 c4 c5 c6 c7 c8 c9
--| --------------------------
28| 3  18 66 31 25 76 9  44 97
60| 35 63 17 15 26 22 73 7  50
74| 64 51 62 54 1  11 69 32 61
8 | 49 75 68 83 40 80 81 89 67
5 | 4  92 45 39 57 87 16 85 56
48| 88 34 55 21 12 37 53 2  41
86| 52 91 79 33 42 10 98 20 82
30| 71 59 43 58 84 14 27 90 19
72| 0  99 47 38 65 96 29 78 13

q)update res:sum (1+til -1+count cols t)*flip value t from t
c0| c1 c2 c3 c4 c5 c6 c7 c8 c9 res
--| -------------------------------
28| 3  18 66 31 25 76 9  44 97 2230
60| 35 63 17 15 26 22 73 7  50 1551
74| 64 51 62 54 1  11 69 32 61 1927
8 | 49 75 68 83 40 80 81 89 67 3297
5 | 4  92 45 39 57 87 16 85 56 2582
48| 88 34 55 21 12 37 53 2  41 1443
86| 52 91 79 33 42 10 98 20 82 2457
30| 71 59 43 58 84 14 27 90 19 2134
72| 0  99 47 38 65 96 29 78 13 2336

q)![t;();0b; enlist[`res]!enlist makeTree 1+til -1+count cols t] ~ update res:sum (1+til -1+count cols t)*flip value t from t
1b

q)\ts do[`int$1e4;![t;();0b; enlist[`res]!enlist makeTree 1+til 9]]
232 3216j
q)\ts do[`int$1e4;update nc:sum (1+til -1+count cols t)*flip value t from t]
69 2832j

I haven't tested this on a large table, so caveat emptor

Community
  • 1
  • 1
JPC
  • 1,891
  • 13
  • 29
1

Here is another solution which is also faster.

t,'([]res:(+/)("I"$(string tcols) inter\: .Q.n) *' (value t) tcols:(cols t) except  keys t)

By spending some time, we can decrease the word count as well. Logic goes like this:

a:"I"$(string tcols) inter\: .Q.n

Here I am first extracting out the integers from column names and storing them in a vector. Variable 'tcols' is declared at the end of query which is nothing but columns of table except key columns.

b:(value t) tcols:(cols t) except keys t

Here I am extracting out each column vector.

c:(+/) a *' b

Multiplying each column vector(var b) by its integer(var a) and adding corresponding values from each resulting list.

t,'([]res:c)

Finally storing result in a temp table and joining it to t.

Thomas Smyth - Treliant
  • 4,993
  • 6
  • 25
  • 36
Rahul
  • 3,914
  • 1
  • 14
  • 25
  • interesting, I actually see this solution as slower on my machine. I see some marginal slowdown by creating and appending col-wise the temptable, rather than directly updating as a new column. Some quick commments: `*` is already going to be atomic,so no need for the `'`. `(+/)` is effectively `sum`, both seem to perform ~ on my machine. I see `value flip value keyed_table` as faster than `(value keyed_table) cols[keyed_table] except keys keyed_table`, is that different on your machine? – JPC Jun 19 '14 at 14:03