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
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.