使用实验室数据,我想在按处理分组并按时间点排序的箱线图上叠加数据点的子集。在 SAS 中将所有元素组合在一起并不简单,需要一种我无法设计或发现自己的聪明方法:)

所需图的美妙之处在于它显示了两种不同类型的异常值:

  • 箱线图包括统计异常值 - 方形标记 (1.5 IQR)
  • 然后覆盖“正常范围”异常值的标记 - 临床定义,特定于每个实验室测试。

  • 在对数据进行分组(例如,按治疗)然后按另一个变量(例如,时间点)进行分组或分类时,这很困难。 SAS 在内部确定箱线图的间距,因此对于叠加的正常范围数据标记,很难模拟此间距。在这个方向上的通用解决方案将是一个不可靠的混搭。

    我在下面演示了这种手动模拟叠加标记的组分离的方法——只是为了给出一个意图的概念。正如预期的那样,正常范围异常值与箱线图组不一致。此外,同时满足异常值标准(统计和临床)的数据点显示为单独的点,而不是具有重叠标记的单个点。我的绿色注释:

    SGPLOT-overlay-fail

    是否有一种简单、可靠的方法来指示 SAS 在箱线图上叠加分组数据点,使所有内容按预期对齐?

    这是重现未命中的代码:
    proc sql;
      create table labstruct
        (  mygroup         char(3) label='Treatment Group'
         , myvisitnum      num     label='Visit number'
         , myvisitname     char(8) label='Visit name'
         , labtestname     char(8) label='Name of lab test'
         , labseed         num     label='Lab measurement seed'
         , lablow          num     label='Low end of normal range'
         , labhigh         num     label='High end of normal range'
        )
      ;
      insert into labstruct
        values('A', 1,  'Day 1',  'Test XYZ', 48, 40, 60)
        values('A', 5,  'Week 1', 'Test XYZ', 50, 40, 60)
        values('A', 10, 'Week 2', 'Test XYZ', 52, 40, 60)
        values('B', 1,  'Day 1',  'Test XYZ', 52, 40, 60)
        values('B', 5,  'Week 1', 'Test XYZ', 50, 40, 60)
        values('B', 10, 'Week 2', 'Test XYZ', 48, 40, 60)
      ;
    quit;
    
    data labdata;
      set labstruct;
    
      * Put normal range outliers on 2nd axis, manually separate groups on 2nd axis *;
      select (mygroup);
        when ('A') scatternum = myvisitnum - 1;
        when ('B') scatternum = myvisitnum + 1;
        otherwise;
      end;
    
      * Make more obs from the seeds above *;
      label labvalue = 'Lab measurement';
      do repeat = 1 to 20;
        labvalue = labseed + 6*rannor(3297);
    
        * Scatter plot ONLY normal range outliers *;
        if labvalue < lablow or labvalue > labhigh
           then scattervalue = labvalue;
        else scattervalue = .;
    
        output;
      end;
      drop repeat labseed;
    run;
    
    proc sgplot data=labdata;
      block x=myvisitnum block=myvisitname /
            nofill
            lineattrs=(color=lightgray);
      vbox labvalue /
           category=myvisitnum
           group=mygroup
           outlierattrs=(symbol=square);
      scatter x=scatternum y=scattervalue /
           group=mygroup
           x2axis
           jitter;
      x2axis display=none;
      keylegend / position=bottom type=marker;
    run;
    

    最佳答案

    所以 - 我认为这里有一个解决方案,但我不确定它有多普遍。当然,它只适用于两元素箱线图。

    您现在遇到的问题是散点图的轴类型默认是线性的,而不是离散的,而箱线图默认是离散的。如果您以这种方式设置它,这总是会很困惑,尽管理论上您可以计算出确切的差异并绘制它。您也可以使用 annotate 工具,尽管它会遇到同样的问题。

    但是,如果您将散点图设置为使用离散轴,则可以使用 discreteoffset 选项使事物正确排列 - 或多或少。不幸的是,没有办法在散点图上使用 group 来告诉 SAS 在适当的箱线图上放置适当的标记,所以默认情况下一切都在离散轴的中心;所以你需要在这里使用两个单独的图,一个用于 a ,一个用于 b ,一个带有负偏移量,一个带有正偏移量。
    discreteoffset 的优点是对于任何两组箱线图它应该是一个常数值,除非您对箱线宽度进行了一些改动;无论实际图有多大,离散偏移量都应该相同(因为它是分配给该值的块总宽度的百分比)。

    这里要考虑的一些事情包括在你的箱线图中有六个元素而不是三个(所以去掉 group 并且只有六个不同的 visnum 值,a_1 b_1 等);这将保证每个箱线图都以离散轴的中心为中心(那么您的散点图将具有 0 离散偏移)。您也可以考虑滚动自己的箱线图;例如,计算您自己的 IQR,然后使用高低图绘制框并通过注释绘制 mustache ,然后散点图所有不同的异常值(不仅仅是您的“正常”异常值)。

    这是似乎适用于您的特定示例的代码,并且希望适用于大多数类似的情况(有两个条)。对于 3 个条形图,它可能也很容易(1 个条形图的偏移量为 0,其他两个条形可能在 +/- 0.25 左右)。除此之外,您开始必须进行更多计算才能确定框的位置,但总体而言 SAS 将非常擅长将它们平均间隔开,因此通常相当简单。

    proc sql;
      create table labstruct
        (  mygroup         char(3) label='Treatment Group'
         , myvisitnum      num     label='Visit number'
         , myvisitname     char(8) label='Visit name'
         , labtestname     char(8) label='Name of lab test'
         , labseed         num     label='Lab measurement seed'
         , lablow          num     label='Low end of normal range'
         , labhigh         num     label='High end of normal range'
        )
      ;
      insert into labstruct
        values('A', 1,  'Day 1',  'Test XYZ', 48, 40, 60)
        values('A', 5,  'Week 1', 'Test XYZ', 50, 40, 60)
        values('A', 10, 'Week 2', 'Test XYZ', 52, 40, 60)
        values('B', 1,  'Day 1',  'Test XYZ', 52, 40, 60)
        values('B', 5,  'Week 1', 'Test XYZ', 50, 40, 60)
        values('B', 10, 'Week 2', 'Test XYZ', 48, 40, 60)
      ;
    quit;
    
    data labdata;
      set labstruct;
    
      * Put normal range outliers on 2nd axis, manually separate groups on 2nd axis *;
      select (mygroup);
        when ('A') a_scatternum = myvisitnum;  /* Note the separate names now, but no added +/- 1 */
        when ('B') b_scatternum = myvisitnum;
        otherwise;
      end;
    
      * Make more obs from the seeds above *;
      label labvalue = 'Lab measurement';
      do repeat = 1 to 20;
        labvalue = labseed + 6*rannor(3297);
    
        * Scatter plot ONLY normal range outliers *;
        if labvalue < lablow or labvalue > labhigh
           then scattervalue = labvalue;
        else scattervalue = .;
    
        output;
      end;
      drop repeat labseed;
    run;
    
    proc sgplot data=labdata noautolegend;  /* suppress auto-legend */
      block x=myvisitnum block=myvisitname /
            nofill
            lineattrs=(color=lightgray);
      vbox labvalue /
           category=myvisitnum
           group=mygroup
           outlierattrs=(symbol=square) name="boxplot"; /* Name for keylegend */
      scatter x=a_scatternum y=scattervalue /     /* Now you have two of these - and no need for an x2axis */
           group=mygroup discreteoffset=-0.175
            jitter
           ;
      scatter x=b_scatternum y=scattervalue /
           group=mygroup discreteoffset=0.175
            jitter
           ;
      keylegend "boxplot" / position=bottom type=marker;  /* Needed to make a custom keylegend or else you have a mess with three plots in it */
    run;
    

    关于sas - 按组绘制的箱线图,加上用户定义的散点图(值子集的标记),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34398118/

    10-12 19:13