Good timing. I was just working on some related code last week, doing some execution time reduction, and wanted to add to what was already said. Going from looping through rows and replacing with the matrix method (which Vince references above) in one script improved the execution time by around 20x-25x. On another script, changing from get rows where() to the matrix method improved it by almost 2x. While I was doing it, I wrote the code below so I could share or reference it later, and it seems reasonable to post it here. Note that using the Loc() with lists from character columns uses different syntax and you can't build the more complex logic like you can with all-matrix variables that you get from numeric columns. So I used a technique shared by Craige Hales (linked here) which uses associative arrays and the intersection function. Hope you find it useful.
Note this script adds new character columns and puts comments in there because I wrote it for demo purposes. Given the original question here, you could just replace the assignment lines such as c3[rowsMale] = "Male";
with column(dtData, "age")
[rowsMale] = .;
or similar.
names default to here(1);
dtData = Open( "$SAMPLE_DATA/Big Class.jmp" );
mAge = column(dtData, "age") << get values(); //a matrix
mHeight = column(dtData, "height") << get values(); //a matrix
lSex = column(dtData, "sex") << get values(); //a list
lName = column(dtData, "name") << get values(); //a list
//note the results of the Loc() function will return a matrix that corresponds to the rows that match the criteria
rowsTall = Loc(mHeight > 63); // simple comparison in Loc() function.
c1 = new column("Comments 1","Character");
c1[rowsTall] = "Tall";
rowsTallAndOver13 = Loc( (mHeight > 63) & (mAge > 13)); // can build more complex logic in Loc() function
c2 = new column("Comments 2","Character");
c2[rowsTallAndOver13] = "Tall and 14 or older";
rowsMale = Loc(lSex, "M"); // note syntax is slightly different with Loc() for lists when dealing with character columns.
//rowsMaleNamedRobert = Loc((lSex, "M"), (lName, "Robert")); // Cannot build complex comparisons with Character columns, see below on the intersection and AA's
c3 = new column("Comments 3","Character");
c3[rowsMale] = "Male";
//rowsTall17M = Loc((mHeight > 63) & (mAge == 17) & (lSex == "M") ); // attempt at building complex logic but the syntax is not correct, and can't have lists in a compound comparison
//rowsTall17M = Loc((mHeight > 63) & (mAge == 17) & (lSex, "M") ); // attempt at building complex logic. Syntax is closer to correct, but can't have lists in a compound comparison
//Build numeric comparisons all at once, then do character comparisons one at a time. Only 1 character comparison shown here.
rowsTall17 = Loc((mHeight > 63) & (mAge == 17) ); // Build the numeric parts separate from char column(s)
rowsMale = Loc(lSex, "M"); // Character columns separately. Seems you need one variable per character column, and then intersect them all later.
//insert above into associative arrays, use intersection function to get the matrices intersection
aa_rowsTall17 = associative array(rowsTall17);
aa_rowsMale = associative array(rowsMale);
intersection = aa_rowsMale; // copy of aa_rowsMale, as intersection gets modified in next line
intersection<<intersect(aa_rowsTall17); // aa_rowsMale gets modified here with the resulting intersection output, but is still an associative array
rowsTall17M = matrix(intersection << get keys); // output here is the intersection of the original two matrices prior to putting them in the AA
c4 = new column("Comments 4","Character");
c4[rowsTall17M] = "Tall, Male, 17";
//achieves the same result as what you see above in "comments 4" column but I have found variations of this to be very slow in large data tables.
gRowsTallM17 = dtData << get rows where( (:age == 17) & (:sex == "M") & (:height > 63) );
c5 = new column("Comments 5 - using get rows where","Character");
c5[gRowsTallM17] = "Tall, Male, 17";