suppose to have the following:
ID Index1 Index2
0001 0 0
0001 0 0
0001 2 0
0001 0 3
0002 0 0
0002 2 0
0002 0 4
Is there a way to update the values different from 0 in column Index1 based on Index2?
In other words if the subsequent value in Index2 is >=2 then the previous (not 0) in Index1 column must be 1.
Desired output:
ID Index1 Index2
0001 0 0
0001 0 0
0001 1 0
0001 0 3
0002 0 0
0002 1 0
0002 0 4
SAS doesn't support leads directly, but you can do it a number of ways. Here's an easy method that merges the data onto itself using a 1-1 merge, but increments the second table by a single observation in order to get a "lead".
data want;
merge have
have(firstobs=2
keep=id index2
rename=(id = lead_id
index2 = lead_index2
)
)
;
if(lead_index2 > 0 AND index1 > 0 AND id = lead_id) then index1 = 1;
drop lead_index2;
run;
By setting firstobs=2, we're telling SAS to merge these two tables:
Table 1 Table 2
------------------ | --------------------
ID Index1 Index2 | lead_id lead_Index2
1 0 0 | 1 0
1 0 0 | 1 0
1 2 0 | 1 3
1 0 3 | 2 0
2 0 0 | 2 0
2 2 0 | 2 4
2 0 4 |
Since it's trying to do a 1-1 merge, the last observation will be missing since there's no match between the last observation of table 1 and table 2. This makes our logic really easy to compare: if lead_Index2 is > 0, Index1 is > 0, and the IDs match then set index1 to 1.
This would be easier if the observations were ordered in the opposite order. It is easier to remember something than to predict the future. In SAS you can remember things with a retained variable or the LAG() function.
data want;
set have;
by id;
lag_index2 = lag(index2);
if first.id then call missing(lag_index2);
index1= (lag_index2 > 1) ;
run;
If you really need to look ahead you can read the data twice. The second time start from the second observation. To make the number of observations match include an empty observation at the end.
data want;
set have;
by id;
set have(firstobs=2 keep=index2 rename=(index2=lead_index2))
have(obs=1 drop=_all_)
;
if last.id then call missing(lead_index2);
index1= (lead_index2 > 1) ;
run;
suppose to have the following data set.
ID Hired Start_date End_date Flag_Start Flag_End
0001 1-1900 01JAN2018 21DEC2018 1 2
0001 1-1900 01JAN2019 01DEC2020 2 2
0002 10-2020 26MAR2020 03MAY2020 1 2
0003 03-2021 18DEC2020 31DEC2020 1 2
..... ....... ......... ......... ........... ...........
I would like the desired output. Sorry if I ask you but I'm a newbie and this seems to be a very difficult task with SAS. I'm familiar with R.
Desired output:
ID Hired Start_date End_date Flag_Start Flag_End
0001 1-1900 01JAN2018 21DEC2018 1 2
0001 1-1900 01JAN2019 01DEC2020 2 3
0002 03-2020 26MAR2020 03MAY2020 1 0
0003 03-2021 18DEC2020 31DEC2020 1 3
..... ....... ......... ......... ........... ...........
So, for each ID, if, after sorting, the last End_date is "x" and the "Hired" is 1-1900 then in Flag_End add 3 otherwise if Hired is < End_date add 0 otherwise if Hired is > End_date but not 1-1900 add 3.
Thank you in advance
I think this is what you want.
The Hired Date does not match between your two posted data sets. I chose the second one (03-2020).
data have;
input ID $ Hired :anydtdte. (Start_date End_date)(:date9.) Flag_Start Flag_End;
format Hired Start_date End_date date9.;
datalines;
0001 1-1900 01JAN2018 21DEC2018 1 2
0001 1-1900 01JAN2019 01DEC2020 2 2
0002 03-2020 26MAR2020 03MAY2020 1 2
0003 03-2021 18DEC2020 31DEC2020 1 2
;
data want;
set have;
by ID;
if last.ID then do;
if Hired = '01jan1900'd then flag_end = 3;
else if Hired < End_date then flag_end = 0;
else if Hired >= End_date then flag_end = 3;
end;
run;
I have this parameter code specification
"Create a record with PARAMCD set to "DLQI7" for every record in SDTM.QS where QS.QSTESTCD equals "DLQI7". If there is a corresponding (by USUBJID, VISITNUM) record where QS.QSTESTCD equals "DLQI7SCO" and QS.QSSTRESN is not missing then set AVAL to the non-missing value of QS.QSSTRESN from this record. Else, if QS.QSORRES equals "00" on the original record where QS.QSTESTCD equals "DLQI7", then set AVAL to 0."
Here is a sample of my data with the relevant columns
USUBJID VISITNUM QSTESTCD QSSTRESN QSORRES
1001 2 DLQI7 0 0
1001 4 DLQI7 0 0
1001 5 DLQI7 0 0
1001 6 DLQI7 0 0
1001 2 DLQI7SCO 0 0
1001 4 DLQI7SCO 0 0
1001 5 DLQI7SCO 0 0
1001 6 DLQI7SCO 0 0
1002 2 DLQI7 0 0
1002 4 DLQI7 0 0
1002 5 DLQI7 0 0
1002 6 DLQI7 0 00
1002 2 DLQI7SCO 1 1
1002 4 DLQI7SCO 1 1
1002 5 DLQI7SCO 1 1
1002 6 DLQI7SCO
1002 2 DLQI7 0
1002 10 DLQI7 0
1002 2 DLQI7SCO 2 2
1002 10 DLQI7SCO 0 0
1003 2 DLQI7 0 0
1003 2 DLQI7SCO 1 1
1004 2 DLQI7 0 00
1004 4 DLQI7 0 00
1004 5 DLQI7 0 00
1004 2 DLQI7SCO
1004 4 DLQI7SCO
1004 5 DLQI7SCO
What's the best approach here?. I have to remove the DLQI7SCO or DLQI7 records depending on if they are non-missing as it has to be one record where QS.QSTESTCD equals "DLQI7" for each subject. Should I use proc transpose for the DLQI7SCO records and then maybe use a combination of nmiss and coalesce to produce AVAL?
Let's break this down step by step and translate each statement into a piece of code.
1. Create a record with PARAMCD set to "DLQI7" for every record in SDTM.QS where QS.QSTESTCD equals "DLQI7"
if(QSTESTCD = 'DLQI7') then PARAMCD = QSTESTCD;
2. If there is a corresponding (by USUBJID, VISITNUM) record where QS.QSTESTCD equals "DLQI7SCO" and QS.QSSTRESN is not missing then set AVAL to the non-missing value of QS.QSSTRESN from this record.
if(QSTESTCD = 'DLQI7SCO' AND NOT missing(QSSTRESN) ) then AVAL = QSSTRESN
3. Else, if QS.QSORRES equals "00" on the original record where QS.QSTESTCD equals "DLQI7", then set AVAL to 0."
else if(QSORRES = '00' AND QSTESTCD = 'DLQI7') then AVAL = 0;
The data looks like it is sorted in a particular order. We'll leave it in that order.
This yields the following program:
data want;
set have;
by usubjid qtestcd visitnum notsorted;
if(QSTESTCD = 'DLQI7') then PARAMCD = QSTESTCD;
if(QSTESTCD = 'DLQI7SCO' AND NOT missing(QSSTRESN) ) then AVAL = QSSTRESN;
else if(QSORRES = '00' AND QSTESTCD = 'DLQI7') then AVAL = 0;
run;
If you can provide me with some feedback on if this is the right approach then that would be helpful, but this is my best direct translation of the instructions.
I'm working in a dashboard that control some kpis at my company. Now, I need to compare every result with the previous one, according to employee number. In the sample below, I show a little exemple of my data, and the expected result in the last column (previous score). I've tryed to solve that using a lot os calculated columns. I got close using the following:
PreviousScore =
VAR EMPNUMBER = BASE[Employee Number]
VAR REF = BASE[Score]
RETURN
CALCULATE(LASTNONBLANK(BASE[Employee Number];EMPNUMBER);FILTER(BASE;BASE[Employee Number]=EMPNUMBER);FILTER(BASE;BASE[Date]<EARLIER(BASE[Date])))
Employee Number Date Score Previous score
1234 01/01/2019 1 BLANK
1235 01/01/2019 4 BLANK
1236 01/01/2019 2 BLANK
1288 01/01/2019 0 BLANK
1259 01/01/2019 0 BLANK
1234 01/02/2019 3 1
1235 01/02/2019 4 4
1236 01/02/2019 1 2
1288 01/02/2019 2 0
1259 01/02/2019 4 0
1234 01/03/2019 1 3
1235 01/03/2019 2 4
1236 01/03/2019 3 1
1288 01/03/2019 0 2
1259 01/03/2019 1 4
1234 01/04/2019 2 1
1235 01/04/2019 3 2
1236 01/04/2019 8 3
1288 01/04/2019 BLANK 0
1259 01/04/2019 BLANK 1
I hope someone could help with this issue.
LW
You have no dependance between your dates, you pickup the earlier column and by accident they are in the right order.
The following query will give you the right result:
Previous score =
var empNR = score[Employee Number]
var theDate = score[Date]
var empData = FILTER(score;score[Employee Number] = empNR)
var prevDate = CALCULATE(MAX(score[Date]);empData;score[Date] < theDate)
return CALCULATE(MAX(score[Score]);FILTER(empData; score[Date] = prevDate))
I filter on emp number to get the data of that employee
I get the prevDate by getting the max of all dates which are lower
than selected date
last step is to get the one row and return the score
The Validation code looks like this:
public function validate($value, Constraint $constraint)
{
if (null === $value || '' === $value) {
return;
}
$teststring = preg_replace('/\s+/', '', $value);
if (strlen($teststring) < 4) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
return;
}
$teststring = substr($teststring, 4)
. strval(ord($teststring{0}) - 55)
. strval(ord($teststring{1}) - 55)
. substr($teststring, 2, 2);
$letterToInt = function ($letter) {
return intval(ord(strtolower($letter[0])) - 87);
};
$teststring = preg_replace_callback('/[A-Za-z]/', $letterToInt, $teststring);
$rest=0;
$strlen = strlen($teststring);
for ($pos = 0; $pos < $strlen; $pos += 7) {
$part = strval($rest) . substr($teststring, $pos, 7);
$rest = intval($part) % 97;
}
if ($rest != 1) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
return;
}
}
For a valid UK IBAN this test fails.
A Valid IBAN code looks like this: GB29 RBOS 6016 1331 9268 19
The test codes looks like
public function getValidIbans()
{
return array(
array('CH93 0076 2011 6238 5295 7'), //Switzerland
array('CH9300762011623852957'), // Switzerland without spaces
//Country list
//http://www.rbs.co.uk/corporate/international/g0/guide-to-international-business/regulatory-information/iban/iban-example.ashx
// array('GB29 RBOS 6016 1331 9268 19'), //United Kingdom -- currently fails!
array('AL47 2121 1009 0000 0002 3569 8741'), //Albania
array('AD12 0001 2030 2003 5910 0100'), //Andorra
array('AT61 1904 3002 3457 3201'), //Austria
array('AZ21 NABZ 0000 0000 1370 1000 1944'), //Azerbaijan
array('BH67 BMAG 0000 1299 1234 56'), //Bahrain
array('BE62 5100 0754 7061'), //Belgium
array('BA39 1290 0794 0102 8494'), //Bosnia and Herzegovina
array('BG80 BNBG 9661 1020 3456 78'), //Bulgaria
array('HR12 1001 0051 8630 0016 0'), //Croatia
array('CY17 0020 0128 0000 0012 0052 7600'), //Cyprus
array('CZ65 0800 0000 1920 0014 5399'), //Czech Republic
array('DK50 0040 0440 1162 43'), //Denmark
array('EE38 2200 2210 2014 5685'), //Estonia
array('FO97 5432 0388 8999 44'), //Faroe Islands
array('FI21 1234 5600 0007 85'), //Finland
array('FR14 2004 1010 0505 0001 3M02 606'), //France
array('GE29 NB00 0000 0101 9049 17'), //Georgia
array('DE89 3704 0044 0532 0130 00'), //Germany
array('GI75 NWBK 0000 0000 7099 453'), //Gibraltar
array('GR16 0110 1250 0000 0001 2300 695'), //Greece
array('GL56 0444 9876 5432 10'), //Greenland
array('HU42 1177 3016 1111 1018 0000 0000'), //Hungary
array('IS14 0159 2600 7654 5510 7303 39'), //Iceland
array('IE29 AIBK 9311 5212 3456 78'), //Ireland
array('IL62 0108 0000 0009 9999 999'), //Israel
array('IT40 S054 2811 1010 0000 0123 456'), //Italy
array('LV80 BANK 0000 4351 9500 1'), //Latvia
array('LB62 0999 0000 0001 0019 0122 9114'), //Lebanon
array('LI21 0881 0000 2324 013A A'), //Liechtenstein
array('LT12 1000 0111 0100 1000'), //Lithuania
array('LU28 0019 4006 4475 0000'), //Luxembourg
array('MK072 5012 0000 0589 84'), //Macedonia
array('MT84 MALT 0110 0001 2345 MTLC AST0 01S'), //Malta
array('MU17 BOMM 0101 1010 3030 0200 000M UR'), //Mauritius
array('MD24 AG00 0225 1000 1310 4168'), //Moldova
array('MC93 2005 2222 1001 1223 3M44 555'), //Monaco
array('ME25 5050 0001 2345 6789 51'), //Montenegro
array('NL39 RABO 0300 0652 64'), //Netherlands
array('NO93 8601 1117 947'), //Norway
array('PK36 SCBL 0000 0011 2345 6702'), //Pakistan
array('PL60 1020 1026 0000 0422 7020 1111'), //Poland
array('PT50 0002 0123 1234 5678 9015 4'), //Portugal
array('RO49 AAAA 1B31 0075 9384 0000'), //Romania
array('SM86 U032 2509 8000 0000 0270 100'), //San Marino
array('SA03 8000 0000 6080 1016 7519'), //Saudi Arabia
array('RS35 2600 0560 1001 6113 79'), //Serbia
array('SK31 1200 0000 1987 4263 7541'), //Slovak Republic
array('SI56 1910 0000 0123 438'), //Slovenia
array('ES80 2310 0001 1800 0001 2345'), //Spain
array('SE35 5000 0000 0549 1000 0003'), //Sweden
array('CH93 0076 2011 6238 5295 7'), //Switzerland
array('TN59 1000 6035 1835 9847 8831'), //Tunisia
array('TR33 0006 1005 1978 6457 8413 26'), //Turkey
array('AE07 0331 2345 6789 0123 456'), //UAE
);
Are you sure the UK IBAN is valid? I tried a number of online validators, and neither of them seemed to accept it.
You could try them yourself:
www.paymentscouncil.org.uk
www.natwest.com
Your validator accepts GB29 NWBK 6016 1331 9268 19 (taken from here) and GB19 LOYD 3096 1700 7099 43 (from here).
I think the checksum number in your IBAN number is incorrect, if you change it from 29 to 86 as such: GB86 RBOS 6016 1331 9268 19 it will be valid.