使用SAS 9.4并排创建3d聚集条形图2个变量
更新:这里有一个完整数据集的链接 我有一个数据集,包括11个省份、半年期、指标、实际值和目标值。我正在尝试创建一个带有axises省X时间段X值的三维条形图,然后为每个省和时间段显示两个条形图,一个用于实际,一个用于目标 我的数据集如下所示:使用SAS 9.4并排创建3d聚集条形图2个变量,sas,Sas,更新:这里有一个完整数据集的链接 我有一个数据集,包括11个省份、半年期、指标、实际值和目标值。我正在尝试创建一个带有axises省X时间段X值的三维条形图,然后为每个省和时间段显示两个条形图,一个用于实际,一个用于目标 我的数据集如下所示: PROVINCE PERIOD VAR VALUE Bandundu 2013S1 OUT_BUD_ODE 250000 Bandundu 2013S1 TAR_BUD_ODE 545000 Bandundu 2013S2
PROVINCE PERIOD VAR VALUE
Bandundu 2013S1 OUT_BUD_ODE 250000
Bandundu 2013S1 TAR_BUD_ODE 545000
Bandundu 2013S2 OUT_BUD_ODE 283000
Bandundu 2013S2 TAR_BUD_ODE 545000
Bandundu 2014S1 OUT_BUD_ODE 800000
Bandundu 2014S1 TAR_BUD_ODE 645000
Bandundu 2014S2 OUT_BUD_ODE 700000
Bandundu 2014S2 TAR_BUD_ODE 645000
Bandundu 2015S1 OUT_BUD_ODE 369363
Bandundu 2015S1 TAR_BUD_ODE 945288
Bandundu 2015S2 OUT_BUD_ODE 1217449
Bandundu 2015S2 TAR_BUD_ODE 958417
Bandundu 2016S1 OUT_BUD_ODE 96618
Bandundu 2016S1 TAR_BUD_ODE 787740
Bandundu 2016S2 OUT_BUD_ODE 1358568
Bandundu 2016S2 TAR_BUD_ODE 787740
Bandundu 2017S1 OUT_BUD_ODE 1312900
Bandundu 2017S1 TAR_BUD_ODE 311729
Bandundu 2017S2 OUT_BUD_ODE .
Bandundu 2017S2 TAR_BUD_ODE 1299771
Bas-Congo 2013S1 OUT_BUD_ODE 150000
Bas-Congo 2013S1 TAR_BUD_ODE 450000
Bas-Congo 2013S2 OUT_BUD_ODE 145000
Bas-Congo 2013S2 TAR_BUD_ODE 450000
Bas-Congo 2014S1 OUT_BUD_ODE 1000000
Bas-Congo 2014S1 TAR_BUD_ODE 900965
Bas-Congo 2014S2 OUT_BUD_ODE 900000
Bas-Congo 2014S2 TAR_BUD_ODE 900965
Bas-Congo 2015S1 OUT_BUD_ODE 433892
Bas-Congo 2015S1 TAR_BUD_ODE 1115965
Bas-Congo 2015S2 OUT_BUD_ODE 943387
Bas-Congo 2015S2 TAR_BUD_ODE 1115965
Bas-Congo 2016S1 OUT_BUD_ODE 552002
.
.
.
如果我们只是绘制一个省的图表,在2D中会像下面这样,我们有并排的条形图、值和时间段。我们需要将省添加为第三个轴,并使条形图成为3d:
我们希望轴是省、周期、值,但对于每个点,我们有两个条形图,一个用于OUT\u BOD\u ODE,一个用于TAR\u BAR\u ODE,如果它们也是3d的,则这些条形图可能会更好用proc sgpanel
替换proc sgplot
我提出的解决方案有很多地方需要调整,但基本上是:
proc sgpanel data=Before;
panelby PROVINCE ;
vbar PERIOD / freq=VALUE group= VAR groupdisplay=cluster;
run;
准备数据
我加载了以下数据:
data Before;
infile datalines truncover;
input PROVINCE $13. year 4. semester $2. VAR $13. VALUE 8.;
format period date9.;
select (semester);
when ('S1') PERIOD = mdy(1, 1, year);
when ('S2') PERIOD = mdy(7, 1, year);
end;
datalines;
Bandundu 2013S1 OUT_BUD_ODE 250000
Bandundu 2013S1 TAR_BUD_ODE 545000
Bandundu 2013S2 OUT_BUD_ODE 283000
Bandundu 2013S2 TAR_BUD_ODE 545000
Bandundu 2014S1 OUT_BUD_ODE 800000
Bandundu 2014S1 TAR_BUD_ODE 645000
Bandundu 2014S2 OUT_BUD_ODE 700000
Bandundu 2014S2 TAR_BUD_ODE 645000
Bandundu 2015S1 OUT_BUD_ODE 369363
Bandundu 2015S1 TAR_BUD_ODE 945288
Bandundu 2015S2 OUT_BUD_ODE 1217449
Bandundu 2015S2 TAR_BUD_ODE 958417
Bandundu 2016S1 OUT_BUD_ODE 96618
Bandundu 2016S1 TAR_BUD_ODE 787740
Bandundu 2016S2 OUT_BUD_ODE 1358568
Bandundu 2016S2 TAR_BUD_ODE 787740
Bandundu 2017S1 OUT_BUD_ODE 1312900
Bandundu 2017S1 TAR_BUD_ODE 311729
Bandundu 2017S2 OUT_BUD_ODE .
Bandundu 2017S2 TAR_BUD_ODE 1299771
Bas-Congo 2013S1 OUT_BUD_ODE 150000
Bas-Congo 2013S1 TAR_BUD_ODE 450000
Bas-Congo 2013S2 OUT_BUD_ODE 145000
Bas-Congo 2013S2 TAR_BUD_ODE 450000
Bas-Congo 2014S1 OUT_BUD_ODE 1000000
Bas-Congo 2014S1 TAR_BUD_ODE 900965
Bas-Congo 2014S2 OUT_BUD_ODE 900000
Bas-Congo 2014S2 TAR_BUD_ODE 900965
Bas-Congo 2015S1 OUT_BUD_ODE 433892
Bas-Congo 2015S1 TAR_BUD_ODE 1115965
Bas-Congo 2015S2 OUT_BUD_ODE 943387
Bas-Congo 2015S2 TAR_BUD_ODE 1115965
Bas-Congo 2016S1 OUT_BUD_ODE 552002
;
建议:如果您在答案中包含您的代码,我将调整我的解决方案,使其看起来更符合您的预期。*
关于这个答案
G3D程序允许绘制所需的三维图形类型,但
- 您需要数值类变量,所以请使用一种格式使它们显示为字符
- 它不覆盖两个绘图,但允许为数据点着色
没有简单的内置SAS语句可以以类似块的方式并排执行3d条。GCHART BLOCK语句使用“子组”选项在块交点处生成堆叠条,但不能生成分组条 步骤1-数据和图表 不完全有方块图
proc gchart data=have;
block province / group=period subgroup=var;
run;
quit;
努力
Sanjay Matange的“Graphically Speaking March 10,2015”博客中的代码显示了如何在SGPLOT中三维表示数据
经过一些数据工作和对宏的小修改后,可以呈现此图表:
其他更复杂的渲染是可能的,但可能在当前SAS产品之外找到:
步骤2-数据准备,将分类值转换为单调标识值
抖动省标识值,以便在每个时段内分离ODE类型
data have_jittered;
set have_mapped;
if var_id = 2 then province_id = province_id + 0.2;
size = 2;
run;
三维散点图宏
如果您想按原样提交博客条目代码,以下操作将完成:
filename source1 http "https://blogs.sas.com/content/graphicallyspeaking/files/2015/03/Matrix_Functions.txt";
filename source2 http "https://blogs.sas.com/content/graphicallyspeaking/files/2015/03/Ortho_3D_Macro_94.txt";
%include source1;
%include source2;
filename source1;
filename source2;
步骤3-修改的宏
我修改了Sanjay的宏以处理更多的输入,从而可以生成更“条形”的渲染
矩阵支持没有变化:
options cmplib=sasuser.funcs;
proc fcmp outlib=sasuser.funcs.mat;
subroutine MatInv(Mat[*,*], InvMat[*,*]);
outargs InvMat;
call inv(Mat, InvMat);
endsub;
subroutine MatMult(A[*,*], B[*,*], C[*,*]);
outargs C;
call mult(A, B, C);
endsub;
subroutine MatIdent(A[*,*]);
outargs A;
call identity(A);
endsub;
run;
quit;
已更新用于打开或关闭图灵墙的Ortho 3D宏,设置标记大小和符号以及3D框的相对比例。对于添加tic标记,有一些未来的论证(tic图纸不在此版本中)
步骤4-使用宏
其他考虑
与任何3d绘图一样,在旋转、倾斜和缩放数据显示时会出现“是否需要查看器”的问题。SAS ActiveX驱动程序确实会产生这样的输出,但是,大多数当前浏览器都禁用、弃用或删除了插件(运行ActiveX或Java小程序所需)
与其深入研究SGPLOT和GTL,不如把精力花在学习WebGL和GTL上。一旦了解了这一点,SAS程序就可以编写包含或引用图表数据的html页面以及现代浏览器本机呈现的查看器
我对最新的SAS研发新的演示驱动程序不感兴趣,也许在
WebGL
设备(类似于ActiveX或Java)上已经有了工作。见鬼,有一个SAS VRML图形驱动程序——但我已经多年没用过了。您的数据和代码不一致,所以很难想象或看到哪里出了问题。好的,我删除了不太有用的代码,将代码与数据对齐会很有帮助。就目前情况而言,问题不再符合SO准则,具体来说,您尝试了什么?我尝试将其与示例代码对齐,但不起作用。使用3D聚集条形图意味着什么?它应该看起来像或像或像其他东西吗?这和我想要的不太接近,但是谢谢你的尝试。我正在寻找一个三维条形图,其中x轴设置为周期,y轴设置为值,z轴设置为省。然后,每个点有两条横线,一条横线表示OUT\u BUD\u ODE,一条横线表示TAR\u BUD\u ODE。这些条也应该是3d的。因此我在你帖子下面的评论中提出了我的问题。我不知道如何在3d中做到这一点。我可以复制代码,将做一个并排条形图在二维,但没有看到任何东西,将在3dno它没有这样做。我可以这样做,但是你如何把它们并排展示而不是堆积如山呢?首先把你的问题清楚地提出来怎么样?
create table IN_NUM as
select IN_.VALUE
, case VAR
when 'TAR_BUD_ODE' then PROV.start
when 'OUT_BUD_ODE' then PROV.start + 0.2
end as PROVINCE format=province.
, PROV.start as PROVINCE format=province.
, PER.start as PERIOD format=period.
, case VAR
when 'TAR_BUD_ODE' then 'blue'
when 'OUT_BUD_ODE' then 'crimson'
end as color
...
data CNTL;
set CNT_VALUES;
...
start = _N_;
end = _N_ + 0.5;
keep FmtName label start;
run;
data have;
infile datalines truncover;
length PROVINCE $13. PERIOD $6. VAR $13. VALUE 8.;
input PROVINCE PERIOD VAR VALUE;
datalines;
Bandundu 2013S1 OUT_BUD_ODE 250000
Bandundu 2013S1 TAR_BUD_ODE 545000
Bandundu 2013S2 OUT_BUD_ODE 283000
Bandundu 2013S2 TAR_BUD_ODE 545000
Bandundu 2014S1 OUT_BUD_ODE 800000
Bandundu 2014S1 TAR_BUD_ODE 645000
Bandundu 2014S2 OUT_BUD_ODE 700000
Bandundu 2014S2 TAR_BUD_ODE 645000
Bandundu 2015S1 OUT_BUD_ODE 369363
Bandundu 2015S1 TAR_BUD_ODE 945288
Bandundu 2015S2 OUT_BUD_ODE 1217449
Bandundu 2015S2 TAR_BUD_ODE 958417
Bandundu 2016S1 OUT_BUD_ODE 96618
Bandundu 2016S1 TAR_BUD_ODE 787740
Bandundu 2016S2 OUT_BUD_ODE 1358568
Bandundu 2016S2 TAR_BUD_ODE 787740
Bandundu 2017S1 OUT_BUD_ODE 1312900
Bandundu 2017S1 TAR_BUD_ODE 311729
Bandundu 2017S2 OUT_BUD_ODE .
Bandundu 2017S2 TAR_BUD_ODE 1299771
Bas-Congo 2013S1 OUT_BUD_ODE 150000
Bas-Congo 2013S1 TAR_BUD_ODE 450000
Bas-Congo 2013S2 OUT_BUD_ODE 145000
Bas-Congo 2013S2 TAR_BUD_ODE 450000
Bas-Congo 2014S1 OUT_BUD_ODE 1000000
Bas-Congo 2014S1 TAR_BUD_ODE 900965
Bas-Congo 2014S2 OUT_BUD_ODE 900000
Bas-Congo 2014S2 TAR_BUD_ODE 900965
Bas-Congo 2015S1 OUT_BUD_ODE 433892
Bas-Congo 2015S1 TAR_BUD_ODE 1115965
Bas-Congo 2015S2 OUT_BUD_ODE 943387
Bas-Congo 2015S2 TAR_BUD_ODE 1115965
Bas-Congo 2016S1 OUT_BUD_ODE 552002
;
run;
proc gchart data=have;
block province / group=period subgroup=var;
run;
quit;
proc sql;
create table uniqs as
select distinct 'province' as _var_, province as key from have
union
select distinct 'period' as _var_, period as key from have
union
select distinct 'var' as _var_, var as key from have
;
quit;
data fmt(keep=fmtname start label);
set uniqs;
by _var_;
if first._var_ then id=1; else id+1;
fmtname = _var_;
start = id;
label = key;
run;
proc format cntlin = fmt;
run;
proc sql;
create table have_mapped as
select
have.*
, f1.start as province_id
, f2.start as period_id
, f3.start as var_id
from have
left join fmt f1 on f1.label = province
left join fmt f2 on f2.label = period
left join fmt f3 on f3.label = var
where f1.fmtname = 'province'
and f2.fmtname = 'period'
and f3.fmtname = 'var'
;
quit;
data have_jittered;
set have_mapped;
if var_id = 2 then province_id = province_id + 0.2;
size = 2;
run;
filename source1 http "https://blogs.sas.com/content/graphicallyspeaking/files/2015/03/Matrix_Functions.txt";
filename source2 http "https://blogs.sas.com/content/graphicallyspeaking/files/2015/03/Ortho_3D_Macro_94.txt";
%include source1;
%include source2;
filename source1;
filename source2;
options cmplib=sasuser.funcs;
proc fcmp outlib=sasuser.funcs.mat;
subroutine MatInv(Mat[*,*], InvMat[*,*]);
outargs InvMat;
call inv(Mat, InvMat);
endsub;
subroutine MatMult(A[*,*], B[*,*], C[*,*]);
outargs C;
call mult(A, B, C);
endsub;
subroutine MatIdent(A[*,*]);
outargs A;
call identity(A);
endsub;
run;
quit;
%macro Ortho3D_Macro
( Data=, X=, Y=, Z=, Group=, Size=, Lblx=X, Lbly=Y, Lblz=Z,
Tilt=65, Rotate=-55, Attrmap=, Title=
, shadewalls = yes
, outlineaxis = yes
, backwall = yes
, sidewall = yes
, floorwall = yes
, floor_marker_symbol = circlefilled
, floor_marker_size = 5
, showdata = yes
, xnormalscale = 1
, ynormalscale = 2
, znormalscale = 1
, xtic=, xtic_jitter=
, ytic=, ytic_jitter=
, ztic=, ztic_jitter=
);
%local A B C WallData;
%let A=&Tilt;
%let B=0;
%let C=&Rotate;
%let WallData=wall_Axes;
/*--Define walls and axes--*/
filename walldata "work.walldata.source";
options noquotelenmax;
data _null_;
datalines="
| X1-Axis D -1 -1 -1 1 -1 -1 0 -1 -1 1
| X3-Axis L -1 -1 1 1 -1 1 . . . .
| X4-Axis D -1 1 1 1 1 1 . . . .
| Y2-Axis D -1 -1 1 -1 1 1 . . . .
| Y3-Axis D 1 -1 -1 1 1 -1 1 0 -1 2
| Y4-Axis L 1 -1 1 1 1 1 . . . .
| Z1-Axis D -1 -1 -1 -1 -1 1 -1 -1 0 3
| Z2-Axis L 1 -1 -1 1 -1 1 . . . .
| Z4-Axis D 1 1 -1 1 1 1 . . . .
| Bottom D -1 -1 -1 . . . . . . .
| Bottom D 1 -1 -1 . . . . . . .
| Bottom D 1 1 -1 . . . . . . .
| Bottom D -1 1 -1 . . . . . . .
| Back D -1 -1 -1 . . . . . . .
| Back D -1 1 -1 . . . . . . .
| Back D -1 1 1 . . . . . . .
| Back D -1 -1 1 . . . . . . .
| Right D -1 1 -1 . . . . . . .
| Right D 1 1 -1 . . . . . . .
| Right D 1 1 1 . . . . . . .
| Right D -1 1 1 . . . . . . .
";
do _n_ = 1 by 1;
file walldata;
line = scan(datalines,_n_,'|');
if line = '' then leave;
put line;
end;
run;
data wall_Axes;
infile walldata;
input @1 id $ group $ xw yw zw xw2 yw2 zw2 xl yl zl label;
run;
/*--Project the walls and axes--*/
data projected_walls;
keep id group xw yw zw xw2 yw2 zw2 xl yl zl lbx lby lbz label;
array u[4,4] _temporary_; /*--Intermediate Matrix--*/
array v[4,4] _temporary_; /*--Intermediate Matrix--*/
array w[4,4] _temporary_; /*--Final View Matrix--*/
array m[4,4] _temporary_; /*--Projection Matrix--*/
array rx[4,4] _temporary_; /*--X rotation Matrix--*/
array ry[4,4] _temporary_; /*--Y rotation Matrix--*/
array rz[4,4] _temporary_; /*--Z rotation Matrix--*/
array d[4,1] _temporary_; /*--World Data Array --*/
array p[4,1] _temporary_; /*--Projected Data Array --*/
retain r t f n;
r=1; t=1; f=1; n=-1;
pi=constant("PI");
fac=pi/180;
A=&A*fac; B=&B*fac; C=&C*fac;
/*--Set up projection matrix--*/
m[1,1]=1/r; m[1,2]=0.0; m[1,3]=0.0; m[1,4]=0.0;
m[2,1]=0.0; m[2,2]=1/t; m[2,3]=0.0; m[2,4]=0.0;
m[3,1]=0.0; m[3,2]=0.0; m[3,3]=-2/(f-n); m[3,4]=-(f+n)/(f-n);
m[4,1]=0.0; m[4,2]=0.0; m[4,3]=0.0; m[4,4]=1.0;
/*--Set up X rotation matrix--*/
rx[1,1]=1; rx[1,2]=0.0; rx[1,3]=0.0; rx[1,4]=0.0;
rx[2,1]=0.0; rx[2,2]=cos(A); rx[2,3]=-sin(A); rx[2,4]=0.0;
rx[3,1]=0.0; rx[3,2]=sin(A); rx[3,3]=cos(A); rx[3,4]=0.0;
rx[4,1]=0.0; rx[4,2]=0.0; rx[4,3]=0.0; rx[4,4]=1.0;
/*--Set up Y rotation matrix--*/
ry[1,1]=cos(B); ry[1,2]=0.0; ry[1,3]=sin(B); ry[1,4]=0.0;
ry[2,1]=0.0; ry[2,2]=1.0; ry[2,3]=0.0; ry[2,4]=0.0;
ry[3,1]=-sin(B); ry[3,2]=0.0; ry[3,3]=cos(B); ry[3,4]=0.0;
ry[4,1]=0.0; ry[4,2]=0.0; ry[4,3]=0.0; ry[4,4]=1.0;
/*--Set up Z rotation matrix--*/
rz[1,1]=cos(C); rz[1,2]=-sin(C); rz[1,3]=0.0; rz[1,4]=0.0;
rz[2,1]=sin(C); rz[2,2]=cos(C); rz[2,3]=0.0; rz[2,4]=0.0;
rz[3,1]=0.0; rz[3,2]=0.0; rz[3,3]=1.0; rz[3,4]=0.0;
rz[4,1]=0.0; rz[4,2]=0.0; rz[4,3]=0.0; rz[4,4]=1.0;
/*--Build transform matrix--*/
call MatMult(rz, m, u);
call MatMult(ry, u, v);
call MatMult(rx, v, w);
set &WallData;
/*--Set axis labels--*/
if label eq 1 then lbx="&Lblx";
if label eq 2 then lby="&Lbly";
if label eq 3 then lbz="&Lblz";
/*--Transform walls--*/
d[1,1]=xw*&xnormalscale; d[2,1]=yw*&ynormalscale; d[3,1]=zw*&znormalscale; d[4,1]=1;
call MatMult(w, d, p);
xw=p[1,1]; yw=p[2,1]; zw=p[3,1];
/*--Transform axes--*/
d[1,1]=xw2*&xnormalscale; d[2,1]=yw2*&ynormalscale; d[3,1]=zw2*&znormalscale; d[4,1]=1;
call MatMult(w, d, p);
xw2=p[1,1]; yw2=p[2,1]; zw2=p[3,1];
/*--Transform labels--*/
d[1,1]=xl*&xnormalscale; d[2,1]=yl*&ynormalscale; d[3,1]=zl*&znormalscale; d[4,1]=1;
call MatMult(w, d, p);
xl=p[1,1]; yl=p[2,1]; zl=p[3,1];
run;
/**/
/*--Compute data ranges--*/
data _null_;
retain xmin 1e10 xmax -1e10 ymin 1e10 ymax -1e10 zmin 1e10 zmax -1e10;
set &Data end=last;
xmin=min(xmin, &X);
xmax=max(xmax, &X);
ymin=min(ymin, &Y);
ymax=max(ymax, &Y);
zmin=min(zmin, &Z);
zmax=max(zmax, &Z);
if last then do;
call symputx("xmin", xmin); call symputx("xmax", xmax);
call symputx("ymin", ymin); call symputx("ymax", ymax);
call symputx("zmin", zmin); call symputx("zmax", zmax);
end;
run;
/*--Normalize the data to -1 to +1 ranges--*/
data normalized;
keep &Group &Size x y z xf yf zf xb yb zb xb2 yb2 zb2 xs ys zs xs2 ys2 zs2;
xrange=&xmax-&xmin;
yrange=&ymax-&ymin;
zrange=&zmax-&zmin;
set &data;
/*--data points--*/
x=(2*&xnormalscale)*(&X-&xmin)/xrange - &xnormalscale;
y=(2*&ynormalscale)*(&Y-&ymin)/yrange - &ynormalscale;
z=(2*&znormalscale)*(&Z-&zmin)/zrange - &znormalscale;
/*--Floor--*/
xf=x; yf=y; zf=-1;
/*--Back Wall--*/
xb=-1; yb=y; zb=z;
xb2=-1; yb2=y; zb2=-1;
/*--Side Wall--*/
xs=x; ys=1; zs=z;
xs2=x; ys2=1; zs2=-1;
run;
/*--Project the data--*/
data projected_data;
keep &Group &Size xd yd zd xf yf zf xb yb zb xb2 yb2 zb2 xs ys zs xs2 ys2 zs2;
array u[4,4] _temporary_; /*--Intermediate Matrix--*/
array v[4,4] _temporary_; /*--Intermediate Matrix--*/
array w[4,4] _temporary_; /*--Final View Matrix--*/
array m[4,4] _temporary_; /*--Projection Matrix--*/
array rx[4,4] _temporary_; /*--X rotation Matrix--*/
array ry[4,4] _temporary_; /*--Y rotation Matrix--*/
array rz[4,4] _temporary_; /*--Z rotation Matrix--*/
array d[4,1] _temporary_; /*--World Data Array --*/
array p[4,1] _temporary_; /*--Projected Data Array --*/
retain r t f n;
r=1; t=1; f=1; n=-1;
pi=constant("PI");
fac=pi/180;
/* call symput ("X", A); call symput ("Y", B); call symput ("Z", C);*/
A=&A*fac; B=&B*fac; C=&C*fac;
/*--Set up projection matrix--*/
m[1,1]=1/r; m[1,2]=0.0; m[1,3]=0.0; m[1,4]=0.0;
m[2,1]=0.0; m[2,2]=1/t; m[2,3]=0.0; m[2,4]=0.0;
m[3,1]=0.0; m[3,2]=0.0; m[3,3]=-2/(f-n); m[3,4]=-(f+n)/(f-n);
m[4,1]=0.0; m[4,2]=0.0; m[4,3]=0.0; m[4,4]=1.0;
/*--Set up X rotation matrix--*/
rx[1,1]=1; rx[1,2]=0.0; rx[1,3]=0.0; rx[1,4]=0.0;
rx[2,1]=0.0; rx[2,2]=cos(A); rx[2,3]=-sin(A); rx[2,4]=0.0;
rx[3,1]=0.0; rx[3,2]=sin(A); rx[3,3]=cos(A); rx[3,4]=0.0;
rx[4,1]=0.0; rx[4,2]=0.0; rx[4,3]=0.0; rx[4,4]=1.0;
/*--Set up Y rotation matrix--*/
ry[1,1]=cos(B); ry[1,2]=0.0; ry[1,3]=sin(B); ry[1,4]=0.0;
ry[2,1]=0.0; ry[2,2]=1.0; ry[2,3]=0.0; ry[2,4]=0.0;
ry[3,1]=-sin(B); ry[3,2]=0.0; ry[3,3]=cos(B); ry[3,4]=0.0;
ry[4,1]=0.0; ry[4,2]=0.0; ry[4,3]=0.0; ry[4,4]=1.0;
/*--Set up Z rotation matrix--*/
rz[1,1]=cos(C); rz[1,2]=-sin(C); rz[1,3]=0.0; rz[1,4]=0.0;
rz[2,1]=sin(C); rz[2,2]=cos(C); rz[2,3]=0.0; rz[2,4]=0.0;
rz[3,1]=0.0; rz[3,2]=0.0; rz[3,3]=1.0; rz[3,4]=0.0;
rz[4,1]=0.0; rz[4,2]=0.0; rz[4,3]=0.0; rz[4,4]=1.0;
/*--Build transform matris--*/
call MatMult(rz, m, u);
call MatMult(ry, u, v);
call MatMult(rx, v, w);
set normalized;
/*--Transform data--*/
d[1,1]=x; d[2,1]=y; d[3,1]=z; d[4,1]=1;
call MatMult(w, d, p);
xd=p[1,1]; yd=p[2,1]; zd=p[3,1]; wd=p[4,1];
/*--Transform floor drop shadow--*/
d[1,1]=xf; d[2,1]=yf; d[3,1]=zf; d[4,1]=1;
call MatMult(w, d, p);
xf=p[1,1]; yf=p[2,1]; zf=p[3,1]; wf=p[4,1];
/*--Transform back wall shadow--*/
d[1,1]=xb; d[2,1]=yb; d[3,1]=zb; d[4,1]=1;
call MatMult(w, d, p);
xb=p[1,1]; yb=p[2,1]; zb=p[3,1]; wb=p[4,1];
d[1,1]=xb2; d[2,1]=yb2; d[3,1]=zb2; d[4,1]=1;
call MatMult(w, d, p);
xb2=p[1,1]; yb2=p[2,1]; zb2=p[3,1]; wb2=p[4,1];
/*--Transform side wall shadow--*/
d[1,1]=xs; d[2,1]=ys; d[3,1]=zs; d[4,1]=1;
call MatMult(w, d, p);
xs=p[1,1]; ys=p[2,1]; zs=p[3,1]; ws=p[4,1];
d[1,1]=xs2; d[2,1]=ys2; d[3,1]=zs2; d[4,1]=1;
call MatMult(w, d, p);
xs2=p[1,1]; ys2=p[2,1]; zs2=p[3,1]; ws2=p[4,1];
run;
/*--Combine data with walls--*/
data combined;
merge projected_walls projected_data;
run;
%let h=_;
%let suf=&a&h&c;
/*--Draw the graph--*/
options mprint;
title "&Title";
footnote j=l h=0.7 "X:&X-Rotation=&A Y:&Y-Rotation=&B Z:&Z-Rotation=&C";
proc sgplot data=combined nowall noborder aspect=1 noautolegend dattrmap=&Attrmap des="Ortho3D plot";
%if &shadewalls = yes %then %do;
polygon id=id x=xw y=yw / fill lineattrs=(color=lightgray)
group=id transparency=0 attrid=walls;
%end;
%if &outlineaxis = yes %then %do;
vector x=xw2 y=yw2 / xorigin=xw yorigin=yw group=group noarrowheads attrid=Axes;
%end;
text x=xl y=yl text=lbx / position=bottomleft;
text x=xl y=yl text=lby / position=bottomright;
text x=xl y=yl text=lbz / position=left;
%if &backwall = yes %then %do;
* --Back wall shadow--;
vector x=xb y=yb / xorigin=xb2 yorigin=yb2 noarrowheads lineattrs=(color=gray) transparency=0.9;
scatter x=xb y=yb / markerattrs=(symbol=circlefilled size=5) group=&group transparency=0.9;
%end;
%if &sidewall = yes %then %do;
*--Side wall shadow--;
vector x=xs y=ys / xorigin=xs2 yorigin=ys2 noarrowheads lineattrs=(color=gray) transparency=0.9;
scatter x=xs y=ys / markerattrs=(symbol=circlefilled size=5) group=&group transparency=0.9;
%end;
%if &floorwall = yes %then %do;
*--Floor line to data;
vector x=xd y=yd / xorigin=xf yorigin=yf noarrowheads lineattrs=(thickness=&floor_marker_size) group=&group transparency=0.7;
*--Floor shadow--;
scatter x=xf y=yf / markerattrs=(symbol=&floor_marker_symbol size=&floor_marker_size) group=&group transparency=0.7;
%end;
%if &showdata = yes %then %do;
*--Data--;
scatter x=xd y=yd / group=&Group name='s' nomissinggroup dataskin=gloss
filledoutlinedmarkers markerattrs=(symbol=&floor_marker_symbol size=&floor_marker_size) dataskin=gloss;
%end;
keylegend 's' / autoitemsize;
xaxis display=none offsetmin=0.05 offsetmax=0.05 min=-1.8 max=1.8;
yaxis display=none offsetmin=0.05 offsetmax=0.05 min=-1.8 max=1.8;
run;
footnote;
%finished:
%mend Ortho3D_Macro;
/*--Define Attributes map for walls and axes--*/
data attrmap;
length ID $ 9 fillcolor $ 10 linecolor $ 10 linepattern $ 10;
input id $ value $10-20 fillcolor $ linecolor $ linepattern $;
datalines;
Walls Bottom cxdfdfdf cxdfdfdf Solid
Walls Back cxefefef cxefefef Solid
Walls Right cxffffff cxffffff Solid
Axes D white black Solid
Axes L white black ShortDash
;
run;
options source;
options mautosource nomprint nomlogic;
ods listing close;
ods html5 file='c:\temp\sample.html' gpath='c:\temp';
options cmplib=sasuser.funcs;
%let dpi=200;
ods graphics / reset
attrpriority=color
imagefmt=png
imagename="Sample"
width=11in
height=8in
;
%Ortho3D_Macro (
Title=Comparative ODE values by province over period
, Data=work.have_jittered, Attrmap=attrmap
, X=province_id, Lblx=Province, xtic=1 to 2, xtic_jitter=0.1
, Y=period_id, Lbly=Period, ytic=1 to 10, ytic_jitter=0
, Z=value, Lblz=Value, ztic=0 to 1e6 by 2e5, ztic_jitter=0
, Group=Var
, Tilt=55, Rotate=-25
, Size=size
, outlineaxis = no
, shadewalls = yes
, backwall = no
, sidewall = no
, floorwall = yes
, floor_marker_symbol = diamondfilled
, floor_marker_size = 6
, showdata = yes
);
ods html5 close;