SAS - hash tables and has_next - hashtable

I'm looking for an elegant solution to the below issue that will help avoid code duplication. You can see that this line:
put auction_id= potential_buyer= ;* THIS GETS REPEATED;
Gets repeated in this code:
data results;
attrib potential_buyer length=$1;
set auction;
if _n_ eq 1 then do;
declare hash ht1(dataset:'buyers', multidata: 'y');
ht1.definekey('auction_id');
ht1.definedata('potential_buyer');
ht1.definedone();
call missing (potential_buyer);
end;
**
** LOOP THROUGH EACH POTENTIAL BUYER AND PROCESS THEM
*;
if ht1.find() eq 0 then do;
put auction_id= potential_buyer= ;* THIS GETS REPEATED;
ht1.has_next(result: ht1_has_more);
do while(ht1_has_more);
rc = ht1.find_next();
put auction_id= potential_buyer= ;* THIS GETS REPEATED;
ht1.has_next(result: ht1_has_more);
end;
end;
run;
I've simplified the above example to a single line as the real code block is quite long and complex. I'd like to avoid using a %macro snippet or a %include if possible as I'd like to keep the logic "within" the data step.
Here's some sample data:
data auction;
input auction_id;
datalines;
111
222
333
;
run;
data buyers;
input auction_id potential_buyer $;
datalines;
111 a
111 c
222 a
222 b
222 c
333 d
;
run;

I figured it out. Turned out to be pretty simple in the end just had a little trouble wrapping my brain around it:
data results;
attrib potential_buyer length=$1;
set auction;
if _n_ eq 1 then do;
declare hash ht1(dataset:'buyers', multidata: 'y');
ht1.definekey('auction_id');
ht1.definedata('potential_buyer');
ht1.definedone();
call missing (potential_buyer);
end;
**
** LOOP THROUGH EACH POTENTIAL BUYER AND PROCESS THEM
*;
if ht1.find() eq 0 then do;
keep_processing = 1;
do while(keep_processing);
put auction_id= potential_buyer= ;* THIS GETS DOESNT GET REPEATED ANYMORE =);
ht1.has_next(result: keep_processing);
rc = ht1.find_next();
end;
end;
run;

You can solve it this way....but Rob's answer is better.
data results;
%Macro NoDuplicate;
Put auction_id= potential_buyer= ; * No Longer Duplicated;
%Mend noduplicate;
attrib potential_buyer length=$1;
set auction;
if _n_ eq 1 then do;
declare hash ht1(dataset:'buyers', multidata: 'y');
ht1.definekey('auction_id');
ht1.definedata('potential_buyer');
ht1.definedone();
call missing (potential_buyer);
end;
**
** LOOP THROUGH EACH POTENTIAL BUYER AND PROCESS THEM
*;
if ht1.find() eq 0 then do;
%NoDuplicate
ht1.has_next(result: ht1_has_more);
do while(ht1_has_more);
rc = ht1.find_next();
%NoDuplicate
ht1.has_next(result: ht1_has_more);
end;
end;
run;

Related

How do you correctly debug A/D convertor code in MPLAB X v5.05 simulator

