Converting Date/Month into written form kdb - kdb

Looking to convert the weekend column below from the date into the written form in KDB/Q
t:flip (`contra`weekend`PnL)!(4#`abc;("2020.01.10";"2020.02.17";"2020.03.24";"2020.03.31");-222j, 844j, 1897j, 947j)
Result should update to
2020.01.10 - Jan-10
2020.02.17 - Feb-17
2020.03.24 - Mar 24
Thanks in advance for your help

How about
q)show m:("Jan";"Feb";"Mar")
"Jan"
"Feb"
"Mar"
q)exec {" - "sv/:flip(x;"-"sv'flip(m mod["m"$"D"$x;12];x[;8 9]))}weekend from t
"2020.01.10 - Jan-10"
"2020.02.17 - Feb-17"
"2020.03.24 - Mar-24"
"2020.03.31 - Mar-31"
or if the column needs to remain in the table
q)update {" - "sv/:flip(x;"-"sv'flip(m mod["m"$"D"$x;12];x[;8 9]))}weekend from t
contra weekend PnL
---------------------------------
abc "2020.01.10 - Jan-10" -222
abc "2020.02.17 - Feb-17" 844
abc "2020.03.24 - Mar-24" 1897
abc "2020.03.31 - Mar-31" 947
When it comes to string manipulation in KDB, vs (vector from scalar) and its inverse sv (scalar from vector) are usually very useful
In the above, first create a list of possible months m (I've done 3 to start with)
Next, inside a lambda for brevity's sake, can isolate the day with indexing
Then find the correct month using a combination of casting and the built-in mod operator to index into the list of months
Use sv to join these lists with a "-" and repeat the process again to join on our initial weekend column (this time with " - ")

The following code fragement should help
monthDay:{ ("Jan"; "Feb"; "Mar"; "Apr"; "May"; "Jun"; "Jul";
"Aug"; "Sep"; "Oct"; "Nov"; "Dec")[(`mm$x)-1],'"-",'string `dd$x:"D"$x }
update weekend:(weekend,'" - ",/:monthDay weekend) from `t

Another option is to use the date parsing library available as part of Kx Developer: https://code.kx.com/developer/libraries/date-parser/#printing-dates
This lib provides a number of utilities for parsing dates & times from strings, and formatting them as strings from kdb+ datatypes
Once set up, usage is like so (first few commands are setting up the env for the libraries to work & loading them in a q session - this could also be done with \l):
jonny#kodiak ~ $ source ~/developer/config/config.profile
jonny#kodiak ~ $ export AXLIBRARIES_HOME=~/developer/
jonny#kodiak ~ $ q $AXLIBRARIES_HOME/ws/axruntimecore.q_
KDB+ 3.6 2018.12.06 Copyright (C) 1993-2018 Kx Systems
l64/ 4(16)core 7360MB jonny kodiak 127.0.1.1 EXPIRE 2020.06.04 jonathon.mcmurray#aquaq.co.uk KOD #4165225
q)t:flip (`contra`weekend`PnL)!(4#`abc;("2020.01.10";"2020.02.17";"2020.03.24";"2020.03.31");-222j, 844j, 1897j, 947j)
q)update .qdate.print["%b-%d";"D"$weekend] from t
contra weekend PnL
--------------------
abc "Jan-10" -222
abc "Feb-17" 844
abc "Mar-24" 1897
abc "Mar-31" 947
q)
Note that I had to parse the string dates in your example table to kdb+ dates with "D"$ as the qdate lib expects kdb+ date/time types.

Although I would prefer Igor's method, it may be useful for you to know that system commands can be used from q console which may offer you more flexibility in picking desired format. Can be used for example in this case:
conv:{"-"^6#4_first system"date -d ", "/" sv "." vs x}
update conv'[weekend] from t
edit:
For your exact output:
update (weekend,'" - ",/: conv'[weekend]) from t

Related

SPARQL how to make DAY/MONTH always return 2 digits

I have a datetime in my SPARQL-query that I want to transform to a date.
Therefore I do:
BIND(CONCAT(YEAR(?dateTime), "-",MONTH(?dateTime), "-", DAY(?dateTime)) as ?date)
This part of code works but returns for example 2022-2-3, I want it to be 2022-02-03. If the dateTime is 2022-11-23, nothing should change.
You can take the integers you get back from the YEAR, MONTH, and DAY functions and pad them with the appropriate number of zeros (after turning them into strings):
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT * WHERE {
BIND(2022 AS ?yearInt) # this would come from your YEAR(?dateTime) call
BIND(2 AS ?monthInt) # this would come from your MONTH(?dateTime) call
BIND(13 AS ?dayInt) # this would come from your DAY(?dateTime) call
# convert to strings
BIND(STR(?yearInt) AS ?year)
BIND(STR(?monthInt) AS ?month)
BIND(STR(?dayInt) AS ?day)
# pad with zeros
BIND(CONCAT("00", ?year) AS ?paddedYear)
BIND(CONCAT("0000", ?month) AS ?paddedMonth)
BIND(CONCAT("00", ?day) AS ?paddedDay)
# extract the right number of digits from the padded strings
BIND(SUBSTR(?paddedYear, STRLEN(?paddedYear)-3) AS ?fourDigitYear)
BIND(SUBSTR(?paddedDay, STRLEN(?paddedDay)-1) AS ?twoDigitDay)
BIND(SUBSTR(?paddedMonth, STRLEN(?paddedMonth)-1) AS ?twoDigitMonth)
# put it all back together
BIND(CONCAT(?fourDigitYear, "-", ?twoDigitMonth, "-", ?twoDigitDay) as ?date)
}
#gregory-williams gives a portable answer. An alternative is functions from F&O (XPath and XQuery Functions and Operators 3.1) "fn:format-...."
I'm not sure of the coverage in various triplestores - Apache Jena provides fn:format-number, which is needed for the question, but not fn-format-dateTime etc
See
https://www.w3.org/TR/xpath-functions-3/#formatting-the-number
https://www.w3.org/TR/xpath-functions-3/#formatting-dates-and-times
For example:
fn:format-number(1,"000") returns the string "001".
Apache Jena also has a local extension afn:sprintf using the C or Java syntax of sprintf:
afn:sprintf("%03d", 1) returns "001".

How to control the number of decimals places for display purpose only using Image function in Ada?

I have the following code line in Ada,
Put_Line ("Array of " & Integer'Image (iterations)
& " is " & Long_Float'Image (sum)
& " Time = " & Duration'Image(milliS) & timescale);
The number of decimal places in sum is too long for display (not for calculations since long float is needed for sum calculations). I know that Ada has alternative way of displaying decimals using aft and fore without using the Image function but before I switch to alternative I would like to know if Image has options or other technique of displaying decimals. Does Image function has an option to display decimals? Is there a technique to shorten the number of decimal places of the Long_Float for display only?
with Ada.Numerics;
with Ada.Text_IO; use Ada.Text_IO;
procedure Images is
sum : Standard.Long_Float;
Pi : Long_Float := Ada.Numerics.Pi;
type Fixed is delta 0.001 range -1.0e6 .. 1.0e6;
type NewFixed is range -(2 ** 31) .. +(2 ** 31 - 1);
type Fixed2 is new Long_Float range -1.0e99.. 1.0e99;
type Fixed3 is new Long_Float range -(2.0e99) .. +(2.0e99);
begin
sum:=4.99999950000e14;
Put_Line ("no fixing number: " & Pi'Image);
Put_Line (" fixed number: " & Fixed'Image(Fixed (Pi)));
Put_Line ("no fixing number: " & Long_Float'Image(sum));
Put_Line (" testing fix: " & Fixed3'Image(Fixed3 (sum)));
end Images;
Addendum:
Note that my variable sum is defined as Standard.Long_Float to agree with other variables used throughout the program.
I am adding code to show the culprit of my problem and my attempts at solving the problem. It is based on example provided by Simon Wright with sum number added by me. Looks like all I need to figure out how to insert delta into Fixed3 type since delta defines number of decimals.
’Image doesn’t have any options, see ARM2012 3.5(35) (also (55.4)).
However, Ada 202x ARM K.2(88) and 4.10(13) suggest an alternative:
with Ada.Numerics;
with Ada.Text_IO; use Ada.Text_IO;
procedure Images is
Pi : Long_Float := Ada.Numerics.Pi;
type Fixed is delta 0.001 range -1.0e6 .. 1.0e6;
begin
Put_Line (Pi'Image);
Put_Line (Fixed (Pi)'Image);
end Images;
which reports (GNAT CE 2020, FSF GCC 10.1.0)
$ ./images
3.14159265358979E+00
3.142
The Image attribute has the same parameters for all the types, so format cannot be specified. There are generic nested packages in Ada.Text_IO for handling I/O of numeric types. For your case you can instantiate Ada.Text_IO.Float_IO for Float or just use the built-in Ada.Float_Text_IO instance. You can then use the Put procedure to specify the format. There is an example in the Ada Standard:
package Real_IO is new Float_IO(Real); use Real_IO;
-- default format used at instantiation, Default_Exp = 3
X : Real := -123.4567; -- digits 8 (see 3.5.7)
Put(X); -- default format "–​1.2345670E+02"
Put(X, Fore => 5, Aft => 3, Exp => 2); -- "bbb–​1.235E+2"
Put(X, 5, 3, 0); -- "b–​123.457"
Long_Float'Image (sum) is just a regular string in the form "snnnnn.ddddd". Note that character 's' representing sign, character 'n' representing number and character 'd' representing decimal.
Thus, if you want to drop the last three decimals, just use the following
Long_Float'Image(sum)(Long_Float'Image(sum)'First .. Long_Float'Image(sum)'Last - 3);
As others have pointed out, the 'Image attribute function doesn't provide for any control over the format. However, it is certainly possible to write a function that does. See PragmARC.Images.Float_Image for a generic example.

Convert symbol to a list of symbols

I read in a csv file:
csvFile:1!("SSS"; enlist ",") 0: hsym `$"\\\\location\\of\\csv";
But lists of symbols are read as single symbols. e.g. in the csv file I have 'a'b'c but
csvFile`some_keyed_value
`col1`col2!``a`b`c`
What I want is this - and note how a single ticker should be an empty list:
`col1`col2!(`a`b`c;())
Is there a way to make this cast or read in the csv differently or modify the csv so that it reads in correctly? Any modifications I make to the csv (e.g. replacing ' with () ) simply converts it to a single symbol (e.g. I get '() ).
Here is a screenshot of a few lines from the csv
For your input, the following works. You can also use cut instead of vs, as in previous example.
q)update `$table, `$_[1;]each vs["`";]each writeAllow, `$_[1;]each
vs["`";]each writeLog from ("***";enlist",")0:`:tmp.csv
table writeAllow writeLog
------------------------------------------------------
:/loader/P1 `pg`sec-fg-id `symbol$()
:/loader/P2 `pg`shara`mcdonald `pg`MD`svc
:/loader/P3 `symbol$() `pg`MD`svc
You should probably reconsider storing the sym data with backticks - it would be straightforward to store with a different delimiter to separate the sub-records and have a dedicated function for parsing those fields.
Taking this csv as an example:
cat ex.csv
x,y
`aa`bb,
`cc`dd,`ee`ff
,`gg`hh
You need to load those nested symbol columns in as strings first:
q)show tab:("**";enlist",")0:`:ex.csv
x y
-----------------
"`aa`bb" ""
"`cc`dd" "`ee`ff"
"" "`gg`hh"
From here you then need to drop the backticks and convert the strings to symbols. One possible way to do this is:
q)update {`$1_'where["`"=x]cut x}'[x] from tab
x y
-------------------
`aa`bb ""
`cc`dd "`ee`ff"
`symbol$() "`gg`hh"
You can also utilize the function value to convert a string with embedded backticks to a symbol list.
// read in as strings as others have said
q)("**";1#",")0:`:test.csv
x y
-------------
"`a`b" "`c`d"
"`e`f" "`g`f"
// value each string to convert to symbol lists
q)value#''("**";1#",")0:`:test.csv
x y
-------
a b c d
e f g f
// check its now nested symbol type
q)meta value#''("**";1#",")0:`:test.csv
c| t f a
-| -----
x| S
y| S
You can of course use value on specific columns that you need if it serves your purposes too, ie update value each col1 from ...
q)t
table writeAllow writeLog
----------------------------------------------------------
:loader/p1 "`pg`sec-fg-id" ""
:loader/p2 "`pg`shara`cmacdonald" "`pg`MD`svc"
:loader/p3 "" "`pg`MD`svc"
q)foo:(`$1_"`" vs) each
q)update foo[writeAllow], foo[writeLog] from t
table writeAllow writeLog
------------------------------------------------------
:loader/p1 `pg`sec-fg-id `symbol$()
:loader/p2 `pg`shara`mcdonald `pg`MD`svc
:loader/p3 `symbol$() `pg`MD`svc

How to ssr to replace all string in kdb?

table dd
dd:([]a:("account=abcde;cash=123";"account=abc;cash=345");1 2)
result:
"account=abcde;cash=123" 1
"account=abc;cash=345" 2
I want to replace everything in account:
so the result should be
"replace;cash=123" 1
"replace;cash=345" 2
I tried this
update ssr[;"account=* ;";""] each a from dd
but it didn't work.
Try the following:
q)update {";"sv #[s;where (s:";"vs x) like "account=*";:;enlist"newVal=123"]}each a from dd
And just to throw one more answer on the pile! If you can assume the account part you want to replace is always the first member of the ; separated list you can use the slightly simpler:
update ";"sv'#[;0;:;"replace"]each";"vs'a from dd
can't use wild card * with ssr information on http://code.kx.com/q/cookbook/regex/#regex-in-q
for integrating with regex libraries.
alternative solution to ssr:
update a:{v:(" *";";")0:x;y,/:v 0}[a;"replacestring;"] from dd where a like "account=\*"
You can also try the following if you want to explicitly use ssr:
update {ssr[first "=" vs x;"account";"replace",";",last ";" vs x]} each a from dd
a x
--------------------
"replace;cash=123" 1
"replace;cash=345" 2

crystal formula loop returns only last record

using Crystal XI
I have two arrays - one captures a list of timepoints the other captures the times associated with them.
I need to associate the two so that the output looks like
timepoint[1] - time [1] - timepoint[2] - timepoint[2];
timepoint[3] - time[3] - timepoint[4] - timepoint[4];
so i wronte the following code - the arrays are intitialized in the header then loaded with data in the details section and then displayed in the group footer (three formula trick). I can display the data in the two arrays seperatly ok
(as timepoint[1], timepoint[2], etc)
the issue only arrises when trying to combine the two. the code below only prints the last two records in the array for the gorup instead of all of the records.
so if there are 5 timepoints and 5 times the code below displays
timepoint[4]-time[4] - timepoint[5]-time[5]
I will have to eventually perform a calculation between the two associated date times but for now just trying to get the association and display working.
Shared stringVar array Timepoints;
Shared DateTimeVar array Times;
local stringVar combineStr;
local numbervar i;
For i:=1 to UBound(Times)-2 do
(combineStr := Timepoints[i] + ','+ totext(Times[i]) + '-' +
Timepoints[i+1] + totext(Times[i+1]) + ',');
combineStr;
thanks for looking at this
You're overwriting the previous value of the combineStr variable with each iteration. You need to do this:
.
.
.
For i:=1 to UBound(Times)-2 do
(combineStr := combineStr + Timepoints[i] + ','+ totext(Times[i]) + '-' +
Timepoints[i+1] + totext(Times[i+1]) + ',');
combineStr;
A few other things: 1. You'll want to concatenate a newline character, chr(10), instead of a comma between iterations to display like your example in the question. 2. You need to add "step 2" to your for-loop otherwise you'll be printing out timepoints twice.