It sounds like the OP is requesting a CYCLIC shift of each row. A cyclic shift can be accomplished by using the MOD function as follows. Let p=NCOL(A).
For example, the OP's example, has p=4, so you can use the following to create the shift for the i_th row:
idx = 1:p;
shiftIdx = p - v[i] + idx; * shift to the right ;
newIdx = mod(shiftIdx-1, p) + 1; * wrap around ;
Here's how it looks being applied to each row:
/* cyclic rotation of row elements */
proc iml;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
p = ncol(A);
idx = 1:p;
/* Demonstrate using the MOD function to shift the indices */
/*
do i = 1 to nrow(v);
shiftIdx = p - v[i] + idx; * shift to the right ;
newIdx = mod(shiftIdx-1, p) + 1; * wrap around ;
print (v[i])[L='shift'], newIdx[c=('c1':'c4')];
end;
*/
/* Solve the real problem: use MOD function to apply a cyclic shift of row A[i,]
by the number of columns in v[i] */
ShiftA = j(nrow(A), p, .); /* allocate */
do i = 1 to nrow(A);
shiftIdx = p - v[i] + idx; /* shift to the right */
newIdx = mod(shiftIdx-1, p) + 1; /* wrap around */
*print i adjIdx;
ShiftA[i,] = A[i, newIdx];
end;
print ShiftA;
How about something like this for start:
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
do i = 1 to r*(max(v) <= c);
x = a[i,(c-v[i])+1:c] || a[i,1:(c-v[i])];
B = B // x;
end;
print B;
quit;
?
Bart
Alternative with the insert() function:
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
B=j(1,c,.);
do i = 1 to r*(max(v) <= c);
x = a[i,(c-v[i])+1:c] || a[i,1:(c-v[i])];
B = insert(B, x, i, 0);
end;
B = B[1:r,];
print B;
quit;
[EDIT:]
Or with the shape() function:
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
do i = 1 to r*(max(v) <= c);
B = B || a[i,(c-v[i])+1:c] || a[i,1:(c-v[i])];
end;
B = shape(B,r,c);
print B;
quit;
[EDIT 2:]
Or with just proper sub-scripting:
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
B=j(r,c,.);
do i = 1 to r*(max(v) <= c);
B[i,1:c] = a[i,(c-v[i])+1:c] || a[i,1:(c-v[i])];
end;
print B;
quit;
Bart
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
idx=c-v+1;
AA=A||A;
want=j(r,c,.);
do i=1 to r;
want[i,]=AA[i,idx[i]:idx[i]+c-1];
end;
print want;
quit;
@Ksharp thanks for reminding about the AA=A||A trick!
Now it can be done "loop-less" with "one" expression (see below):
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
/* step by step */
AA=A||A;
x=1:2*c;
y1 = repeat(x, r, 1);
y2 = y1 + (c-v);
y3 = y2[,1:c];
y4 = y3 + t(0:r-1)*c*2;
print x,y1,y2,y3,AA,y4;
want = shape(AA[y4],r);
print want;
/* one expression - in Klingons language */
want2 = shape((A||A)[(repeat(1:2*c,r,1)+(c-v))[,1:c]+t(0:r-1)*c*2],r);
print want2;
quit;
Output:
All the best
Bart
@Ksharp , even shorter:
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
/* step by step */
AA=A||A; /* trick by @Ksharp */
x=1:2*r*c;
y1=shape(x,r);
y2=(y1+(c-v))[,1:c];
print x,y1,y2;
want3 = shape(AA[y2],r);
print want3;
/* one expression - in less Klingons language */
want4 = shape((A||A)[(shape(1:2*r*c,r)+(c-v))[,1:c]],r);
print want4;
quit;
Output:
Bart
Can't help myself 😛 😛
PROC IML;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
r=nrow(A);
c=ncol(A);
/* step by step */
AA=A||A; /* trick by @Ksharp */
v1 = SHAPE(1:2*r*c,r);
v2 = REPEAT(c-v, 1, 2*c);
v3 = (v2+v1)[,1:c];
want5 = shape(AA[v3],r);
print AA,v,v1,v2,v3,want5;
/* one expression - in yet another Klingons language */
want6 = shape((A||A)[(REPEAT(c-v,1,2*c)+SHAPE(1:2*r*c,r))[,1:c]],r);
print want6;
quit;
Output:
Bart
It sounds like the OP is requesting a CYCLIC shift of each row. A cyclic shift can be accomplished by using the MOD function as follows. Let p=NCOL(A).
For example, the OP's example, has p=4, so you can use the following to create the shift for the i_th row:
idx = 1:p;
shiftIdx = p - v[i] + idx; * shift to the right ;
newIdx = mod(shiftIdx-1, p) + 1; * wrap around ;
Here's how it looks being applied to each row:
/* cyclic rotation of row elements */
proc iml;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
p = ncol(A);
idx = 1:p;
/* Demonstrate using the MOD function to shift the indices */
/*
do i = 1 to nrow(v);
shiftIdx = p - v[i] + idx; * shift to the right ;
newIdx = mod(shiftIdx-1, p) + 1; * wrap around ;
print (v[i])[L='shift'], newIdx[c=('c1':'c4')];
end;
*/
/* Solve the real problem: use MOD function to apply a cyclic shift of row A[i,]
by the number of columns in v[i] */
ShiftA = j(nrow(A), p, .); /* allocate */
do i = 1 to nrow(A);
shiftIdx = p - v[i] + idx; /* shift to the right */
newIdx = mod(shiftIdx-1, p) + 1; /* wrap around */
*print i adjIdx;
ShiftA[i,] = A[i, newIdx];
end;
print ShiftA;
@Rick_SAS , vectorised version:
proc iml;
A = {1 2 3 4,
5 6 7 8,
9 0 1 2} ;
v = {1,3,2} ;
c = ncol(A);
r = nrow(A);
idx = repeat(1:c,r);
row = t(0:r-1)*c;
print idx, row;
/* vectorised */
shiftIdx = c - v + idx;
print shiftIdx;
newIdx = mod(shiftIdx-1, c) + 1;
want = shape(A[newIdx + row], r);
print want;
/* one expression */
want2 = shape(A[(mod((c - v + idx)-1, c) + 1) + t(0:r-1)*c], r);
print want2;
quit;
Output:
Bart
Yes, and you can use the COL function to simplify the creation of the matrix for which M[i,j]=j.
idx = col(A);
The rest of your program uses a formula to convert between subscripts and indices. The NDX2SUB and SUB2NDX functions incorporate this functionality and are sometimes useful in situations like this.
@yabwon BTW, a cyclic shift is one kind of a permutation. If the OP were interested in more general permutations, they could use the ideas in the article Permute elements within each row of a matrix - The DO Loop, which shows how to apply a random permutation to each row of a matrix in a vectorized manner. Using the program in that article, I suspect you can write a new function called CyclicPerm and replace the call to RANPERM.
Thank you every body who respond. I like MOD function. very elgelant. Also like vectorize program. Need to make timeing to see if makes differnce. My matrix have 15 column and 10.000 row.
@yabwon Are you learning IML? I see you answer other forums like macro and dataq step, but not often IML.
If there's a question I try to help. I don't distinguish "forums". They all the same SAS programming questions to me.
Bart
For 10,000 rows, both the loop and the vectorized methods have approximately the same run time. On my laptop, the looping method takes 0.007 s and the vectorized method takes 0.004 s. On the one hand, the vectorized method is almost twice as fast. On the other hand, you save only 0.003 s by using it.
If your matrix has 100,000 or more rows, then the performance comparison is:
Here is the program I use, so you can run your own experiments:
/* Rick's original version: Loop over rows and apply each shift */
%let NCOL = 15;
%let NROW = 1000000;
proc iml;
nr = &NROW;
p = &NCOL;
A = shape(1:(nr*p), nr, p);
call randseed(1234);
v = randfun(nr, "Uniform", 0, p-1); /* create shift vector */
nRep = 10; /* repeat the experiment nRep times and report average */
/* use MOD function to apply a cyclic shift of row A[i,]
by the number of columns in v[i] */
t0 = time();
do rep = 1 to nRep;
idx = 1:p;
ShiftA = j(nr, p, .); /* allocate */
do i = 1 to nr;
shiftIdx = p - v[i] + idx; /* shift to the right */
newIdx = mod(shiftIdx-1, p) + 1; /* wrap around */
ShiftA[i,] = A[i, newIdx];
end;
end;
tLoop = (time() - t0) / nRep;
/* yabwon's vectorized version */
t0 = time();
do rep = 1 to nRep;
c = ncol(A);
r = nrow(A);
idx = repeat(1:c,r);
row = t(0:r-1)*c;
shiftIdx = c - v + idx;
newIdx = mod(shiftIdx-1, c) + 1;
want = shape(A[newIdx + row], r);
end;
tVectorized = (time() - t0) / nRep;
print tLoop tVectorized;
print (max(abs(ShiftA - want)))[L="Diff between answers"];
April 27 – 30 | Gaylord Texan | Grapevine, Texas
Walk in ready to learn. Walk out ready to deliver. This is the data and AI conference you can't afford to miss.
Register now and lock in 2025 pricing—just $495!