如何通过 IML 替换处理 SAS 文件的整个观察
How to replace a whole observation working on a SAS file through IML
老用户,第一次发布。我是 IML 的新手,之前在 R 中玩过。我目前正在尝试创建一个邻接列表,以便更轻松地计算 SAS/IML 中的网络。我正在处理的文件很大。我正在做一个涉及使用 SASfile 并且在内存中没有邻接列表的实现。创建一个空文件并从特定行(对应于特定代理)读取一切顺利,直到 "final" 步骤:更新整个观察。
以下是有效的 IML 代码,直到最后阶段。
proc iml;
/* initialize vars*/
checkObs = 2;
numCol = 5;
db = "myTestDataBase";
nObs = 5;
temp = {};
myList = J(1, numCol, 0);
nVarToUpdate = 2;
/* create empty database */
create (db) from myList;
append from myList;
close (db);
do i = 1 to (nObs-1);
edit (db);
append from myList;
close (db);
end;
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
/* I want to replace point chekObs with the whole of vector temp*/
replace point checkObs var _all_;
close (db);
print temp;
我的目标是 replace/update 整个观察(行),同时保持行的顺序不变。有什么想法吗?
你的问题是这样的,来自the documentation for REPLACE
:
The REPLACE statement replaces the values of observations in a SAS data set with current values of matrices with the same name.
所以这行得通:
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
col2 = 1; /* I would like to update some values*/
/* I want to replace point chekObs with the whole of vector temp*/
replace point (checkObs) var('col2');
close (db);
当然,这可能不是您想要的。
我不知道是否有 IML 方法可以做到这一点 - 也许 Rick 会碰巧得到答案。我不确定为什么不存在,但我远不是 IML 专家,所以也许存在——或者是不存在的原因。我的感觉是,您尝试做的事情可能以完全不同的方式更好地完成;不过,目前还不清楚你在做什么。如果你正在做一个神经网络或类似的东西,那里有很多代码——另一种方法可能会出现。我发现从其他语言来到 SAS 的人通常期望在另一种语言中工作的东西在这里工作 - 当有一种完全不同的方式可以在 SAS 中有效地做到这一点时(效果很好,如果不是更好 - 只需要意识到它是什么) .
无论如何,您可以通过一些设置和宏语言来完成此操作。这是一个例子。您必须提前进行设置 - 如果您有动态变量列表,这可能会很复杂(例如,您可能必须有一个双重解析的宏变量名称)。
使用宏方法要记住的主要事情是,宏值必须在 IML 运行之前完全可用;所以你不能在参数中放置一个变量,它必须是一个硬编码值或一个宏变量,它本身在 IML 运行之前是已知的。 DOSUBL/RUN_MACRO 可能会让您在某种程度上解决这个问题,但这样做可能会很复杂。
proc sql;
select name into :namelist separated by ','
from dictionary.columns
where libname='WORK' and memname='MYTESTDATABASE';
quit;
%macro replace_var(num_Vars=1);
%do _i = 1 %to &num_vars.;
%let _var = %scan(%bquote(&namelist),&_i,%str(,));
&_var. = temp[&_i.];
%end;
%mend;
proc iml;
/* initialize vars*/
checkObs = 4;
numCol = 5;
db = "myTestDataBase";
nObs = 5;
temp = {};
myList = J(1, numCol, 0);
nVarToUpdate = 2;
/* create empty database */
create (db) from myList;
append from myList;
close (db);
do i = 2 to (nObs);
edit (db);
append from myList;
close (db);
end;
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp[colname=temp_names]; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
%replace_var(num_vars=&sqlobs.); *call the macro which sets the various variable names to their values. Semicolon is just for syntax coloring to work.;
/* I want to replace point chekObs with the whole of vector temp*/
replace point (checkObs) var _all_;
close (db);
print temp;
print temp_names;
quit;
@Joe 的解决方案可以解决这个问题,但是以这种方式结合宏和 IML 就像亲吻你的妹妹一样:这并不愉快,人们会用奇怪的眼光看你。 Joe 获得变量名称的想法是正确的,但他忘记了 you can use the VALSET call to perform indirect assignment。换句话说,通过拥有变量的名称,您可以更改它的值。
如果你想避免宏,你可以一次获取变量的名称(在 EDIT 循环之外),然后遍历变量的名称,如下所示:
/* get column names ONE TIME */
use (db);
read next var _ALL_ into temp[colname=varNames]; /* get names of cols */
close (db);
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
do i = 1 to ncol(temp);
call valset(varNames[i], temp[i]); /* update scalar variables */
end;
replace point (checkObs) var _all_;
close (db);
此技术的主要优点是您可以在 运行 时间发现变量名称。
请注意,这种方法(创建变量名)可能很危险,因为如果数据集有一个名为 X 的变量,您将覆盖程序中任何预先存在的同名变量。
另请注意,在庞大的数据集上使用 EDIT 和 READ POINT 一次更改一行将比冬天糖蜜流上山要慢。如果可能,您应该read in a big block of data,对该块中的所有行进行操作,然后写出该块。
如果磁盘 space 允许,您可能想尝试使用 SETIN 和 SETOUT 语句 read from one data set while writing to another。那会
完全消除了对 REPLACE 语句和 VALSET 调用的需要。一般来说,以只读方式打开数据集并按顺序处理它比打开它read/write并使用随机访问处理它更有效。
老用户,第一次发布。我是 IML 的新手,之前在 R 中玩过。我目前正在尝试创建一个邻接列表,以便更轻松地计算 SAS/IML 中的网络。我正在处理的文件很大。我正在做一个涉及使用 SASfile 并且在内存中没有邻接列表的实现。创建一个空文件并从特定行(对应于特定代理)读取一切顺利,直到 "final" 步骤:更新整个观察。
以下是有效的 IML 代码,直到最后阶段。
proc iml;
/* initialize vars*/
checkObs = 2;
numCol = 5;
db = "myTestDataBase";
nObs = 5;
temp = {};
myList = J(1, numCol, 0);
nVarToUpdate = 2;
/* create empty database */
create (db) from myList;
append from myList;
close (db);
do i = 1 to (nObs-1);
edit (db);
append from myList;
close (db);
end;
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
/* I want to replace point chekObs with the whole of vector temp*/
replace point checkObs var _all_;
close (db);
print temp;
我的目标是 replace/update 整个观察(行),同时保持行的顺序不变。有什么想法吗?
你的问题是这样的,来自the documentation for REPLACE
:
The REPLACE statement replaces the values of observations in a SAS data set with current values of matrices with the same name.
所以这行得通:
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
col2 = 1; /* I would like to update some values*/
/* I want to replace point chekObs with the whole of vector temp*/
replace point (checkObs) var('col2');
close (db);
当然,这可能不是您想要的。
我不知道是否有 IML 方法可以做到这一点 - 也许 Rick 会碰巧得到答案。我不确定为什么不存在,但我远不是 IML 专家,所以也许存在——或者是不存在的原因。我的感觉是,您尝试做的事情可能以完全不同的方式更好地完成;不过,目前还不清楚你在做什么。如果你正在做一个神经网络或类似的东西,那里有很多代码——另一种方法可能会出现。我发现从其他语言来到 SAS 的人通常期望在另一种语言中工作的东西在这里工作 - 当有一种完全不同的方式可以在 SAS 中有效地做到这一点时(效果很好,如果不是更好 - 只需要意识到它是什么) .
无论如何,您可以通过一些设置和宏语言来完成此操作。这是一个例子。您必须提前进行设置 - 如果您有动态变量列表,这可能会很复杂(例如,您可能必须有一个双重解析的宏变量名称)。
使用宏方法要记住的主要事情是,宏值必须在 IML 运行之前完全可用;所以你不能在参数中放置一个变量,它必须是一个硬编码值或一个宏变量,它本身在 IML 运行之前是已知的。 DOSUBL/RUN_MACRO 可能会让您在某种程度上解决这个问题,但这样做可能会很复杂。
proc sql;
select name into :namelist separated by ','
from dictionary.columns
where libname='WORK' and memname='MYTESTDATABASE';
quit;
%macro replace_var(num_Vars=1);
%do _i = 1 %to &num_vars.;
%let _var = %scan(%bquote(&namelist),&_i,%str(,));
&_var. = temp[&_i.];
%end;
%mend;
proc iml;
/* initialize vars*/
checkObs = 4;
numCol = 5;
db = "myTestDataBase";
nObs = 5;
temp = {};
myList = J(1, numCol, 0);
nVarToUpdate = 2;
/* create empty database */
create (db) from myList;
append from myList;
close (db);
do i = 2 to (nObs);
edit (db);
append from myList;
close (db);
end;
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp[colname=temp_names]; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
%replace_var(num_vars=&sqlobs.); *call the macro which sets the various variable names to their values. Semicolon is just for syntax coloring to work.;
/* I want to replace point chekObs with the whole of vector temp*/
replace point (checkObs) var _all_;
close (db);
print temp;
print temp_names;
quit;
@Joe 的解决方案可以解决这个问题,但是以这种方式结合宏和 IML 就像亲吻你的妹妹一样:这并不愉快,人们会用奇怪的眼光看你。 Joe 获得变量名称的想法是正确的,但他忘记了 you can use the VALSET call to perform indirect assignment。换句话说,通过拥有变量的名称,您可以更改它的值。
如果你想避免宏,你可以一次获取变量的名称(在 EDIT 循环之外),然后遍历变量的名称,如下所示:
/* get column names ONE TIME */
use (db);
read next var _ALL_ into temp[colname=varNames]; /* get names of cols */
close (db);
/* read index checkObs and write to temp*/
edit (db);
read point (checkObs) into temp; /* Read an entire row*/
temp[nVarToUpdate] = 1; /* I would like to update some values*/
do i = 1 to ncol(temp);
call valset(varNames[i], temp[i]); /* update scalar variables */
end;
replace point (checkObs) var _all_;
close (db);
此技术的主要优点是您可以在 运行 时间发现变量名称。
请注意,这种方法(创建变量名)可能很危险,因为如果数据集有一个名为 X 的变量,您将覆盖程序中任何预先存在的同名变量。
另请注意,在庞大的数据集上使用 EDIT 和 READ POINT 一次更改一行将比冬天糖蜜流上山要慢。如果可能,您应该read in a big block of data,对该块中的所有行进行操作,然后写出该块。
如果磁盘 space 允许,您可能想尝试使用 SETIN 和 SETOUT 语句 read from one data set while writing to another。那会 完全消除了对 REPLACE 语句和 VALSET 调用的需要。一般来说,以只读方式打开数据集并按顺序处理它比打开它read/write并使用随机访问处理它更有效。