SAS Documentation
SASĀ® Solution for Stress Testing
Reference manual - version 08.2021
Loading...
Searching...
No Matches
irmc_riskagg_corr_mat_parser.sas
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 SAS Institute Inc. Cary, NC, USA
3 */
4
5/**
6 \file
7 \anchor irmc_riskagg_corr_mat_parser
8 \anchor irmc_riskagg_corr_mat_parser_key
9
10
11 \brief parse of the correlation matrices used for risk aggregation.
12
13
14 \param[in] ds_in_corr_matrix Unstructured correlation matrix table.
15 \param[in] working_libname Working LIBNAME. By default, it is set to work. This option is typically used to facilitate debugging.
16 \param[out] ds_out_matrix Structured correlation matrix table.
17
18
19 \details
20
21 Given a correlation matrix (<i>DS_IN_CORR_MATRIX</i>) specified in an unstructured form (specifying k elements),
22 this macro returns a structured correlation matrix (<i>DS_OUT_CORR_MATRIX</i>) containing n*n elements where k=n*(n-1)/2.
23
24
25 For example the following unstructured correlation matrix defined by k=10 elements
26
27 | Correlation Identifier | Correlation Measurement |
28 |-----------------------------|--------------------------|
29 | Corr(item1,item2) | num1 |
30 | Corr(item1,item3) | num2 |
31 | Corr(item1,item4) | num3 |
32 | Corr(item1,item5) | num4 |
33 | Corr(item2,item3) | num5 |
34 | Corr(item2,item4) | num6 |
35 | Corr(item2,item5) | num7 |
36 | Corr(item3,item4) | num8 |
37 | Corr(item3,item5) | num9 |
38 | Corr(item4,item5) | num10 |
39
40 is equivalent to the following 5*5 structured correlation matrix
41
42 | | item1 | item2 | item3 | item4 | item5 |
43 |-------|-------|-------|-------|-------|-------|
44 | item1 | 1 | num1 | num2 | num3 | num4 |
45 | item2 | num1 | 1 | num5 | num6 | num7 |
46 | item3 | num2 | num5 | 1 | num8 | num9 |
47 | item4 | num3 | num6 | num8 | 1 | num10 |
48 | item5 | num4 | num7 | num9 | num10 | 1 |
49
50
51
52
53
54
55 \ingroup RiskAggregation
56
57 \author SAS Institute INC.
58 \date 2020
59
60*/
61
62
63/* Sample call:
64%irmc_riskagg_corr_mat_parser( ds_in_corr_matrix =work.RISKAGG_HICOPULA_SUB1_CORR
65 , working_libname =work
66 , ds_out_matrix =matrix_final
67 );
68*/
69
70%macro irmc_riskagg_corr_mat_parser( ds_in_corr_matrix =
71 , working_libname =work
72 , ds_out_matrix =
73 );
74
75 /*create a bygroup map where we select the distinct values of REPORTING_DT,SCENARIO_ID,RISK_MEASURE,_TYPE_.
76 In the typical case where this macro is called from within a STRATUM based solution, the only only variable that defines the bygroup should be SCENARIO_ID*/
77 proc sql noprint;
78 create table &working_libname..bygroup_map as
79 select distinct REPORTING_DT,SCENARIO_ID,RISK_MEASURE,_TYPE_
80 from &ds_in_corr_matrix.;
81 quit;
82
83 /*Adding a numeric "index" to the bygroup map*/
84 data &working_libname..bygroup_map;
85 set &working_libname..bygroup_map end=last;
86 index=_N_;
87 if last then do;
88 call symputx("bygroup_cnt",index);
89 end;
90 run;
91
92
93 /*Enrich the original correlation matrix with the bygroup index.*/
94 data &working_libname..raw_matrix_enhanched;
95 set &ds_in_corr_matrix.;
96 if _N_=1 then do;
97 declare hash h(dataset:"&working_libname..bygroup_map");
98 h.defineKey("REPORTING_DT","SCENARIO_ID");
99 h.defineData("index");
100 h.defineDone();
101 call missing(index);
102 end;
103 rc=h.find();
104 run;
105
106
107 /*Loop over the bygroups and create the corresponding matrices.
108 The raw data are in a non canonical shape:
109 For example the following data k=10 data points
110 Corr(item1,item2)=num1
111 Corr(item1,item3)=num2
112 Corr(item1,item4)=num3
113 Corr(item1,item5)=num4
114 Corr(item2,item3)=num5
115 Corr(item2,item4)=num6
116 Corr(item2,item5)=num7
117 Corr(item3,item4)=num8
118 Corr(item3,item5)=num9
119 Corr(item4,item5)=num10
120 are equivalent to the following n*n matrix
121 where n=5 and k=n*(n-1)/2
122 item1 item2 item3 item4 item5
123 -----------------------------
124 item1 |1 num1 num2 num3 num4
125 item2 |num1 1 num5 num6 num7
126 item3 |num2 num5 1 num8 num9
127 item4 |num3 num6 num8 1 num10
128 item5 |num4 num7 num9 num10 1
129 */
130 %do i_bygroup=1 %to &bygroup_cnt.;
131 /*create two data set containing the distinct values of parm1 and parm2 respectively*/
132 proc sql noprint;
133 create table &working_libname..parm1_data as
134 select distinct _PARM1_ as _PARM_
135 from &working_libname..raw_matrix_enhanched(where=(index=&i_bygroup.))
136 order by _PARM1_;
137 create table &working_libname..parm2_data as
138 select distinct _PARM2_ as _PARM_
139 from &working_libname..raw_matrix_enhanched(where=(index=&i_bygroup.))
140 order by _PARM2_;
141 quit;
142
143 /*Create a unique data set containing the set of unique entries common to both parm1 and parm2*/
144 data &working_libname..parm_data;
145 merge &working_libname..parm1_data &working_libname..parm2_data;
146 by _PARM_;
147 run;
148
149 /*create macros storing the list of unique entries in both parm1 and parm2 as well as the total count of these elements.*/
150 proc sql noprint;
151 select _PARM_, count(_PARM_) into :parm_list separated by " ", :parm_cnt
152 from &working_libname..parm_data;
153 quit;
154
155
156 /*Checks the validity of _PARM1_ and _PARM2_ for use as a SAS variable name.*/
157 %do i_parm=1 %to &parm_cnt.;
158 %if %sysfunc(NVALID(%scan(&parm_list.,&i_parm.,%str( ))))=0 %then %do;
159 %put ERROR: when defining a correlation matrix, the values of _PARM1_ and _PARM2_ must be valid character string to be used as a SAS variable name.;
160 %put The attempted invalid string is: %scan(&parm_list.,&i_parm.,%str( ));
161 %abort cancel;
162 %end;
163 %end;
164
165 /*Create the canonical correlation matrix.
166 The way this is done is the following:
167 1) create an identity matrix
168 2a) lookup the off diagonal elements with a hash table.
169 2b) if the element is not found, repeat the search by swapping the keys
170 2c) if 2a and 2b are not able to fill up all the off-diagonal elements,
171 the user may have not specified enough correlation elements.*/
172 %let httpSuccess=1;
173 data %if &i_bygroup.=1 %then %do;
174 &ds_out_matrix.;
175 %end;
176 %else %if &i_bygroup.>1 %then %do;
177 &working_libname..matrix_&i_bygroup.;
178 %end;
179 length _PARM1_ $64. _PARM2_ $64. _NAME_ $64.;
180 rc=.;
181 if _N_=1 then do;
182 declare hash h(dataset: "&working_libname..raw_matrix_enhanched(where=(index=&i_bygroup.))");
183 h.defineKey("_PARM1_","_PARM2_");
184 h.defineData("_MEASUREMENT_");
185 h.defineDone();
186 call missing(_MEASUREMENT_);
187 end;
188 index=&i_bygroup.;
189 %do i_row=1 %to &parm_cnt.;
190 _NAME_="%scan(&parm_list.,&i_row.,%str( ))";
191 %do i_col=1 %to &parm_cnt.;
192 %if &i_row.=&i_col. %then %do;
193 %scan(&parm_list.,&i_col.,%str( ))=1;
194 %end;
195 %else %do;
196 _PARM1_="%scan(&parm_list.,&i_row.,%str( ))";
197 _PARM2_="%scan(&parm_list.,&i_col.,%str( ))";
198 %scan(&parm_list.,&i_col.,%str( ))=.;
199 rc=h.find();
200 if rc=0 then do;
201 %scan(&parm_list.,&i_col.,%str( ))=_MEASUREMENT_;
202 end;
203 else do;
204 _PARM1_="%scan(&parm_list.,&i_col.,%str( ))";
205 _PARM2_="%scan(&parm_list.,&i_row.,%str( ))";
206 rc=h.find();
207 if rc=0 then do;
208 %scan(&parm_list.,&i_col.,%str( ))=_MEASUREMENT_;
209 end;
210 if rc ^= 0 then do;
211 put 'ERROR: Not enough elements were specified to the define a correct correlation matrix.';
212 call symput("httpSuccess", 0);
213 end;
214 end;
215 %end;
216 %end;
217 output;
218 %end;
219 run;
220
221 %if(not &httpSuccess.) %then %do;
222 %abort cancel;
223 %end;
224
225
226 proc iml;
227 use %if &i_bygroup.=1 %then %do;
228 &ds_out_matrix.;
229 %end;
230 %else %if &i_bygroup.>1 %then %do;
231 &working_libname..matrix_&i_bygroup.;
232 %end;
233 read all var {&parm_list.} into corr_mat;
234
235 eigvalues = eigval(corr_mat);
236
237 min_eigenvalue=min(eigvalues);
238
239 if min_eigenvalue<0 then do;
240 call symputx ("positive_semi_def_flg","FALSE","L");
241 end;
242 else do;
243 call symputx ("positive_semi_def_flg","TRUE","L");
244 end;
245 quit;
246
247 %if &positive_semi_def_flg.=FALSE %then %do;
248 %put ERROR: The correlation matrix &ds_in_corr_matrix. is not positive semi-definite.;
249 %abort cancel;
250 %end;
251
252
253
254 /*Append the correlation matrix produced for each bygroup*/
255 %if &i_bygroup.>1 %then %do;
256 proc append base=&ds_out_matrix. data=&working_libname..matrix_&i_bygroup.;
257 run;
258 %end;
259
260 %end;
261
262
263 /*Populate the newly created correlation matrices with information relative to REPORTING_DT SCENARIO_ID RISK_MEASURE _TYPE_*/
264 data &ds_out_matrix.;
265 set &ds_out_matrix. &working_libname..bygroup_map(obs=0 keep=REPORTING_DT SCENARIO_ID RISK_MEASURE _TYPE_);
266 if _N_=1 then do;
267 declare hash h(dataset:"&working_libname..bygroup_map");
268 h.defineKey("index");
269 h.defineData("REPORTING_DT","SCENARIO_ID","RISK_MEASURE","_TYPE_");
270 h.defineDone();
271 end;
272 rc=h.find();
273 drop rc _PARM1_ _PARM2_ _MEASUREMENT_ index;
274 run;
275
276%mend;