This is a continuation of the question posed in How do you run a SCL file in MPLAB without a "Run SCL" button
I have an assembly code for PIC18F458 that gets data from channel 0 (RA0) of ADC and displays the result on PORTC and PORTD.
Although, I have managed to verify that the code operates as desired within Proteus, I am struggling to do the same within the MPLAB X simulator environment using a SCL file, and I suspect that this is due to the way that the text files, referred to by it, are laid out. (Please see below)
testbench for "pic18f458" is
begin
process is
file datafile : text;
variable intVar : integer;
variable sampling_voltage : integer;
variable fileStatus : file_open_status;
variable fileLine : line;
begin
loop
report("Analog injection started...");
file_open(fileStatus, datafile, "text2.txt", read_mode);
if fileStatus == open_ok then
report("Reading the values file...");
while endfile(datafile) == false loop
wait until ADCON0.GO_nDONE == '1';
report("Conversion started");
readline(datafile, fileLine);
wait for 400 ns;
read(fileLine, intVar);
sampling_voltage := intVar; -- sample input voltage
wait until ADCON0.GO_nDONE == '0';
report("Conversion ended");
if ADCON1.ADFM == '0' then -- left justified
ADRESH <= sampling_voltage / 4;
ADRESL <= sampling_voltage * 64;
else -- right justified
ADRESH <= sampling_voltage / 256;
ADRESL <= sampling_voltage;
end if;
end loop;
file_close(datafile);
end if;
end loop;
wait;
end process;
end testbench;
The SCL file refers to 1 of 2 text files during my debugging session (text.txt and text2.txt) laid out differently. The first consists of decimal numbers from 0 to 255 and the second consists of decimal numbers representing voltages in mV.
txt.txt
128
192
238
255
238
192
128
64
17
0
17
64
128
text2.txt
250 mV
500 mV
750 mV
1000 mV
1250 mV
1500 mV
1750 mv
2000 mV
2250 mv
2500 mv
2750 mv
3000 mv
3250 mV
3500 mV
3750 mV
4000 mV
4250 mV
4500 mV
4750 mv
5000 mV
In both cases, the ADC seems to just be churning out the numbers that it is receiving, instead of converting them. (Please see images below)
This is obviously no good, as I am getting values within the ADRES register that are greater than 10-bits, in particularly, with regards to my text2.txt values.
ADC Results with text.txt
ADC Results with text2.txt
Therefore, my question is how do I correctly debug my A/D convertor code in MPLAB X v5.05 simulator using a SCL file or any other methods?

Lua: recursive function builts wrong table - pil4

