3

How to efficiently and concisely switch formulas dependent on the value of other vector?

According to kx documentation, it is possible to

have more than just a true/false selection, e.g. match1/match2/match3/others mapping to result1/result2/result3/default

Data:

q)t:([]a:til 5;b:10+til 5;c:100+til 5;d:1000+til 5;g:`I`B`I`U`B) 
a b  c   d    g
---------------
0 10 100 1000 I
1 11 101 1001 B
2 12 102 1002 I
3 13 103 1003 U
4 14 104 1004 B

I've done it like this:

q)update r:(flip (a+b;c+d;a-d))@'`I`B`U?g from t
a b  c   d    g r    
---------------------
0 10 100 1000 I 10   
1 11 101 1001 B 1102 
2 12 102 1002 I 14   
3 13 103 1003 U -1000
4 14 104 1004 B 1108 

Question - is there more efficient way (time,space,lines of code)?

Thomas Smyth - Treliant
  • 4,993
  • 6
  • 25
  • 36
Daniel Krizian
  • 4,586
  • 4
  • 38
  • 75

3 Answers3

2

You can use a vector conditional: http://code.kx.com/q/ref/lists/#vector-conditional

    q)update r:?[`I=g;a+b;c+d] from t
     a b  c   d    g r
     --------------------
     0 10 100 1000 I 10
     1 11 101 1001 B 1102
     2 12 102 1002 I 14
     3 13 103 1003 I 16
     4 14 104 1004 B 1108

Edit: If g has more values than two you can extend further with nested conditionals:

   q)\t res2:delete idx from`idx xasc raze{[t;idx;it;k] @[d;`idx`r;:;](idxs;
   (+).(d:t idxs:idx[it])k)}[t;group t`g;]./:flip(`I`C`B;(`a`b;`b`c;`c`d))
   122
   q)
   q)\t res3:update r:?[`I=g;a+b;?[`B=g;c+d;a-d]] from t
   59
   q)res3~res2
   1b
jomahony
  • 1,662
  • 6
  • 9
2

This is similar to your solution, but seems about 30% faster, possibly because there's no flip

q)update r: ((a+b;c+d;a-d)@(`I`B`U?g))@'i from t
a b  c   d    g r
---------------------
0 10 100 1000 I 10
1 11 101 1001 B 1102
2 12 102 1002 I 14
3 13 103 1003 U -1000
4 14 104 1004 B 1108

q)t:1000000?t
q)\t update r: ((a+b;c+d;a-d)@(`I`B`U?g))@'i from t
166
q)\t update r:(flip (a+b;c+d;a-d))@'`I`B`U?g from t
248

Although the nested conditional still looks faster:

q)\t update r:?[`I=g;a+b;?[`B=g;c+d;a-d]] from t
46
James Little
  • 1,964
  • 13
  • 12
1

You can use the trusty dictionary. Have your possible values as keys and the functions as values. You could even generate this function dynamically based on the actual population of key values you have in your table. I'm sure this isn't the best in terms of performance.

    q)fd:`I`B`C!({(+). x@`a`b};{(+). x@`b`d};{(+). x@`c`d})
    q)update r:{(fd x[`g])x}each t from t
    a b  c   d    g r
    --------------------
    0 10 100 1000 I 10
    1 11 101 1001 B 1012
    2 12 102 1002 I 14
    3 13 103 1003 C 1106
    4 14 104 1004 B 1018
    q)

*edit Faster approach and comparison...

    q)t:1000000?t
    q)fd:`I`B`C!({(+). x@`a`b};{(+). x@`c`d};{(+). x@`b`c})
    q)\t res1:update r:{(fd x[`g])x}each t from t
    635
    q)\t res2:delete idx from`idx xasc raze{[t;idx;it;k] @[d;`idx`r;:;](idxs;(+).(d:t idxs:idx[it])k)}[t;group t`g;]./:flip(`I`C`B;(`a`b;`b`c;`c`d))
    79
    q)res1~res2
    1b
    q)
Chromozorz
  • 451
  • 2
  • 6