Week Number in Q/KDB - kdb

I'm looking to map a date/week to the Week number of the year.
I've thought about subtracting the start of the year, and dividing by 7 - however it might not line up correctly.
e.g.
2020.01.02 -> Week 1
2020.01.06 -> Week 2

I would suggest to use following function:
weekOfYear: {1+floor (x-`week$"d"$12 xbar"m"$x)%7}
This function
Finds the first Monday before or on 1st Jan. E.g. {(`week$"d"$12 xbar"m"$x)}2020.01.01 returns 2019.12.30
Then finds difference in days between x and the first Monday
Divides difference by 7 and adds 1, which returns result you are looking for
For example
weekOfYear 2019.12.31 2020.01.01 2020.01.02 2020.01.05 2020.01.06 2020.01.07
returns
53 1 1 1 2 2

Just to build on Antons great answer, you could also use the div function instead of flooring it, which would look something like
{1 + (x - `week $ `date $ 12 xbar `month $ x) div 7}

Related

Make a list with the quarter and year based on a date range of quarters KDB+/Q

I have a list of date ranges for the past 8 quarters given by the below function
q) findLastYQuarters:{reverse("d"$(-3*til y)+m),'-1+"d"$(-3*-1+til y)+m:3 bar"m"$x}[currentDate;8]
q) findLastYQuarters
2020.01.01 2020.03.31
2020.04.01 2020.06.30
2020.07.01 2020.09.30
2020.10.01 2020.12.31
2021.01.01 2021.03.31
2021.04.01 2021.06.30
2021.07.01 2021.09.30
2021.10.01 2021.12.31
I need to produce a separate list that labels each item in this list by a specific format; the second list would need to be
1Q20,2Q20,3Q20,4Q20,1Q21,2Q21,3Q21,4Q21
This code needs to be able to run on it's own, so how can I take the first list as an input and produce the second list? I thought about casting the latter date in the range as a month and dividing it by 3 to get the quarter and extracting the year, but I couldn't figure out how to actually implement that. Any advice would be much appreciated!
I'm sure there are many ways to solve this, a function like f defined below would do the trick:
q)f:{`$string[1+mod[`month$d;12]%3],'"Q",/:string[`year$d:x[;0]][;2 3]}
q)lyq
2020.01.01 2020.03.31
2020.04.01 2020.06.30
2020.07.01 2020.09.30
2020.10.01 2020.12.31
2021.01.01 2021.03.31
2021.04.01 2021.06.30
2021.07.01 2021.09.30
2021.10.01 2021.12.31
q)f lyq
`1Q20`2Q20`3Q20`4Q20`1Q21`2Q21`3Q21`4Q21
Figured it out.
crop:findLastYQuarters;
crop[0]:crop[0][1];
crop[1]:crop[1][1];
crop[2]:crop[2][1];
crop[3]:crop[3][1];
crop[4]:crop[4][1];
crop[5]:crop[5][1];
crop[6]:crop[6][1];
crop[7]:crop[7][1];
labels:()
labelingFunc:{[r] temp:("." vs string["m"$r]); labels,((string(("J"$temp[1])%3)),"Q",(temp[0][2,3])};
leblingFunc each crop;
labels

Rounding timestamp to the nearest 30 seconds

My table is as follows:
t: ([]dt: 2021.10.25T09:30:28 2021.10.25T09:30:32;price:9.99 10.00)
I wish to round the timestamp to the nearest 30sec mark.
I tried using xbar like so:
update roundedDt: 30 xbar dt.second from t
However it seems to have floored the results.
The desired result should be 09:30:30 for both rows.
How can one round to the nearest 30 second mark?
Jonathon's answer is the most flexible for modifying the rounding for not just seconds specifically but an alternative simple solution for just seconds would be to offset by 15:
q)update roundedDt:30 xbar 15+dt.second from t
dt price roundedDt
---------------------------------------
2021.10.25T09:30:28.000 9.99 09:30:30
2021.10.25T09:30:32.000 10 09:30:30
Edit: If you want the full dateTime rounded, I would convert it to timestamp as easy to work with and adjust my offset/xbar to match.
q)update roundedDt:30000000000 xbar 15000000000 + `timestamp$dt from t
dt price roundedDt
-----------------------------------------------------------
2021.10.25T09:30:28.000 9.99 2021.10.25D09:30:30.000000000
2021.10.25T09:30:32.000 10 2021.10.25D09:30:30.000000000
2020.10.25T23:59:59.000 9.99 2020.10.26D00:00:00.000000000
2020.10.26T00:00:01.000 10 2020.10.26D00:00:00.000000000
You can try something like this:
update roundedDt:?[(`ss$dt)within(0;14);`time$(`int$`time$dt)-1000*`ss$dt;
?[(`ss$dt)within(15;44);`time$30000+(`int$`time$dt)-1000*`ss$dt;`time$60000+(`int$`time$dt)-1000*`ss$dt]] from t
You could use a modified version of xbar that rounds to nearest int instead of flooring:
q)xbar2:{type[y]$x*"j"$y%x:$[16h=abs type x;"j"$x;x]}
q)update roundedDt:xbar2[30;dt.second] from t
dt price roundedDt
---------------------------------------
2021.10.25T09:30:28.000 9.99 09:30:30
2021.10.25T09:30:32.000 10 09:30:30
Note that because this function is defined in root namespace you must use bracket notation (xbar2[30;dt.second]). If you wish to use infix notation (30 xbar2 dt.second), you'll need to define the function in .q namespace i.e. .q.xbar2:{type[y]$x*"j"$y%x:$[16h=abs type x;"j"$x;x]}.
xbar2 is based on the original xbar, but where xbar uses div which has the effect of flooring the result, here % is used which will produce a float output and this is then cast to a long int which will round to the nearest integer.
What about this solutions:
/ x is your timestamp
/ y is the timebucket (in seconds)
.time.round:{
:"z"$+[`date$x;`time$1e3*y*`int$%[`time$x;y*1e3]];
};
As example, if you want to round at the nearest 30 seconds, you need to use this as follows:
ts1:2020.10.30T10:32:35
.time.round[ts1;30]
In your case, simply type:
t[`round_time]:{.time.round[x;30]} each t[`dt]
As a side note, some of the proposed solutions would round timestamps like 2020.10.25T23:59:59 and 2020.10.26T00:00:01 to 24:00:00 and 00:00:00 respectively, which is not what we would like I suppose.

KDB - Automatic function argument behavior with Iterators

I'm struggling to understand the behavior of the arguments in the below scan function. I understand the EWMA calc and have made an Excel worksheet to match in an attempt to try to understand but the kdb syntax is throwing me off in terms of what (and when) is x,y and z. I've referenced Q for Mortals, books and https://code.kx.com/q/ref/over/ and I do understand whats going on in the simpler examples provided.
I understand the EWMA formula based on the Excel calc but how is that translated into the function below?
x = constant, y= passed in values (but also appears to be prior result?) and z= (prev period?)
ewma: {{(y*1-x)+(z*x)} [x]\[y]};
ewma [.25; 15 20 25 30 35f]
15 16.25 18.4375 21.32813 24.74609
Rearranging terms makes it easier to read but if I were write this in Excel, I would incorrectly reference the y value column in the addition operator instead of correctly referencing the prev EWMA value.
ewma: {{y+x*z-y} [x]\[y]};
ewma [.25; 15 20 25 30 35f]
15 16.25 18.4375 21.32813 24.74609
EWMA in Excel formula for auditing
0N! is useful in these cases for determining variables passed. Simply add to start of function to display variable in console. EG. to show what z is being passed in as each run:
q)ewma: {{0N!z;(y*1-x)+(z*x)} [x]\[y]};
q)ewma [.25; 15 20 25 30 35f]
15f
16.25
18.4375
21.32812
//Or multiple at once
q)ewma: {{0N!(x;y;z);(y*1-x)+(z*x)} [x]\[y]};
q)
q)ewma [.25; 15 20 25 30 35f]
0.25 15 20
0.25 16.25 25
0.25 18.4375 30
0.25 21.32812 35
Edit:
To think about why z is holding 'y' values it is best to think about below simplified example using just x/y.
//two parameters specified in beginning.
//x initialised as 1 then takes the function result for next run
//y takes value of next value in list
q){0N!(x;y);x+y}\[1;2 3 4]
1 2
3 3
6 4
3 6 10
//in this example only one parameter is passed
//but q takes first value in list as x in this special case
q){0N!(x;y);x+y}\[1 2 3 4]
1 2
3 3
6 4
1 3 6 10
A similar occurrence is happening in your example. x is not being passed to the the iterator and therefore will assume the same value in each run.
The inner function y value will be initilised taking the first value of the outer y variable (15f in this case) like above simplified example. Then the z takes the 2nd value of the list for it's initial run. y then takes the result of previous function run and z takes the next value in the list until how list has bee passed to function.

Clingo: logic OR in integrity constraint

For a lecture exercise, I have to represent in Answer Set Programming (we use Clingo as interpreter) a the following integrity constraint:
"You have to plan the calendar of a Masterclass. Normally, the lectures are on Fridays (8 hours) and Saturday(4 or 5 hours). And the 7th and 16th week are full, which means the lectures goes from Monday to Friday, with 8 hours per day, and on Saturday, with 4 or 5 hours of lecture."
The basic settings for the problem are the following:
#const n_weeks = 2. % for now we limit the problem size to 2 weeks
#const n_days = 6. % days in a fullweek
week(1..n_weeks).
day(1..n_days).
hour(1..8). % from the 1st to the 8th hour of the day
% the second week is a fullweek (lectures from 1st to 8th hour from Monday to Friday)
fullweek(2).
% We number all the weekdays (mon-fri) (we need it for the saturday)
fullday(1..5).
% some professors just for test
prof("prof1").
prof("prof2").
prof("prof3").
prof("prof4").
% subj, total hours, prof
subject("subj1", 8, "prof1").
subject("subj2", 14, "prof2").
subject("subj3", 24, "prof3").
subject("subj4", 11, "prof1").
% The main predicate, to print out at the end.
0 {calendar(W, D, H, W*100+D*10+H, lecture(S, P))} 1 :- week(W), day(D), hour(H), subject(S, _, P).
Now, as mentioned above (the final line in bold), we have some problems with the following constraint:
"In this masterclass the hours of a lecture on Saturday can be 4 or 5."
For now, me and my colleagues represented this constraint like this:
% The Saturday has 4 or 5 hours of lecture
:- #count{I : calendar(W, D, _, I, lecture(_, _))} > 5, week(W), day(D), not fullday(D).
:- #count{I : calendar(W, D, _, I, lecture(_, _))} < 4, week(W), day(D), not fullday(D).
Is it the right way to represent constraint like this? There is a better approach?
I do not believe there is the "right way" to represent the constraint as long as it is technically correct. I suggest to consider the following points:
The way on how to express Saturday is complicated, i.e. you can replace the variable D by 6 and eliminate the predicates day and fullday.
I do not understand why you use "lecture(_, _)" instead of the underscore.
I am not sure why you use the variable I for counting and think you like to count the hours instead.
Maybe it make sense to use disjunction explicitly, i.e. use a predicate like "hours_on_sunday(H)" and write a rule that H must be 4 or 5.

Error handling table generated by os.time() Lua

table.concat(os.date("*t"), ":",4,6)
any idea why ^this^ or ˇthisˇ
test = os.date("*t")
table.concat(test, ":" , 4 , 6 )
does not work?
input:3: invalid value (nil) at index 4 in table for 'concat'
table.concat works on numerically indexed table. Whereas the output of os.date '*t' would be a table like:
hour 18
min 20
wday 1
day 2
month 3
year 2014
sec 49
yday 61
isdst false
Although not the answer to your direct question, I suspect you are trying to do is retrieve the time separated by colons.
The best way to do that is os.date"%H:%M:%S"
The formatting options are very flexible and use the C strftime format.