while working on the exercise 2.2 of "programming in Lua 4" I do have to create a function to built all permutations of the numbers 1-8. I decided to use Heaps algorithm und made the following script. I´m testing with numbers 1-3.
In the function I store the permutations as tables {1,2,3} {2,1,3} and so on into local "a" and add them to global "perm". But something runs wrong and at the end of the recursions I get the same permutation on all slots. I can´t figure it out. Please help.
function generateperm (k,a)
if k == 1 then
perm[#perm + 1] = a -- adds recent permutation to table
io.write(table.unpack(a)) -- debug print. it shows last added one
io.write("\n") -- so I can see the algorithm works fine
else
for i=1,k do
generateperm(k-1,a)
if k % 2 == 0 then -- builts a permutation
a[i],a[k] = a[k],a[i]
else
a[1],a[k] = a[k],a[1]
end
end
end
end
--
perm = {}
generateperm(3,{1,2,3}) -- start
--
for k,v in ipairs (perm) do -- prints all stored permutations
for k,v in ipairs(perm[k]) do -- but it´s 6 times {1,2,3}
io.write(v)
end
io.write("\n")
end
debug print:
123
213
312
132
231
321
123
123
123
123
123
123

Delphi displays strange results for operations with negative values to TDateTime

We have a solution in Delphi that calculates a travel's duration of a given vehicle, for example, 20 minutes, 25 minutes and so on. However, sometimes we have to antecipate the travel's start time, from a specific datetime, for example 09:00 to 08:40. Then, we need to substract a negative value from a TDateTime variable (travel's start), in this case, something like "-00:20". To do this, we multiply the datetime value by -1 (for example MyDiffDateTimeVariable * -1). The output we got is very strange, sometimes we obtain the exactly opposite behavior. In other case, an operation to extract 20 minutes results in a difference of two days from the original datetime.
Here is a sample console application that simulate our situation, with the current outputs, and what we will expected:
program DateTimeSample;
uses
System.SysUtils, System.DateUtils;
var
LDate1: TDateTime;
LDate2: TDateTime;
begin
LDate1 := IncMinute(0, 20);
LDate2 := IncMinute(0, -20);
WriteLn('Date1: ' + DateTimeToStr(LDate1));
// Output = Date1: 30/12/1899 00:20:00 [OK]
WriteLn('Date2: ' + DateTimeToStr(LDate2));
// Output = Date2: 29/12/1899 23:40:00 [OK]
WriteLn('-----');
WriteLn('Date1: ' + DateTimeToStr(LDate1 * -1));
// Output = Date1: 30/12/1899 00:20:00 [Expected 29/12/1899 23:40:00]
WriteLn('Date2: ' + DateTimeToStr(LDate2 * -1));
// Output = Date2: 31/12/1899 23:40:00 [Expected 30/12/1899 00:20:00]
ReadLn;
end.
When you inspect the value casted to double, you can see:
double(LDate1) = 0.0138888888888889
double(LDate2) = -1.98611111111111
Seems like a bug to me, because with today it returns:
double(LDate1) = 43168,0138888889
double(LDate2) = 43167,9861111111
Edit: Hmm, according the documentation, it is not a bug, it is a feature :-)
When working with negative TDateTime values, computations must handle time portion separately. The fractional part reflects the fraction of a 24-hour day without regard to the sign of the TDateTime value. For example, 6:00 A.M. on December 29, 1899 is –1.25, not –1 + 0.25, which would equal –0.75. There are no TDateTime values from –1 through 0.
Karel's answer explains what's happening. Basically, TDateTime is represented as a Double, but that doesn't mean you can work with it in the same way as you normally would a Double value. It's internal structure carries particular semantics that if you don't handle them correctly, you're bound to get some peculiar behaviour.
The key mistake you're making is in taking the negative of a date-time value. This concept doesn't really make sense. Not even if you look at dates in BC, because the calendar system has changed a number of times over the years.
This is the main reason you should favour library routines that deal with the nuances of the internal structure (whatever your platform). In Delphi that means you should use the SysUtils and DateUtils routines for working with dates and times.
You seem to be trying to hold duration as a TDateTime value. You'd be much better off determining your preferred unit of measure and using Integer (perhaps Int64) or Double (if you need support for fractions of a unit). Then you can add or subtract, preferably using library routines, the duration from your start or end times.
The following code demonstrates some examples.
var
LStartTime, LEndTime: TDateTime;
LDuration_Mins: Integer;
begin
{ Init sample values for each calculation }
LStartTime := EncodeDateTime(2018, 3, 9, 8, 40, 0, 0);
LEndTime := EncodeDateTime(2018, 3, 9, 9, 0, 0, 0);
LDuration_Mins := 20;
{ Output result of each calculation }
Writeln(Format('Whole Duration: %d', [MinutesBetween(LStartTime, LEndTime)]));
Writeln(Format('Frac Duration: %.6f', [MinuteSpan(LStartTime, LEndTime)]));
Writeln(Format('Start Time: %s', [FormatDateTime('yyyy-mm-dd hh:nn:ss', IncMinute(LEndTime, -LDuration_Mins))]));
Writeln(Format('End Time: %s', [FormatDateTime('yyyy-mm-dd hh:nn:ss', IncMinute(LStartTime, LDuration_Mins))]));
end;
Additional Considerations
You said you're dealing with vehicle travel times. If you're dealing with long-haul travel you might have some other things to think about.
Daylight saving: If a vehicle starts its journey shortly before DST changes and ends after, you need to take this into account when calculating a missing value. Perhaps easiest would be to convert date-time values to UTC for the calculation. Which leads to...
Time zone changes: Again, unless your code is time-zone aware you're bound to make mistakes.
Compiler always appears to treat TDateTime as positive when doing numerical operations on it. Try this:
uses
System.SysUtils, System.DateUtils;
function InvertDate(ADateTime: TDateTime): TDateTime;
var
LMsec: Int64;
begin
LMsec := MillisecondsBetween(ADateTime, 0); //Always Positive
if ADateTime > 0 then
LMsec := 0 - LMsec;
Result := IncMillisecond(0, LMsec);
end;
var
LDate1: TDateTime;
LDate1Negative: TDateTime;
LDate2: TDateTime;
begin
try
LDate1 := IncMinute(0, 20);
LDate2 := IncMinute(0, -20);
WriteLn('Date1: ' + DateTimeToStr(LDate1));
// Output = Date1: 30/12/1899 00:20:00 [OK]
WriteLn('Date2: ' + DateTimeToStr(LDate2));
// Output = Date2: 29/12/1899 23:40:00 [OK]
WriteLn('-----');
WriteLn('Date1: ' + DateTimeToStr( InvertDate(LDate1) ));
// Output = Date1: Expected 29/12/1899 23:40:00
WriteLn('Date2: ' + DateTimeToStr( InvertDate(LDate2) ));
// Output = Date2: 30/12/1899 00:20:00
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

proc gplot Spaghetti Plot on more than 255 subjects

Hi I have a question when trying to make a spaghetti plot. I don't want each subject to have different symbols or colors. I just need them to each have a black segmented line. I have been able to do it successfully with fewer subjects, by just create the same symbol statement for everyone and use gplot, but when I do it with more than 255 subjects, SAS complains that I can't have more than 255 symbols. Is there a way to do this?
data _null_;
set ptdata&trtn. end=eof;
retain patcount 0;
by usubjid;
if first.usubjid then patcount+1;
if last.usubjid then lastgfr='Y';
call symput('sym'||trim(left(patcount)),
'symbol'||trim(left(patcount))
|| ' '|| 'c=black'|| ' '||'v=Dot'||' '
|| 'i=join'|| ' ' || 'line=1' || 'width=1' ||';');
if eof then call symput('total',patcount);
run;
%macro symbol;
%do j=1 %to &total;
&&sym&j
%end;
%mend symbol;
%symbol
proc gplot data = ptdata&trtn. ;
plot change_since_bl*FUPTIME=usubjid /haxis=axis3 vaxis=axis4 href=0 nolegend;
format change_since_bl 8. ;*/
run ;
I would use PROC SGPLOT, it is not limited to 255 like GPLOT and it is easier to use.
Try this:
data test;
do person=1 to 256;
value = 100;
do time=0 to 10;
value = value + rannor(1);
output;
end;
end;
run;
proc sgplot data=test noautolegend;
series x=time y=value / group=person lineattrs=(color=black pattern=dash) ;
run;
I think this is what you are looking for.

Unix formatting text into table with grep or awk or sed

I have been able to locate things no problem with grep however the assignment is basically pulling data out and formatting it and displaying it as a table columns with multiple rows. Now it shouldn't be anything crazy because we only have basic knowledge of awk and sed. Now I'm curious: is there any way to take my output from grep and format it so for example I get:
Jake
0001
Bob
0002
Kim
0003
and want to make it something like this
# Name LD #
--- ---- ----
1 Jake 0001
2 Bob 0002
3 Kim 0003
Also is it possible to explain each part of your line and also is it possible to make it expandable if I have a large record to deal with?
You need to defined (or identify) a control logic that matches your grep output.
Derived from what you gave I assume the following:
the heading is a constant text that is intrinsic to your formatting
(Not to be deduced from input)
the first column is an ordinal number starting with one
the records from the input are identified by a string of all digits
Then the following awk script will do the formatting:
BEGIN {
# initialize ordinal
ordinal=1;
# print heading
printf "%-3s %5s %4s\n", "#", "Name", "LD #"
}
# match trigger line for output
/^[0-9]+$/ { printf "%3d %5s %4s\n", ordinal++, label, $1;
# cleanou label - not necessary for single data item case
# we are done with this input line
next;
}
# collect data item
{
label=$1;
# we are done with this input line
next;
}
If you want to include more record items (leading to more columns) you might check whether the preceeding column values have been encountered.
Or even just use a counter for indicating at what column you are within your record.
Then you could use e.g.:
BEGIN {
# initialize ordinal
ordinal=1;
column=0;
# print heading
printf "%-3s %5s %4s\n", "#", "Name", "LD #"
}
# match trigger line for output
/^[0-9]+$/ { printf "%3d (%d)", ordinal++, column;
for (i=0; i < column; i++) {
printf " %s", data[i];
data[i] = "";
}
printf "\n";
# we are done with this input line
column=0;
next;
}
# collect data item
{
data[column++]=$1;
if (length($1) > max[column]) {
max[column]=length($1);
}
# we are done with this input line
next;
}
END {
for (i=0; i< length(max); i++) {
printf "Col %d: %d\n", i, max[i];
}
}
I also included a way of determining the size of the columns (character count).

Resources