How to efficiently get the counts of values (from a list) less than some index number? - kdb

It is hard for me to describe in words what this function does, but I have some working code.
f:{[n;k] sum flip k </: til n}
i:i: 3 4 6 7 13;
f[30;i]
0 0 0 0 1 2 2 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5i
In am concerned that the flip operation may be expensive for large input values. Is there a way to do this without the flip that is more efficient?
Being just as concise would be a nice-to-have.

For your inputs you can achieve the same result with
{[n;k] k binr til n}
This should work so long as k remains in ascending order.
Docs for binr are here: https://code.kx.com/q/ref/bin/

Seans answer is probably more efficient (test it), but why not change your each-right to each-left to avoid the flip?
q)g:{[n;k] sum k<\:til n}
q)f[30;i]~g[30;i]
1b

Related

Merge 2 lists according to values in a boolean list

I have a method of achieving this which also explains my question.
a:1 2 3 4;
b:5 6 7;
cond:1101001b;
comb:(count cond) # 0N;
comb[where cond]:a;
comb[where not cond]:b
But q has so many utilities for manipulating lists, I am wondering if there is a more direct way of doing this.
rank is what you need.
q)comb
1 2 5 3 6 7 4
q)(b,a)rank cond
1 2 5 3 6 7 4
You could write the expression in a single line
comb:#[;where not cond;:;b] #[;where cond;:;a] (count cond)#0N
Alternatively, assuming the 1s and 0s of cond matches the lengths of a and b:
(a,b) iasc where[cond],where not cond

How do I evaluate a function into a matrix in KDB?

Let's say I've got a function that defines a matrix in terms of it's i and j coordinates:
f: {y+2*x}
I'm trying to create a square matrix that evaluates this function at all locations.
I know it needs to be something like f ' (til 5) /:\: til 5, but I'm struggling with rest.
Rephrasing your question a bit, you want to create a matrix A = [aij] where aij = f(i, j), i, j = 0..N-1.
In other words you want to evaluate f for all possible combinations of i and j. So:
q)N:5;
q)i:til[N] cross til N; / all combinations of i and j
q)a:f .' i; / evaluate f for all pairs (i;j)
q)A:(N;N)#a; / create a matrix using #: https://code.kx.com/q/ref/take/
0 1 2 3 4
2 3 4 5 6
4 5 6 7 8
6 7 8 9 10
8 9 10 11 12
P.S. No, (til 5) /:\: til 5 is not exactly what you'd need but close. You are generating a list of all pairs i.e. you are pairing or joining the first element of til 5 with every element of (another) til 5 one by one, then the second , etc. So you need the join operator (https://code.kx.com/q/ref/join/):
(til 5),/:\: til 5
You were close. But there is no need to generate all the coordinate pairs and then iterate over them. Each Right Each Left /:\: manages all that for you and returns the matrix you want.
q)(til 5)f/:\:til 5
0 1 2 3 4
2 3 4 5 6
4 5 6 7 8
6 7 8 9 10
8 9 10 11 12

KDB/Q how do we calculate the moving median

There are already moving average in kdb/q.
https://code.kx.com/q/ref/avg/#mavg
But how do I compute moving median?
Here is a naive approach. It starts with an empty list and null median and iterates over the list feeding in a new value each time.
Sublist is used fix the window, and this window is passed along with the median as the state of into the next iteration.
At the end scan \ will output the state at every iteration from which we take the median (first element) from each one
mmed:{{(med l;l:neg[x] sublist last[y],z)}[x]\[(0n;());y][;0]}
q)mmed[5;til 10]
0 0.5 1 1.5 2 3 4 5 6 7
q)i:4 9 2 7 0 1 9 2 1 8
q)mmed[3;i]
4 6.5 4 7 2 1 1 2 2 2
There's also a generic "sliding window" function here which you can pass your desired aggregator into: https://code.kx.com/q/kb/programming-idioms/#how-do-i-apply-a-function-to-a-sequence-sliding-window
q)swin:{[f;w;s] f each { 1_x,y }\[w#0;s]}
q)swin[avg; 3; til 10]
0 0.33333333 1 2 3 4 5 6 7 8
q)update newcol:swin[med;10;mycol] from tab

kdb q - create 2d buckets for positive integers

I am trying create 2d buckets in q
Given a 2d grid
5 o---o---o
| | |
3 o---o---o
| | |
0 o---o---o
0 3 5
each node on the grid defines the boundary of 2d buckets for positive integers. For example the center node would contain tuples (x;y) where 3<=x<5 and 3<=y<5. The nine buckets are indexed from 0,...,8.
The way I tried to implemented this in q is
bucketidx:{((0 3 5i) cross (0 3 5i)) bin "i"$(first x;last x)}
To traverse through the buckets:
bucketidx each ((0 3 5i) cross (0 3 5i))
/0j, 1j, 2j, 3j, 4j, 5j, 6j, 7j, 8j
However I get a strange behavior on bucketidx 6 0. I expect this to be in the upper left node
(5<=y) and (x=0)
but it returns index 8 which would be the upper right node. I hope it is clear what I am trying to do.
Thanks for the help
Thats because of the bin behavior.
Binary search returns the index of the last item in x which is <=y
https://code.kx.com/q/ref/search/#bin-binr
Your list is :
q) a:(0 3 5i) cross (0 3 5i)
q) a / (0 0; 0 3;0 5;3 0; 3 3; 3 5;5 0;5 3; 5 5)
You are searching (6 0) in this list using bin function and last item in that list which is <=(6;0) is (5;5) and index of that item is 8.
q) a bin 6 0 / 8
thats the reason you are getting 8.
I think 'tuple with bin' approach is not the right way to go for this problem.
You could use something similar to below idea. First argument to function is X coordinate and second is Y coordinate.
q) node:{b:0 3 5;(b bin x)+3*b bin y}
q) node[0;6] / 6
Another approach is to use a dictionary with sorted attribute, which makes it a step function.
q)d:`s#0 3 5!0 1 2
q)3 sv' d#(0 3 5i) cross (0 3 5i)
0 1 2 3 4 5 6 7 8
q)3 sv' d#enlist 6 0
,6

Get first two maximum values in matrix

I've got a matrix (n x m). And I'd like to know, for each row, the indexes of the coloums that contain the first two maximum values:
2 3 4 2
2 4 7 1
1 1 2 4
5 5 9 6
1 4 2 1
9 8 1 2
The answer should be:
2 3
2 3
3 4
3 4
2 3
1 2
How can I obtain it with matlab commands? I'd like not to use for loops. I tried with:
[x,y]=max(matrix')
y=y';
y gives me the colum indexes for the maximum elements. Now I'd set to zero these elements and repeat the instructions but I have no idea how to do. I treid:
matrix(:,y)=0;
but it doesn't work.
if A is your matrix, then sort and pick the top two indices,
[a ix]=sort(A,2)
ans= ix(:,end-1:end)