I created my own function of round off:
.q.rnd:{$[x < 0; -1; 1] * floor abs[x] + 0.5}
I have a table Test with a string column of COL
select "F"$(COL) from Test
24549.18741328
48939.50717263
-274853.33568872
-24549.18741328
298753.62574861
84822.70074144
-7468840.64371524
117944.21228603
-117944.21228603
7468840.64371524
-7468840.64371524
I want to derive a table that would round-off the records in Test
One would think that the statement below would work. But it does not.
select .q.rnd "F"$(COL) from Test
I get the error "type". So how do I round off the records?
The result if the if-else conditional must be an atomic boolean. When you run .q.rnd on a column, you are operating on a list and x<0 is going to return a list of booleans, not an atom. The vector conditional is ?
Nonetheless, it looks like you want a resulting integer/long anyway, so just use parse here
q)t:([]string (10?-1 1)*10?10000f)
q)select "F"$x from t
x
-------------------
4123.1701336801052
-9877.8444156050682
-3867.3530425876379
7267.8099689073861
4046.5459413826466
-8355.0649625249207
6427.3701561614871
-5830.2619284950197
1424.9352994374931
-9149.8820902779698
q)select "j"$"F"$x from t
x
-----
4123
-9878
-3867
7268
4047
-8355
6427
-5830
1425
-9150
To add to what Sean's said, if you wanted to use your function as well you could use each which will apply .q.rnd to each item in the list.
q)select .q.rnd each "F"$x from t
x
-----
-3928
5171
5160
-4067
-1781
3018
-7850
5347
-7112
-4116
but using select "F"$x from t is better as it is vectorised.
q)\t:1000 select "j"$"F"$x from t
22
q)\t:1000 select .q.rnd each "F"$x from t
33
Also it should be noted that the .q namespace isn't necessary and is "reserved for kx use". A lot of the default q functions are in the .q namespace and there's always a chance future kdb updates could add a .q.rnd that has different behaviour and will break any code where you have used your function in.
Related
I have the following where clauses :
q)).tst.wc
(max$["b"];((/:;like);`Col1;(enlist;"0009D000";"00080000")))
(like;`Col2;,"B0000000999")
I want to crate the following query:
,(|;(max$["b"];((/:;like);`Col1;(enlist;"0009D000";"00080000")));(like;`Col2;,"B0000000999"))
I tried
(or;.tst.wc) //doesn't work
(or; first each .tst.wc) // doesn't work
(or;.tst.wc 0;.tst.wc 1) // works
however I cannot use the last one because I don't know how many where clauses will be there. Any suggestions?
You can just append them to or as follows:
q)(or),.tst.wc
|
($["b"];((/:;like);`Col1;(enlist;"0009D000";"00080000")))
(like;`Col2;,"B0000000999")
q)(or;.tst.wc 0;.tst.wc 1)~(or),.tst.wc
1b
EDIT: if you have an unknown number of where clauses and they all need an "or" between them all then you can use:
(or;;)/[.tst.wc]
however this where clause will fast become inefficient as nested "or"s are not optimal query filtering - each clause needs to be applied to the entire length of the table
so the best I could find so far was :
{[x;y]$[x~();(or;y);x,enlist y]}/[();.tst.wc]
... ready to accept and upvote a better answer ...
What about using any? If I understand you correctly you are creating any number of or conditions.
t:([]a:til 10;b:`a`b`c`d`e`f`g`h`i`j;c:10*til 10);
.tst.wc:((=;`a;0);(=;`b;enlist `d);(=;`c;90));
?[t;enlist ((any),enlist (enlist),.tst.wc);0b;()!()]
a b c
------
0 a 0
3 d 30
9 j 90
// also works with one
.tst.wc2:enlist (=;`a;0);
?[t;enlist ((any),enlist (enlist),.tst.wc2);0b;()!()]
a b c
-----
0 a 0
I want to be able to construct (+; (+; `a; `b); `c) given a list of `a`b`c
Similarly if I have a list of `a`b`c`d, I want to be able to construct another nest and so on and so fourth.
I've been trying to use scan but I cant get it right
q)fsum:(+;;)/
enlist[+;;]/
q)fsum `a`b`c`d
+
(+;(+;`a;`b);`c)
`d
If you only want the raw parse tree output, one way is to form the equivalent string and use parse. This isn't recommended for more complex examples, but in this case it is clear.
{parse "+" sv string x}[`a`b`c`d]
+
`d
(+;`c;(+;`b;`a))
If you are looking to use this in a functional select, we can use +/ instead of adding each column individually, like how you specified in your example
q)parse"+/[(a;b;c;d)]"
(/;+)
(enlist;`a;`b;`c;`d)
q)f:{[t;c] ?[t;();0b;enlist[`res]!enlist (+/;(enlist,c))]};
q)t:([]a:1 2 3;b:4 5 6;c:7 8 9;d:10 11 12)
q)f[t;`a`b`c]
res
---
12
15
18
q)f[t;`a`b]
res
---
5
7
9
q)f[t;`a`b`c]~?[t;();0b;enlist[`res]!enlist (+;(+;`a;`b);`c)]
1b
You can also get the sum by indexing directly to return a list of each column values and sum over these. We use (), to turn any input into a list, otherwise it will sum the values in that single column and return only a single value
q)f:{[t;c] sum t (),c}
q)f[t;`a`b`c]
12 15 18
Write a Q-SQL query to multiply the price of BA.N by 2, GS.N by 3 and MSFT.O by 4 and call the column newPrice using vector conditional statement
tab2:`syms`prices!(`MSFT.O`GS.N`BA.N;45.15 191.10 178.50)
flip tab2
select syms,prices,newPrice:(prices*(4,3,2)) from flip tab2
I'm not sure using a vector conditional would be the easiest way to go about this. For example, you could use a simple dictionary to achieve a similar effect. First define a dictionary mapping your syms to their multipliers then use that dictionary in your select statement:
tab2: flip `syms`prices!(`MSFT.O`GS.N`BA.N;45.15 191.10 178.50)
d: `MSFT.O`GS.N`BA.N!4 3 2;
select syms, prices, newPrice: prices*d[syms] from tab2
syms prices newPrice
----------------------
MSFT.O 45.15 180.6
GS.N 191.1 573.3
BA.N 178.5 357
Vector conditionals can only return one of two results, depending on if the condition is true or false. To extend that limitation to what you want you could nest the conditionals inside each other. So like:
select syms, prices, newPrice: ?[syms=`MSFT.O; prices*4; ?[syms=`GS.N; prices*3; ?[syms=`BA.N;prices*2;prices]]] from tab2
But this quickly becomes unwieldy and doesn't scale well. If you added more syms, it would be easy to update the dictionary, but annoying to update the conditional.
You should create multipliers map
(`MSFT.O`GS.N`BA.N!2 3 4)
and multiply each price on value from the map based on row syms:
update newPrice: prices*(`MSFT.O`GS.N`BA.N!2 3 4)syms from flip tab2
Consider the following procedure f:{[x] ..} with starting value a:0:
Do something with x and a. The output is saved as the new version of a, and the output is returned by the function
For the next input x, redo the procedure but now with the new a.
For a single value x, this procedure is easily constructed. For example:
a:0;
f:{[x] a::a+x; :a} / A simple example (actual function more complicated)
However, how do I make such a function such that it also works when applied on a table column?
I am clueless how to incorporate this step for 'intermediate saving of a variable' in a function that can be applied on a column at once. Is there a special technique for this? E.g. when I use a table column in the example above, it will simply calculate a+x with a:0 for all rows, opposed to also updating a at each iteration.
No need to use global vars for this - can use scan instead - see here.
Example --
Generate a table -
q)t:0N!([] time:5?.z.p; sym:5?`3; price:5?100f; size:5?10000)
time sym price size
-----------------------------------------------
2002.04.04D18:06:07.889113280 cmj 29.07093 3994
2007.05.21D04:26:13.021438816 llm 7.347808 496
2010.10.30D10:15:14.157553088 obp 31.59526 1728
2005.11.01D21:15:54.022395584 dhc 34.10485 5486
2005.03.06D21:05:07.403334368 mho 86.17972 2318
Example with a simple accumilator - note, the function has access to the other args if needed (see next example):
q)update someCol:{[a;x;y;z] (a+1)}\[0;time;price;size] from t
time sym price size someCol
-------------------------------------------------------
2002.04.04D18:06:07.889113280 cmj 29.07093 3994 1
2007.05.21D04:26:13.021438816 llm 7.347808 496 2
2010.10.30D10:15:14.157553088 obp 31.59526 1728 3
2005.11.01D21:15:54.022395584 dhc 34.10485 5486 4
2005.03.06D21:05:07.403334368 mho 86.17972 2318 5
Say you wanted to get cumilative size:
q)update cuSize:{[a;x;y;z] (a+z)}\[0;time;price;size] from t
time sym price size cuSize
------------------------------------------------------
2002.04.04D18:06:07.889113280 cmj 29.07093 3994 3994
2007.05.21D04:26:13.021438816 llm 7.347808 496 4490
2010.10.30D10:15:14.157553088 obp 31.59526 1728 6218
2005.11.01D21:15:54.022395584 dhc 34.10485 5486 11704
2005.03.06D21:05:07.403334368 mho 86.17972 2318 14022
If you wanted more than one var passed through the scan, can pack more values into the first var, by giving it a more complex structure:
q)update cuPriceAndSize:{[a;x;y;z] (a[0]+y;a[1]+z)}\[0 0;time;price;size] from t
time sym price size cuPriceAndSize
--------------------------------------------------------------
2002.04.04D18:06:07.889113280 cmj 29.07093 3994 29.07093 3994
2007.05.21D04:26:13.021438816 llm 7.347808 496 36.41874 4490
2010.10.30D10:15:14.157553088 obp 31.59526 1728 68.014 6218
2005.11.01D21:15:54.022395584 dhc 34.10485 5486 102.1188 11704
2005.03.06D21:05:07.403334368 mho 86.17972 2318 188.2986 14022
#MdSalih solution is correct, I am just explaining here what could be the possible reason with global variable in your case and solution for that.
q) t:([]id: 1 2)
q)a:1
I think you might have been using it like this:
q) select k:{x:x+a;a::a+1;:x} id from t
output:
k
--
1
2
And a value is 2 which means function executed only once. Reason is we passed full id column list to function and (+) is atomic which means it operates on full list at once. In following ex. 2 will get added to all items in list.
q) 2 + (1;3;5)
Correct way to use it is 'each':
q)select k:{x:x+a;a::a+1;:x} each id from t
output:
k
--
2
3
Ok so i have a DECIMAL field called "Score". (e.g 10.00)
Now, in my SP, i want to increment/decrement the value of this field in update transactions.
So i might want to do this:
SET #NewScore = #CurrentScore + #Points
Where #Points is the value im going to increment/decrement.
Now lets say #Points = 10.00.
In a certain scenario, i want 10.00 to become -10.00
So the statement would be translated to:
SET #NewScore = #CurrentScore + -10.00
How can i do that?
I know its a strange question, but basically i want that statement to be dynamic, in that i dont want to have a different statement for incrementing/decrementing the value.
I just want something like this:
SET #Points = 10.00
IF #ActivityBeingPerformedIsFoo
BEGIN
-- SET #Points to be equivalent negative value, (e.g -10.00)
END
SET #NewScore = #CurrentScore + #Points
Can't you just multiply it by -1?
I always do 0 - #Points. It was this way in some code I inherited. "A foolish consistency..."
Multiply #Points by -1 in that certain scenario.
I thought of subtracting it with a multiple of 2, i.e. x - 2x