我有一个数据集mydat
,其中包含以下变量:
MNES IV
0.84 0.40
0.89 0.34
0.91 0.31
0.93 0.29
0.95 0.26
0.98 0.23
0.99 0.22
1.00 0.22
1.02 0.20
1.04 0.18
1.07 0.18
我需要将三次样条拟合到这些元素,其中MNES
是对象(X),IV
是图像(Y)。
我通过PROC IML成功完成了我需要的东西,但我担心这不是最有效的解决方案。
具体来说,我的输出数据集是:
mnes iv
0.333 0.40
0.332 0.40 <- for mnes out of sample MNES range, copy first IV;
0.336 0.40
... ...
0.834 0.40
0.837 0.40
0.840 0.40
0.842 INTERPOLATION
0.845 INTERPOLATION
0.848 INTERPOLATION
...
1.066 INTERPOLATION
1.069 INTERPOLATION
1.072 INTERPOLATION
1.074 0.18
1.077 0.18 <- for mnes out of sample MNES range, copy last IV;
1.080 0.18
... ...
3.000 0.18
必要的具体内容如下:
MNES
总是有1001分,范围从0.(3)到3(因此,每一步都是(3-1 / 3)/ 1000)。IV
的插值只应用于最小值和最大值MNES
之间的点。MNES
大于样本中最大MNES
的点,IV
应该等于最大IV
的{{1}}和同样适用于最小MNES
(它总是按MNES
排序)。我对效率的担心是因为我必须解决这个问题大约200万次,现在它(下面的代码,使用PROC IML)对于10万个不同的输入数据集大约需要5个小时。
我的问题是:如果我希望在给定输入数据集(例如上面的输入数据集)的情况下拟合三次样条曲线并将其输出到特定的对象网格,我还有哪些替代方案? 什么解决方案最有效?
为了这个目的,我附上了我正在使用的代码,并解释了我在做什么。阅读以下内容对于回答问题没有必要,但对于使用PROC IML解决此类问题或希望更好地理解我所说的内容的人来说,这可能是有用的。
我正在复制一种方法论(Buss和Vilkov(2012)),除其他外,它将三次样条应用于这些元素,其中MNES
是对象(X),MNES
是图像(Y)。
以下代码主要基于Vilkov针对Buss和Vilkov(2012)的模型免费隐含波动率(MFIV)MATLAB代码,available on his website。
插值是一种通过计算OTM看跌和看涨价格来计算风险中性指标下股票收益波动率的数字的方法。我的目的是为了我的硕士论文。另外,由于我的PROC IML版本没有Black-Scholes选项定价功能,我自己定义了。
IV
答案 0 :(得分:4)
确定。我将为2个数据集执行此操作,以帮助您了解您有一堆事实。您必须修改输入,但这应该会为您提供更好的性能。
诀窍是“欺骗”EXPAND认为MNES是每日时间序列。我这样做是通过使它成为一个整数 - 日期值是SAS背后的整数。如果没有差距,ETS程序将采用“每日”频率。
完成此操作后,运行数据步骤以调用Black-Scholes(BLKSHPTPRC,BLKSHCLPRC)函数并完成分析。
/*Sample Data*/
data input1;
input MNES IV;
/*Make MNES and integer*/
MNES = MNES * 1000;
datalines;
0.84 0.40
0.89 0.34
0.91 0.31
0.93 0.29
0.95 0.26
0.98 0.23
0.99 0.22
1.00 0.22
1.02 0.20
1.04 0.18
1.07 0.18
;
run;
data input2;
input MNES IV;
MNES = MNES * 1000;
datalines;
0.80 0.40
0.9 0.34
0.91 0.31
0.93 0.29
0.95 0.26
0.98 0.23
1.02 0.19
1.04 0.18
1.07 0.16
;
run;
/*Get the first and last values from the input data*/
data _null_;
set input1 end=last;
if _n_ = 1 then do;
call symput("first1",mnes);
call symput("first1_v",iv);
end;
if last then do;
call symput("last1",mnes);
call symput("last1_v",iv);
end;
run;
data _null_;
set input2 end=last;
if _n_ = 1 then do;
call symput("first2",mnes);
call symput("first2_v",iv);
end;
if last then do;
call symput("last2",mnes);
call symput("last2_v",iv);
end;
run;
/*A list of the MNES values*/
data points;
do mnes=333 to 3000;
output;
end;
run;
/*Join Inputs to the values and set the lower and upper values*/
data input1;
merge points input1;
by mnes;
if mnes < &first1 then
iv = &first1_v;
if mnes > &last1 then
iv = &last1_v;
run;
data input2;
merge points input2;
by mnes;
if mnes < &first2 then
iv = &first2_v;
if mnes > &last2 then
iv = &last2_v;
run;
/*Append the data sets together, keep a value
so you can tell them apart*/
data toSpline;
set input1(in=ds1)
input2(in=ds2);
if ds1 then
Set=1;
else if ds2 then
Set=2;
run;
/*PROC Expand for the spline. The integer values
for MNES makes it think these are "daily" data*/
proc expand data=toSpline out=outSpline method=spline;
by set;
id mnes;
run;
答案 1 :(得分:1)
这是我提出的解决方案。遗憾的是,我还不能断定这是否比PROC IML解决方案更有效 - 只是对于一个数据集,它们都采用了几乎相同的运行时间。
MSPLINT:
real time: 1.42 seconds
cpu time 0.23 seconds
PROC IML:
real time: 1.02 seconds
cpu time: 0.26 seconds
与@DomPazz上述解决方案相比,此解决方案的最大缺点是我无法通过“按组”来处理数据,这肯定会让它快得多......我仍然思考我是否可以在不诉诸宏循环的情况下解决这个问题,但我完全没有想法。
我保留了使用@DomPazz提出的第一个和最后一个值来定义宏变量的解决方案,但我然后使用了一个datastep,它复制了第一个和最后一个值,或者根据MNES的值来应用插值正在逐步走过。它通过MSPLINT函数应用插值。其语法如下:
MSPLINT(X, n, X1 <, X2, ..., Xn>, Y1 <,Y2, ..., Yn> <, D1, Dn>)
其中X是您要评估样条曲线的对象,n是提供给函数的节点数(即输入数据中的观测数),X1,...,Xn是对象中的对象输入数据(即MNES)和Y1,...,Yn是输入数据中的图像(即IV)。 D1和Dn(可选)是您希望为插值对象X&lt; X1和X> Xn。
一个有趣的说明是指定D1和Dn。如果为0,则可以使网格之外的点等于插值区域内的最后一个观察点。然而,这迫使样条图像收敛到零的导数,可能在数据中产生非自然图案。我选择不将它们定义为零并分别定义插值区域外的点。
因此,我使用PROC SQL在宏变量中定义MNES和IV的元素列表,除以逗号,以便我可以在MSPLINT函数中输入它们。我还通过PROC SQL定义观察数量。
正如我在上面的答案中评论的那样,MNES在我的解释中没有明确定义。它需要被定义为变量u到元素的功率从-500到500.这只是一个细节,但它可以让你在下面的例子中理解MNES的来源。所以,这是解决方案,包括示例数据。
* Set model inputs;
%let m = 500;
%let k = 2;
%let u = (1+&k) ** (1/&m);
/*Sample Data*/
data input1;
input MNES 13.10 IV 8.6;
cards;
0.8444984010 0.400535
0.8901469633 0.347988
0.9129712444 0.318596
0.9357955255 0.291456
0.9586198066 0.264852
0.9814440877 0.236231
0.9928562283 0.224858
1.0042683688 0.220035
1.0270926499 0.201118
1.0499169310 0.189373
1.0727412121 0.185628
;
run;
data _null_;
set input1 end=last;
if _n_ = 1 then do;
call symput("first1",MNES);
call symput("first1_v",IV);
end;
if last then do;
call symput("last1",MNES);
call symput("last1_v",IV);
end;
run;
proc sql noprint;
select MNES into:mneslist
separated by ','
from input1;
select IV into:IVlist
separated by ','
from input1;
select count(*) into:countlist
from input1;
quit;
data splined;
do grid=-500 to 500;
mnes = (&u) ** grid;
if mnes < &first1 then IV = &first1_v;
if mnes > &last1 then IV = &last1_v;
if mnes >= &first1 and mnes <= &last1 then IV = msplint(mnes, &countlist, &mneslist, &IVlist);
end;
run;