【问题标题】:aggregate by column value and paste row values together in SAS在 SAS 中按列值聚合并将行值粘贴在一起
【发布时间】:2015-04-20 17:24:06
【问题描述】:

我有一个看起来像这样的数据集:

有:

data have;

input a b c d e f g h ;

datalines;

1 0 0 0 0 0 1 0

0 0 1 0 1 0 0 0

0 0 0 1 0 1 0 0

0 1 0 0 0 0 0 1

;

run;

a、b、c 和 d 列是问题 1 的四个选项,采用 4 分制。 obs1 列 A 中的值“1”表示受访者已为该问题选择了选项 A,表示 4 分制中的 4。

a = 4,b = 3,c = 2 和 d = 1。

下一个问题的选项是 e、f、g 和 h。受访者选择了选项 g,即 4 分制中的 2。 e = 4,f = 3,g = 2 和 h = 1。

数据集包含数百个这样的列。我的想法是将 4 列合并为一列,得到如下值:“1000”、“0100”、“0010”、“0001”,然后转换 1000 = 4、0100 = 3、0010 = 2 和 0001 = 1。

我希望它是这样的:

block   col1    col2    col3    col4
1       1000    0100    0010    0001
2       0100    0010    1000    0001
3       1000    0100    1000    0010

我已经走到这一步了:

proc transpose data = have out = have_t;
run;

data have_t_block;
set have_t;
retain block;
if _n_ = 1 then block = 1;
if mod(_n_/4,1) = 0.25 and _n_ gt 1 then block +1;
run;

有没有办法在 SAS 中按块聚合时连接行值?我在 R 中这样做,如下所示:

#Create data    
data <- data.frame(a = c(1, 0, 0), b = c(0, 1, 0), c = c(0, 0, 1), d = c(0, 0, 0), e = c(0, 1, 0), f = c(1, 0, 0), g = c(0, 0, 1), h = c(0, 0, 0), i = c(0, 0, 1), j = c(1, 0, 0), k = c(0, 0, 0), l = c(0, 1, 0))

#transpose
data <- data.frame(t(data))

#create a key for each group of 4
data$block <- rep(1:(nrow(data)/4), each = 4)

#convert data to long format and group by key (block) and use paste to concatenate
require(reshape2)
data_melt <- melt(data, id = c("block"))
trial <- data.frame(t(dcast(data_melt, block ~ variable, paste, collapse = "")))

【问题讨论】:

  • 连接行值是什么意思。您的意思是将四个变量连接成一个 4 字符长的字符串吗?
  • 另外 - 至少对我来说,你如何从 HAVE 到 WANT 并不明显。有什么关系?想要的输出的哪些部分来自原始的 HAVE?
  • 好的,让我在问题中提供一些背景信息。
  • 是的,将 4 个变量连接成一个 4 字符长的字符串。我已经编辑了问题以解释这种关系。

标签: sas concatenation aggregate-functions


【解决方案1】:

首先,除非您对数据的解释有误,否则您的转置在这里并没有太大帮助,因为没有特别的理由让每个受访者都有一列 - 让我们只有一列,句号。这是一个更好的方法。

data have_t;
  set have;
  array cols a--h;
  do _i = 1 to dim(cols);
    value = cols[_i];
    output;
  end;
  keep value; *and an ID I hope?;
run;

使数据集“垂直”(一列)非常容易。只需遍历所有列的数组,为每个列设置一个公共变量到该值,输出。通常我会跟踪我输出的变量名,但也许这不是必需的。

对于您的主要问题,您想要做的是使用retain,这很可能与您处理块的方式没有什么不同。这里我只是直接计算分数:

data want;
  set have_t;
  retain score;
  counter = mod(_n_,4);
  if counter=1 then block+1; *slightly easier version of what you wrote;
  if value=1 then score = 5-counter; *first=4, second=3, third=2, fourth=1;
  if counter=0 then output;
  *We never "clear" score here - to be safer you may want to do that in the if counter=1 block;
run;

如果你想要中间的 '0010' 或其他什么,你也可以包含它。

data want;
  set have_t;
  retain score int_Value;
  length int_Value $4;
  counter = mod(_n_,4);
  if counter=1 then block+1; *slightly easier version of what you wrote;
  if value=1 then score = 5-counter; *first=4, second=3, third=2, fourth=1;
  int_value = cats(int_value,value);
  if counter=0 then do;
    output;
    int_value=' ';  *have to clear this every 4;
    score=.;  *here we might as well clear it; 
  end;
run;

【讨论】:

  • 谢谢乔。 .我希望我的输出看起来是水平的 - 列中的问题。
  • 那你需要从这个开始,做两件事。一,有一些方法来识别问题编号 - 这可能是您为每个 id 重新设置的计数器,或者您可能在转置期间输出它(假设您的变量名称可能具有内置的问题编号)。然后是两个,您需要通过您的受访者 ID 和问题回复 proc transpose,使用 id 选项 id question_var_name; 或任何名称和 by respondent_id; 或任何名称。
【解决方案2】:

如果我正确理解了你的问题,试试这个:

data want;
  do i=1 by 1 until(last.block);
  set have_t_block;
  array var $4. var1-var4;
  array col col1-col4;
  length var1-var4 $4.;
  by block notsorted;
  do over var;
  var=cats(var,col);
  end;
  if last.block then output;
  end;
  keep var: block;
run;

【讨论】:

  • 你为什么do i=1 by 1 until (last.block)?与常规 datastep 循环相比,这能为您带来什么?
  • [为了清楚起见,我并不是在争论它本身——而是要求解释。] 一般来说,没有解释的代码与没有代码的问题一样糟糕:除非它是一些非常琐碎和明显的事情,它对未来的读者的帮助几乎没有解释为什么它起作用,以及你为什么要做你所做的事情。
猜你喜欢
  • 1970-01-01
  • 2023-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-12
  • 1970-01-01
相关资源
最近更新 更多