kdb conditional update and select in one query - kdb

I can do this:
x:([]v: 4 2; w: 10 100)
x: update z:`test from x where v = 4
x
But i'd really like to be able to do the conditional update and select all in one hit. something like
select v, w, (select `test from x where v = v) from z
Is this possible in kdb?

You could try
update z:?[v=4;`test;`] from x

Is the vector conditional if what you're looking for?
q)select v,w,z:?[v=4;`test;`] from x
v w z
----------
4 10 test
2 100
http://code.kx.com/q/ref/lists/#vector-conditional

Related

KDB+ Merging multiple update statements

q)d:([] f1:`a`b` ;f2:```c; m1:`x``z;m2:``y`z)
f1 f2 m1 m2
-----------
a x
b y
c z z
I want to update the f1 & m1 columns to f2 & m2 respectively if f1 & m1 have nulls; actually I want to merge these 2 queries to one update statement :
update f1:f2 from d where null f1
update m1:m2 from d where null m1`
An alternative you might like to consider is fill, ^ which allows you to fill nulls in one list with items from another list (in this case, the lists are columns in the table) e.g.
q)d:([] f1:`a`b` ;f2:```c; m1:`x``z;m2:``y`z)
q)update f2^f1,m2^m1 from d
f1 f2 m1 m2
-----------
a x
b y y
c c z z
You can use Triadic vector conditional evaluation ?
?[vb;exprtrue;exprfalse]
The new query would be :
q)update f1:?[null f1;f2;f1] , m1:?[null m1;m2;m1] from d
f1 f2 m1 m2
-----------
a x
b y y
c c z z
Fill can be used to update nulls:
If you want to update table d in place, then you can use:
update f2^f1,m2^m1 from`d
or
![`d;();0b;`f1`m1!((^;`f2;`f1);(^;`m2;`m1))]
If you want to display the output of update without updating original table, then:
update f2^f1,m2^m1 from d or
![d;();0b;`f1`m1!((^;`f2;`f1);(^;`m2;`m1))]

T-SQL - Padding data points

I have a series of data points in a table in an Azure SQL database. When plotted, the points have the following distribution:
What I am trying to do is add padding so that they have the appearance of being in a continuous line - or at lease more continuous than it is now.
I tried adding points between each point, but the problem with that is that it's all relative. If you look closely, you can see some of the points are blue, and some are dark red. The blue points are the ones I added, but the line looks the same.
I'm looking for advice on the logic I should use to solve this issue. I want to add x number of points between each data point based on the distance between the nearest points... if that makes sense.
I think this works
declare #t table (x smallmoney primary key, y smallmoney);
declare #inc smallmoney = 1;
insert into #t(x, y) values
(1, 1)
, (5, 3)
, (8, 4)
, (10, 5)
, (11, 6);
with cte as
( select x, x as x0, y, y as y0, cnt = cast(1 as smallmoney)
, lead(x) over (order by x) as nextX
, lead(y) over (order by x) as nextY
from #t t
union all
select x + #inc, x0, y + #inc/(nextX-x0)*(nextY-y0), y0, cnt+1, nextX, nextY
from cte t
where x + #inc < nextX
)
select *
from cte t
order by t.x;
I'm not confident that this is the best solution, but I think you could build off of it. Here's an sqlfiddle
SELECT x + COALESCE(((nextx-x)/10)*inc, 0) as x, y + COALESCE(((nexty-y)/10)*inc, 0) as y
FROM
(SELECT x, y, nextx, nexty, inc.n + 0.0 as inc FROM
(SELECT x, y, lead(x) over (order by x) as nextx, lead(y) over (order by x) as nexty
FROM points) p inner join (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) inc(n)
ON nextx is not null or inc.n = 0
) a ORDER BY x
This will add 9 points between each point (10 points total, including the "real" one).
The basic idea is that I'm using lead for each row to get the next x and next y, then I join that to a hardcoded list of values 0 to 9. Then for each value, I increment x by 1/10 of the difference between nextx and x, and increment y by 1/10 of the difference between nexty and y.
The join condition nextx is not null or inc.n = 0 is so that I only join inc(0) to the last x value (rather than joining 10 times).
You could change my hardcoded list of values and the hardcoded 10s to increment differently. Similarly, you'd probably need some changes if you only want integers, but the principle will be the same.

How to elegantly sum into repeated indices of a vector in matlab

I am trying to figure out a way to do the following without a loop:
Lets say I have a vector x where I sum some elements e into. I also have a vector of indices ids which say to which elements of x to send the values to. i.e.
x = zeros(1,4);
e = [ 1 10 100 1e3 1e4];
ids = [1 1 2 4 3];
I would like to do something like
x(ids) = x(ids) + e
That will return
x =
11 100 10000 1000
because we refer to x(1) twice, while instead it returns
x =
10 100 10000 1000
accumaray is a really useful function for doing such tricks. In your case:
accumarray(ids',e)
will do the job.

Function returns only one output at time

I have a problem with this function (I have very little experience with Matlab). The function is supposed to return 3 different arguments but always returns only one. I mean, to get Y out I have to remove Tot and Z from the output arguments! As it is now only Tot is returned.
function [Tot, Z, Y] = something( A )
%A = [1 2 3; 4 5 6; 7 8 9; 10 11 12];
Z=cumsum(A); %cumulative sum of columns
Y=cumsum(A,2); %cumulative sum of rows
Tot = sum(Z(:))+sum(Y(:)); %Total sum of Z+Y
end
Why is doing that ? What am I doing wrong, I need all 3 arguments out.
The proper call to receive all three arguments would be: [A, B, C] = something (X), where Tot would be placed into A, Z into B, and Y into C. If you just want Y, you can use the "tilde" operator to ignore other outputs: [~,~,C] = something(X).

Generate random number between 1 and 10 with the exception of a single number in matlab

I would like to generate a random number between 1 and 10 using for example randi([1,10]) but I would like to exclude a single number, say 7 - this number would always change and be specified in a variable called b.
Is that possible to do somehow?
Use randsample. For instance, to generate a number between 1 and 10, excluding 7, do the following:
b = 7;
x = randsample(setdiff(1:10, b), 1);
Here setdiff is used to exclude the value of b from the vector 1:10.
If you don't have the Statistics Toolbox installed, you won't be able to use randsample, so use rand:
v = setdiff(1:10, b);
x = v(ceil(numel(v) * rand));
For those without the statistics toolbox:
b = 7;
pop = 1:10;
pop(b) = [];
then
pop(randperm(9,1))
or for n random integers from the population:
pop(randi(numel(pop), 1, n))
As #EitanT mentioned, you can use randsample to do so, but I think that doing so in a simpler manner should do for you:
>> b = 7;
>> randsample([1:b-1,b+1:10],1)
This simply samples a random value from the array [1:b-1,b+1:10] which would here be
1 2 3 4 5 6 8 9 10
Or similarly, if the `randsample' function is unavailable as #EitanT had mentioned,
v = [1:b-1,b+1:10];
x = v(ceil(numel(v) * rand));