turn on suggestions

Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.

Showing results for

Find a Community

Topic Options

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

01-19-2017 12:26 AM - edited 01-19-2017 12:26 AM

Hi,

I have a 3x3 character array which I want to populate with random occurences of the characters A, B or C. To do this, I created a parrallel 3x3 nuemeric array and withing each (i,j) element of that array I want to randomly generate a number form the uniform distributiom. If that number if within the first third, then the corresponding element of the character array is A etc.

Here is the code:

```
proc iml;
schedule = j(3,3," ");
random_numbers=j(3,3,.);
print schedule;
print random_numbers;
do i = 1 to 3;
do j = 1 to 3;
call randgen(random_numbers[i,j],"uniform");
if random_numbers[i,j] <= 1/3 then schedule[i,j] = "A";
if 1/3 < random_numbers[i,j] <= 2/3 then schedule[i,j] = "B";
if 2/3 <random_numbers[i,j] <= 1 then schedule[i,j] = "C";
end;
end;
print schedule;
quit;
```

But as a result all of the character array's elements are C...

Thanks!

Accepted Solutions

Solution

01-19-2017
08:05 PM

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Posted in reply to ilikesas

01-19-2017 04:51 AM

Obviously there are 1000 ways to do this, but using your own solution as a starting point, you could simplify by vectorizing your creation of random_numbers setting your loop up backwards as this

```
proc iml;
schedule = j(3,3," ");
random_numbers=j(3,3,.);
call randgen(random_numbers,"uniform");
do i = 1 to 3;
do j = 1 to 3;
if random_numbers[i,j] <= 1 then schedule[i,j] = "A";
if random_numbers[i,j] <= 2/3 then schedule[i,j] = "B";
if random_numbers[i,j] <= 1/3 then schedule[i,j] = "C";
end;
end;
print schedule;
quit;
```

But I agree with @RW9, doing this in a data step is much simpler

All Replies

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Posted in reply to ilikesas

01-19-2017 04:24 AM

Well, looking at your if statements:

```
if random_numbers[i,j] <= 1/3 then schedule[i,j] = "A";
if 1/3 < random_numbers[i,j] <= 2/3 then schedule[i,j] = "B";
if 2/3 <random_numbers[i,j] <= 1 then schedule[i,j] = "C";
```

Each one of these is evaluated each time - as there is no else statement - and something which is <= 1/3 or 0.33, will also be <= 1. hence if they do fulfil the first criteria it makes no difference as the third criteria will also be true and hence C overwrites it.

Do note, it would be simpler to do this in a datastep:

data want; call streaminit(4563456); array schedule{3} $1.; array n{3} 8.; do i = 1 to 3; do j=1 to 3; n{j}=ceil(10 * rand("Uniform")); if 1 <= n{j} < 4 then schedule{j}="A"; else if 4 <= n{j} < 8 then schedule{j}="B"; else schedule{j}="C"; end; output; end; run;

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

01-19-2017 08:14 PM

Thanks RW9 for the code. Although the original question is about IML, data step is also very useful, especially that this question is actually a subquestion of my post: https://communities.sas.com/t5/Base-SAS-Programming/creating-worker-schedules/m-p/325852#M72509 where I want to create a random work schedule for employees given constraints. The first step that I needed is to generate a random matrix/array, and then optimize it according to the constraints

Solution

01-19-2017
08:05 PM

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Posted in reply to ilikesas

01-19-2017 04:51 AM

Obviously there are 1000 ways to do this, but using your own solution as a starting point, you could simplify by vectorizing your creation of random_numbers setting your loop up backwards as this

```
proc iml;
schedule = j(3,3," ");
random_numbers=j(3,3,.);
call randgen(random_numbers,"uniform");
do i = 1 to 3;
do j = 1 to 3;
if random_numbers[i,j] <= 1 then schedule[i,j] = "A";
if random_numbers[i,j] <= 2/3 then schedule[i,j] = "B";
if random_numbers[i,j] <= 1/3 then schedule[i,j] = "C";
end;
end;
print schedule;
quit;
```

But I agree with @RW9, doing this in a data step is much simpler

- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Posted in reply to ilikesas

01-19-2017 05:28 AM - edited 01-19-2017 05:32 AM

There are several problems with your code.

The RANDGEN call should be used to completely fill a matrix with random numbers in one go. So instead of attempting to write the numbers one at a time, you should write :

`call randgen(random_numbers,"uniform");`

somewhere before the two do loops. Also syntax of the form

`if 2/3 < x <= 1 then . . .`

is not operating the same wasy that it might in a data step. IML first evaluates 2/3 < x as either true(1) or false(0), and since both alternatives are less than or equal to 1, the whole statement is always true.

In IML there very often other more efficient ways of getting what you want without having to write loops. For example the SAMPLE function will give you what you want.

```
call randseed(123);
x = sample('A':'C', 9, 'Replace', {1 1 1}/3);
y = shape(x,3,3);
print y;
```