没有简单的内置 SAS 语句可以以类似块的方式并排显示 3d 条。 GCHART BLOCK 语句使用 subgroup 选项在块交叉处构建堆叠条形图,但不能进行分组条形图。
Step 1 - 数据和差不多的图表
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;
不完全存在的 BLOCK 图
proc gchart data=have;
block province / group=period subgroup=var;
run;
quit;
为之奋斗
Sanjay Matange 博客“2015 年 3 月 10 日”的“A 3D Scatter Plot Macro”中的代码展示了如何在 SGPLOT 中 3D 表示数据。
经过一些数据处理和对宏的小修改后,可以渲染这个图表:
其他更复杂的渲染是可能的,但可能在当前的 SAS 产品之外找到:
第 2 步 - 数据准备,将分类值转换为单调标识值
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;
抖动省份标识值以区分每个时期内的ODE类型。
data have_jittered;
set have_mapped;
if var_id = 2 then province_id = province_id + 0.2;
size = 2;
run;
3D 散点图宏
如果您想按原样提交博客条目代码,请执行以下操作:
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绘图)
%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;
第 4 步 - 使用宏
/*--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;
其他注意事项
与任何 3d 绘图一样,出现的问题是“您是否需要查看器”来旋转、倾斜和缩放数据表示。 SAS ActiveX 驱动程序确实会产生这样的输出,但是,大多数当前的浏览器都禁用、弃用或删除了插件(需要运行 ActiveX 或 Java 小程序)。
与其深入研究 SGPLOT 和 GTL,不如把精力花在学习 WebGL 和 three.js library 上。一旦获得了这种理解,SAS 程序就可以编写包含或引用图表数据的 html 页面以及现代浏览器将本机呈现的查看器。
我没有对新的演示驱动程序进行最新的 SAS 研发,也许 WebGL 设备(类似于 ActiveX 或 Java)已经在进行中。哎呀,有一个 SAS VRML 图形驱动程序——但我没有多年来一直没有摆弄它。