SAS: Stuck in a loop even with changes made? Any pointers? - loops

This is the code that I am using, I have tried changing a few things around but I think I am getting stuck in an endless loop.
DATA songs;
INFILE datalines;
INPUT City $ 1-15 Age domk wj hwow simbh kt aomm libm tr filp ttr;
ARRAY song (10) domk wj hwow simbh kt aomm libm tr filp ttr;
DO i = 1 TO 10;
IF song(i) = 9 THEN song(i) = .;
END;
datalines;
Albany 54 4 3 5 9 9 2 1 4 4 9
Richmond 33 5 2 4 3 9 2 9 3 3 3
Oakland 27 1 3 2 9 9 9 3 4 2 3
Richmond 41 4 3 5 5 5 2 9 4 5 5
Berkeley 18 3 4 9 1 4 9 3 9 3 2
;
PROC PRINT DATA = songs;
TITLE 'WBRK Song Survey';
RUN;
Can you point out what is wrong here? I have already tried changing the DO loop by adding an incremental i.
DO i = 1 TO 10;
IF song(i) = 9 THEN song(i) = .;
i+1;
END;
but the result is the same. I am new to SAS although not new to programming. I am wondering if I am making a syntax error here. Either way, any help is appreciated.

As I suspected, there was an issue with your import statement, at least for me. The following code worked for me:
DATA songs;
INFILE datalines;
informat city $20.;
INPUT City $ Age domk wj hwow simbh kt aomm libm tr filp ttr;
ARRAY song (10) domk wj hwow simbh kt aomm libm tr filp ttr;
DO i = 1 TO 10;
IF song(i) = 9 THEN song(i) = .;
END;
datalines;
Albany 54 4 3 5 9 9 2 1 4 4 9
Richmond 33 5 2 4 3 9 2 9 3 3 3
Oakland 27 1 3 2 9 9 9 3 4 2 3
Richmond 41 4 3 5 5 5 2 9 4 5 5
Berkeley 18 3 4 9 1 4 9 3 9 3 2
;
PROC PRINT DATA = songs;
TITLE 'WBRK Song Survey';
RUN;

Related

Problems with setting array elements in Forth

I am writing code in Forth that should create a 12x12 array of random numbers from 1 to 8.
create big_array 144 allocate drop
: reset_array big_array 144 0 fill ;
reset_array
variable rnd here rnd !
: random rnd # 31421 * 6927 + dup rnd ! ;
: choose random um* nip ;
: random_fill 144 1 do 8 choose big_array i + c! loop ;
random_fill
: Array_# 12 * + big_array swap + c# ;
: show_small_array cr 12 0 do 12 0 do i j Array_# 5 u.r loop cr loop ;
show_small_array
However, I notice that elements 128 to 131 of my array are always much larger than expected:
0 4 0 4 2 6 0 5 2 5 7 3
6 3 7 3 7 7 3 1 5 0 6 1
0 3 3 0 3 1 0 7 2 0 4 5
3 7 6 6 2 1 0 2 3 4 2 7
4 7 1 5 3 5 7 2 3 5 3 6
3 0 6 4 1 3 3 2 5 4 4 7
3 2 1 4 3 4 3 7 2 6 5 5
2 4 4 3 4 5 4 4 6 5 6 0
2 5 2 7 3 1 5 0 1 4 6 7
2 0 3 3 0 7 3 6 4 1 3 6
0 1 1 6 0 3 0 2 169 112 41 70
7 2 3 1 2 2 7 6 0 5 1 2
Moreover, when I try to change the value of these elements individually, this causes the other three elements to change value. For example, if I code:
9 choose big_array 128 + c!
then the array will become:
0 4 0 4 2 6 0 5 2 5 7 3
6 3 7 3 7 7 3 1 5 0 6 1
0 3 3 0 3 1 0 7 2 0 4 5
3 7 6 6 2 1 0 2 3 4 2 7
4 7 1 5 3 5 7 2 3 5 3 6
3 0 6 4 1 3 3 2 5 4 4 7
3 2 1 4 3 4 3 7 2 6 5 5
2 4 4 3 4 5 4 4 6 5 6 0
2 5 2 7 3 1 5 0 1 4 6 7
2 0 3 3 0 7 3 6 4 1 3 6
0 1 1 6 0 3 0 2 2 12 194 69
7 2 3 1 2 2 7 6 0 5 1 2
Do you have any idea why these specific elements are always impacted and if there is a way to prevent this?
Better readability and less error prone: 144 allocate ⇨ 144 chars allocate
A mistake: create big_array 144 allocate drop ⇨ create big_array 144 chars allot
A mistake: random um* nip ⇨ random swap mod
A mistake: 144 1 do ⇨ 144 0 do
An excessive operation: big_array swap + ⇨ big_array +
And add the stack comments, please. Especially, when you ask for help.
Do you have any idea why these specific elements are always impacted and if there is a way to prevent this?
Since you try to use memory in the dictionary space without reserving it. This memory is used by the Forth system.

