Hi SAS community
I have data over time for ~200 participants. For each participant, I want to fit the same model, but I have a different set of initial conditions for the PROC NLIN routine (derived by looking at data for each paritcipant). How do I get this to work (code below)? Any help really appreciate. I keep get error like this:
NOTE: DER.a not initialized or missing. It will be computed automatically.
NOTE: DER.b not initialized or missing. It will be computed automatically.
NOTE: DER.c not initialized or missing. It will be computed automatically.
NOTE: DER.d not initialized or missing. It will be computed automatically.
WARNING: Zero observations could be evaluated.
NOTE: The data set WORK.PARAMS_OUT_4 has 0 observations and 9 variables.
data initial_params;
set in.initial_params;
nf+1;
call symputx("last_nf",nf);
run;
data participant_data;
set in.participant_data;
run;
%macro fit;
%do j=1 %to &last_nf.;
data _null_;
set initial_params;
where nf=&j;
call symputx("beta10",beta10);
call symputx("beta20",beta20);
call symputx("beta30",beta30);
call symputx("beta40".beta40);
call symputx("participant",subject);
run;
proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out_&j.;
where subject="&participant";
parameters a = &beta10. &beta20. = -3 c = &beta30. d = &beta40.;
model response = a*exp(time/b) + c*time + d;
run;
%end;
%mend;
%fit;
suggesting what I do wrong would be very helpful to me!
Hi @linlin87,
First of all, I see two syntax errors in your code which would prevent the PROC NLIN step from even producing the messages you show:
One situation in which the warning "Zero observations could be evaluated" would occur is that the WHERE condition subject="&participant" is not met for any observation of dataset PARTICIPANT_DATA. So, make sure that those character values match exactly.
But most importantly, I think you could both simplify your program and make it more robust if you used a single PROC NLIN step with a BY statement (by subject) instead of the macro, macro variables and the WHERE condition. Then you could name the dataset containing the subject-specific initial parameter values in the PDATA= option of the PARAMETERS statement. See the syntax and section "Assigning Starting Values from a SAS Data Set" in the PROC NLIN documentation.
Edit: Below is an example of what your PROC NLIN step could look like. Note the structure of the PDATA= dataset (which I named init_params).
/* Create test data for demonstration */
data temp;
input subject a b c d;
cards;
1 8 2 0.2 1
2 7 1 0.3 -1
;
proc transpose data=temp out=init_params(rename=(col1=estimate)) name=parameter;
by subject;
run;
data participant_data(drop=a--d);
set temp;
do time=1 to 5;
response=a*exp(time/b) + c*time + d + rannor(1);
output;
end;
run;
/* Use BY-group processing and the PDATA= option in PROC NLIN */
proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out;
by subject;
parameters / pdata=init_params;
model response = a*exp(time/b) + c*time + d;
run;
Hi @linlin87,
First of all, I see two syntax errors in your code which would prevent the PROC NLIN step from even producing the messages you show:
One situation in which the warning "Zero observations could be evaluated" would occur is that the WHERE condition subject="&participant" is not met for any observation of dataset PARTICIPANT_DATA. So, make sure that those character values match exactly.
But most importantly, I think you could both simplify your program and make it more robust if you used a single PROC NLIN step with a BY statement (by subject) instead of the macro, macro variables and the WHERE condition. Then you could name the dataset containing the subject-specific initial parameter values in the PDATA= option of the PARAMETERS statement. See the syntax and section "Assigning Starting Values from a SAS Data Set" in the PROC NLIN documentation.
Edit: Below is an example of what your PROC NLIN step could look like. Note the structure of the PDATA= dataset (which I named init_params).
/* Create test data for demonstration */
data temp;
input subject a b c d;
cards;
1 8 2 0.2 1
2 7 1 0.3 -1
;
proc transpose data=temp out=init_params(rename=(col1=estimate)) name=parameter;
by subject;
run;
data participant_data(drop=a--d);
set temp;
do time=1 to 5;
response=a*exp(time/b) + c*time + d + rannor(1);
output;
end;
run;
/* Use BY-group processing and the PDATA= option in PROC NLIN */
proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out;
by subject;
parameters / pdata=init_params;
model response = a*exp(time/b) + c*time + d;
run;
Thank you @FreelanceReinh !!
One more questions for you. Now I have this
proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out;
by subject;
parameters / pdata=init_params;
model response = a*exp(time/b) + c*time + d;
run;
but actually I need that a+d = value. So:
proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out;
by subject;
parameters / pdata=init_params;
bounds d+a = value;
model response = a*exp(time/b) + c*time + d;
run;
Note that value is different for each SUBJECT. How do I do?
You're welcome.
I think the BOUNDS statement is more suitable for constraints in the form of inequalities about the parameters. Given that all these numbers are non-integers, an exact equality such as d+a=value would be a risky requirement anyway. (Note that, e.g., 0.1+0.2 ne 0.3 in Windows SAS due to rounding errors in the binary system.) Also, the BOUNDS statement, unlike the PARAMETERS statement, does not allow for varying values coming from a dataset. There is a different procedure, PROC HPNLMOD, which offers a RESTRICT statement where you could specify your linear constraint. But I have never used PROC HPNLMOD before today, so let's stay with PROC NLIN.
In PROC NLIN it's probably better to implement the constraint d+a=value in the model equation. You have two options for that: Either replace d with value-a, or replace a with value-d. Let's follow the first approach, i.e., eliminate parameter d in the model equation and introduce value as a new variable (not parameter) in the input dataset participant_data.
Here is the example from my earlier post, with the new constraint implemented. Changes are highlighted in blue.
/* Create test data for demonstration */ data temp; input subject a b c value; /* parameter d has been eliminated by the constraint d+a=value */ cards; 1 8 2 0.2 9 2 7 1 0.3 6 ; proc transpose data=temp(drop=value) out=init_params(rename=(col1=estimate)) name=parameter; by subject; run; data participant_data(drop=a--c); set temp; do time=1 to 5; response=a*exp(time/b) + c*time + value - a + rannor(1); output; end; run; /* Use BY-group processing and the PDATA= option in PROC NLIN */ proc nlin data = participant_data g4 plots = fit maxiter = 50 outest = params_out; by subject; parameters / pdata=init_params; model response = a*exp(time/b) + c*time + value - a; run;
As you can see in dataset participant_data, variable value is a subject-dependent constant over time. Parameters a, b, c are estimated, starting with (unchanged) subject-specific initial values from dataset init_params, as before. For each subject, you could use the estimate for parameter a computed by PROC NLIN and the known value of variable value to compute d = value - a, if needed:
data est(drop=_:);
set params_out;
where _type_='FINAL';
run;
data values;
set participant_data(keep=subject value);
by subject;
if first.subject;
run;
data want;
merge values est;
by subject;
d=value-a;
run;
Obviously, the estimates will satisfy the constraint a+d = value, possibly up to tiny rounding differences like 1E-16 as in this example:
138 data _null_; 139 d=0.9-0.3; 140 if 0.3+d ne 0.9 then put 'Surprised?'; 141 delta=0.3+d-0.9; 142 put delta=; 143 run; Surprised? delta=1.110223E-16
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.
Find more tutorials on the SAS Users YouTube channel.