J: Coordinates with specific value

Let's say we have array
0 1 2 3 4 5 8 7 8 9
There are two indexes that have value 8:
(i.10) ([#~8={) 0 1 2 3 4 5 8 7 8 9
6 8
Is there any shorter way to get this result? May be some built-in verb.
But more important. What about higher dimensions?
Let's say we have matrix 5x4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
I want to find out what are coordinates with value 6.
I want to get result such (there are three coordinates):
4 1
3 2
2 3
It's pretty basic task and I think it should exist some simple solution.
The same in three dimensions?
Thank you
Using Sparse array functionality ($.) provides a very fast and lean solution that also works for multiple dimensions.
]a=: 5 ]\ 1 + i. 8
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
6 = a
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 1 0 0
4 $. $. 6 = a
1 4
2 3
3 2
Tacitly:
getCoords=: 4 $. $.
getCoords 6 = a ,: a
0 1 4
0 2 3
0 3 2
1 1 4
1 2 3
1 3 2
Verb indices I. almost does the job.
When you have a simple list, I.'s use is straightforward:
I. 8 = 0 1 2 3 4 5 8 7 8 9
6 8
For higher order matrices you can pair it with antibase #: to get the coordinates in base $ matrix. Eg:
]a =: 4 5 $ 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
I. 6 = ,a
9 13 17
($a) #: 9 13 17
1 4
2 3
3 2
Similarly, for any number of dimensions: flatten (,), compare (=), get indices (I.) and convert coordinates (($a)&#:):
]coords =: ($a) #: I. 5 = , a =: ? 5 6 7 $ 10
0 0 2
0 2 1
0 2 3
...
(<"1 coords) { a
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
By the way, you can write I. x = y as x (I.#:=) y for extra performance. It is special code for
indices where x f y

How to do print formatting in Python with chunks of strings?

I'm having some trouble with formatting the pyramid. I've tried to use format when printing from the loop but that didn't seem to work and just breaks the program. What would be different ways to format the output. The only trouble that I am having is when I am printing 10 and up when there's double digits. What would be the best approach formatting the printing output? I've tried variety of ways but couldn't make formatting work within the loop from documentation
https://docs.python.org/3.5/library/string.html#formatstrings
Here is the script:
userinput = int(input("Enter the number of lines: " )) # User input of the total number of lines
userinput = userinput + 1 # adding a value of 1 additionally with the user input to make numbers even
for i in range(1, userinput): # Loop through lines from 1 to userinput
for j in range(userinput - i): # printing spaces, 1 at a time from j = 1 to j = userinput - i
print(" ", end = " ")
for j in range(i, 0, -1): # printing number decreasing from the line number j to 1
print(j, end = " ")
for j in range(2,i + 1): # Printing number increasing from 2 to line number j
print(j, end = " ")
print()
j += 1
The output when its less than 10
Enter the number of lines: 9
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
The output when it's 15 or more:
Enter the number of lines: 15
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11
12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12
13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13
14 13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
When I have reserved an extra space for 10 and up, here is what my outout looks like: (The dots were used to distinguish from empty space, all I did was added a " " quotes in the beginning of the print.
Enter the number of lines: 12
. . . . . . . . . . . . 1
. . . . . . . . . . . 2 1 2
. . . . . . . . . . 3 2 1 2 3
. . . . . . . . . 4 3 2 1 2 3 4
. . . . . . . . 5 4 3 2 1 2 3 4 5
. . . . . . . 6 5 4 3 2 1 2 3 4 5 6
. . . . . . 7 6 5 4 3 2 1 2 3 4 5 6 7
. . . . . 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
. . . . 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
. . . 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
. . 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11
. 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12
Here is what I've tried changing by adding aditional space
for j in range(userinput - i): # printing spaces, 1 at a time from j = 1 to j = userinput - i
print(".", end = " ")
for j in range(i, 0, -1): # printing number decreasing from the line number j to 1
print(" ", j, end = "")
for j in range(2,i + 1): # Printing number increasing from 2 to line number j
print(" ", j, end = "")
for j in range(userinput - i): # printing spaces, 1 at a time from j = 1 to j = userinput - i
print(" ", end = " ")
Here is the ideal output of what I am trying to accomplish:
1
2 1 2
3 2 1 2 3
4 3 2 1 2 3 4
5 4 3 2 1 2 3 4 5
6 5 4 3 2 1 2 3 4 5 6
7 6 5 4 3 2 1 2 3 4 5 6 7
8 7 6 5 4 3 2 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9
10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10
11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11
12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12
13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13
14 13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Thank you!
The things to consider for this problem are
The length of the largest number.
The length of the current number being printed.
The difference in lengths.
In order to correctly space everything, you're going to need to print extra
spaces after the numbers with less digits (to compensate for the extra digits in the larger number).
For example, if you have a row that contains the number 10, in order to correctly space the other smaller numbers, you're going to need to use extra spaces to compensate for that second digit in the number 10.
This solution works for me.
userinput = int(input("Enter the number of lines: " ))
userinput = userinput + 1
# Here, you can see I am storing the length of the largest number
input_length = len(str(userinput))
for i in range(1, userinput):
# First the row is positioned as needed with the correct number of spaces
spaces = " " * input_length
for j in range(userinput - i):
print(spaces, end = " ")
for j in range(i, 0, -1):
# Now, the current numbers length is compared to the
# largest number's length, and the appropriate number
# of spaces are appended after the number.
spaces = " " * (input_length + 1 - len(str(j)))
print(j, end = spaces)
for j in range(2,i + 1):
# The same is done here as in the previous loop.
spaces = " " * (input_length + 1 - len(str(j)))
print(j, end = spaces)
print()
j += 1
Take a look at
https://stackoverflow.com/a/13077777/6510412
I think this might be what you're looking for. I hope it helps.

SAS: Non-sequential do loop within a data step

I would like to be able to execute a do loop for a non-sequential set of values. The way I have written this code runs a new data step for each value - so therefore the end product is a data table with a column added for the final value of the do loop only. What I want is for the the values in the varlst to loop through the if/then statements - thereby adding multiple columns to the table - without executing a new data step each time (which only results in adding one final column to the table).
INPUT DATA
DATA have;
INPUT id order Q3 Q5 Q6 Q50 Q75 Q102;
DATALINES;
1 1 2 0 7 2 2 0
1 2 3 0 5 5 3 0
3 1 6 1 7 2 7 1
3 2 6 0 7 5 7 0
6 1 3 1 4 7 7 2
6 2 5 2 7 7 7 1
7 1 3 5 6 5 3 0
7 2 4 1 7 5 2 1
9 1 4 1 6 5 6 1
9 2 1 3 5 7 5 0
;
run;
/********/
%macro test;
%let varlst=2 3 5 6 50 75 102 /*more values*/;
%do i=1 %to %sysfunc(countw(&varlst));
%let value=%scan(&varlst,&i);
data want;
set have;
by id order;
if Q&value ne lag(Q&value) and not first.id then do;
Q&value.Equal = 0;
end;
if Q&value=lag(Q&value) and not first.id then do;
Q&value.Equal = 1;
end;
%end;
run;
%mend;
%test;
/**********/
OUTPUT
id order Q3 Q5 Q6 Q50 Q75 Q102 Q102Equal
1 1 2 0 7 2 2 0 .
1 2 3 0 5 5 3 0 1
3 1 6 1 7 2 7 1 .
3 2 6 0 7 5 7 0 0
6 1 3 1 4 7 7 2 .
6 2 5 2 7 7 7 1 0
7 1 3 5 6 5 3 0 .
7 2 4 1 7 5 2 1 0
9 1 4 1 6 5 6 1 .
9 2 1 3 5 7 5 0 0
Why don't you try using PROC COMPARE?
data have ;
input id order Q3 Q5 Q6 Q50 Q75 Q102;
cards;
1 1 2 0 7 2 2 0 .
1 2 3 0 5 5 3 0 1
3 1 6 1 7 2 7 1 .
3 2 6 0 7 5 7 0 0
6 1 3 1 4 7 7 2 .
6 2 5 2 7 7 7 1 0
7 1 3 5 6 5 3 0 .
7 2 4 1 7 5 2 1 0
9 1 4 1 6 5 6 1 .
9 2 1 3 5 7 5 0 0
;;;;
proc compare
data=have(where=(order=1))
compare=have(where=(order=2))
outdiff out=want
;
id id ;
var q: ;
run;

Reshape acast() remove missing values

I have this dataframe:
df <- data.frame(subject = c(rep("one", 20), c(rep("two", 20))),
score1 = sample(1:3, 40, replace=T),
score2 = sample(1:6, 40, replace=T),
score3 = sample(1:3, 40, replace=T),
score4 = sample(1:4, 40, replace=T))
subject score1 score2 score3 score4
1 one 2 4 2 2
2 one 3 3 1 2
3 one 1 2 1 3
4 one 3 4 1 2
5 one 1 2 2 3
6 one 1 5 2 4
7 one 2 5 3 2
8 one 1 5 1 3
9 one 3 5 2 2
10 one 2 3 3 4
11 one 3 2 1 3
12 one 2 5 2 1
13 one 2 4 1 4
14 one 2 2 1 3
15 one 1 3 1 4
16 one 1 6 1 3
17 one 3 4 2 2
18 one 3 2 1 3
19 one 2 5 3 1
20 one 3 6 2 1
21 two 1 6 3 4
22 two 1 2 1 2
23 two 3 2 1 2
24 two 1 2 2 1
25 two 2 3 1 3
26 two 1 5 3 3
27 two 2 4 1 4
28 two 2 6 2 4
29 two 1 6 2 2
30 two 1 5 1 4
31 two 2 1 2 4
32 two 3 6 1 1
33 two 1 1 3 1
34 two 2 4 2 3
35 two 2 1 3 2
36 two 2 3 1 3
37 two 1 2 3 4
38 two 3 5 2 2
39 two 2 1 3 4
40 two 2 1 1 3
Note that the scores have different ranges of values. Score 1 ranges from 1-3, score 2 from -6, score 3 from 1-3, score 4 from 1-4
I'm trying to reshape data like this:
library(reshape2)
dfMelt <- melt(df, id.vars="subject")
acast(dfMelt, subject ~ value ~ variable)
Aggregation function missing: defaulting to length
, , score1
1 2 3 4 5 6
one 6 7 7 0 0 0
two 8 9 3 0 0 0
, , score2
1 2 3 4 5 6
one 0 5 3 4 6 2
two 5 4 2 2 3 4
, , score3
1 2 3 4 5 6
one 10 7 3 0 0 0
two 8 6 6 0 0 0
, , score4
1 2 3 4 5 6
one 3 6 7 4 0 0
two 3 5 5 7 0 0
Note that the output array includes scores as "0" if they are missing. Is there any way to stop these missing scores being outputted by acast?
In this case, you might do better sticking to base R's table feature. I'm not sure that you can have an irregular array like you are looking for.
For example:
> lapply(df[-1], function(x) table(df[[1]], x))
$score1
x
1 2 3
one 9 6 5
two 11 4 5
$score2
x
1 2 3 4 5 6
one 2 5 4 3 3 3
two 4 2 2 3 4 5
$score3
x
1 2 3
one 9 5 6
two 4 11 5
$score4
x
1 2 3 4
one 4 4 8 4
two 2 6 5 7
Or, using your "long" data:
with(dfMelt, by(dfMelt, variable,
FUN = function(x) table(x[["subject"]], x[["value"]])))
Since each "score" subset is going to have a different shape, you will not be able to preserve the array structure. One option is to use lists of two-dim arrays or data.frames. eg:
# your original acast call
res <- acast(dfMelt, subject ~ value ~ variable)
# remove any columns that are all zero
apply(res, 3, function(x) x[, apply(x, 2, sum)!=0] )
Which gives:
$score1
1 2 3
one 7 8 5
two 6 8 6
$score2
1 2 3 4 5 6
one 4 2 6 4 1 3
two 2 5 3 4 3 3
$score3
1 2 3
one 5 10 5
two 5 11 4
$score4
1 2 3 4
one 5 4 4 7
two 4 6 6 4

Resources