基于多个条件如何累加(平均)数据

4

我有一组数据,其中我记录了每组三个读数的值(以便能够获得SEM的概括性想法)。我将它们记录在一个列表中,看起来像下面这样,我正在尝试将其折叠为每组三个点的平均值:

Original Table

我希望将每3行数据折叠成一行,并提供该组的平均数据值。实际上,它看起来应该像这样:

Desired result

我基本上知道如何在Excel中完成这个任务(即使用数据透视表),但我不确定如何在MATLAB中完成相同的任务。我尝试使用accumarray,但是在实际操作中很难将多个条件整合起来。我需要创建一个subs数组,其中每个数字对应于每组唯一的3个数据点。通过暴力方法,我可以创建这样一个数组:

subs = [1 1 1; 2 2 2; 3 3 3; 4 4 4; ...]'

我希望你能使用一些循环并将其作为我的subs数组,但由于它与数据本身没有关联,并且可能会出现奇怪的问题(即每组超过3个数据点或缺少数据等),所以需要某种方式来进行类似于透视表的分组。但需要一些帮助才能启动它。谢谢。
以下是文本形式的输入数据:
Subject  Flow   On/Off   Values
1        10     1        2.20
1        10     1        2.50
1        10     1        2.60
1        20     1        5.50
1        20     1        6.10
1        20     1        5.90
1        30     1        10.10
1        30     1        10.50
1        30     1        10.50
1        10     0        1.90
1        10     0        2.20
1        10     0        2.30
1        20     0        5.20
1        20     0        5.80
1        20     0        5.60
1        30     0        9.80
1        30     0        10.20
1        30     0        10.20
2        10     1        5.70
2        10     1        6.00
2        10     1        6.10
2        20     1        9.00
2        20     1        9.60
2        20     1        9.40
2        30     1        13.60
2        30     1        14.00
2        30     1        14.00
2        10     0        5.40
2        10     0        5.70
2        10     0        5.80
2        20     0        8.70
2        20     0        9.30
2        20     0        9.10
2        30     0        13.30
2        30     0        13.70
2        30     0        13.70

请您也将输入数据以文本形式粘贴一下好吗?另外,您是如何得到例如输出2.13的结果的呢?那里平均了哪些数字? - Luis Mendo
嗨@LuisMendo,感谢您查看这个问题。我刚刚上传了文本数据。 - teepee
4个回答

4
你可以这样使用 uniqueaccumarray 来维护数据行的顺序:
[newData, ~, subs] = unique(data(:, 1:3), 'rows', 'stable');
newData(:, 4) = accumarray(subs, data(:, 4), [], @mean);

newData =

    1.0000   10.0000    1.0000    2.4333
    1.0000   20.0000    1.0000    5.8333
    1.0000   30.0000    1.0000   10.3667
    1.0000   10.0000         0    2.1333
    1.0000   20.0000         0    5.5333
    1.0000   30.0000         0   10.0667
    2.0000   10.0000    1.0000    5.9333
    2.0000   20.0000    1.0000    9.3333
    2.0000   30.0000    1.0000   13.8667
    2.0000   10.0000         0    5.6333
    2.0000   20.0000         0    9.0333
    2.0000   30.0000         0   13.5667

谢谢 @gnovice 的帮助,这真的很棒。我还想知道:如果数据中的某一列是另一种数据类型(例如日期单元格),因此无法合并到“data”数组中,该怎么办呢?就像在这个例子中一样。 - teepee
@teepee:如果你有一个日期,你可以使用datenum将其转换为数值。然后你可以将它作为另一列添加到data中。 - gnovice
啊,好的,这就是做法。太棒了,非常感谢。我已经通过将数据编译成“表格”来实现了一个解决方法,因为“unique()”接受这种类型并可以输出我需要的“subs”。作为替代方案,这种方法有什么特别的缺点吗? - teepee
@teepee:不,表格是收集和展示不同类型数据的好方法。 - gnovice

3

我假设

  • 您想基于前三列的唯一值进行平均(不是基于每三行一组进行平均,尽管在您的示例中这两个标准重合);
  • 顺序由第1列、第3列和第2列确定。

然后,将您的数据表示为x

[~, ~, subs] = unique(x(:, [1 3 2]), 'rows', 'sorted');
result = accumarray(subs, x(:,end), [], @mean);

提供

result =
    2.1333
    5.5333
   10.0667
    2.4333
    5.8333
   10.3667
    5.6333
    9.0333
   13.5667
    5.9333
    9.3333
   13.8667

如您所见,我正在使用带有'rows''sorted'选项的unique的第三个输出。这将根据您数据的前三列以所需的顺序创建subs分组向量。然后,将其传递给accumarray来计算平均值。


谢谢您的回答!使用“sorted”类型相对于使用“stable”类型有什么优势? - teepee
没有使用 'sorted',你将无法按照你在问题中指定的顺序得到结果。 - Luis Mendo
1
啊,好的,非常感谢;这是一种非常清晰简洁的方法。谢谢! - teepee
我也在想,@LuisMendo,如果我要包括另一列 - 例如日期值 - 我该如何执行相同的过程,因为我无法将日期列与其余数据合并到一个数组中? - teepee

0

accumarray 确实是正确的方法。首先,您需要使用 unique 为每组值分配一个索引:

[unique_subjects, ~, ind_subjects] = unique(vect_subjects);
[unique_flows, ~, ind_flows] = unique(vect_flows);
[unique_on_off, ~, ind_on_off] = unique(vect_on_off);

所以基本上,你现在得到了ind_subjectsind_flowsind_on_off,它们都是[1..2][1..3][1..2]中的值。

现在,你可以计算一个[3x2x2] 数组的平均值(在你的例子中):

mean_values = accumarray([ind_flows, ind_on_off, ind_subjects], vect_values, [], @mean);
mean_values = mean_values(:);

注意:顺序根据您的示例设置。

然后,您可以构建摘要:

[ind1, ind2, ind3] = ndgrid(1:numel(unique_flows), 1:numel(unique_on_off), 1:numel(unique_subjects));
flows_summary = unique_flows(ind1(:));
on_off_summary = unique_on_off(ind2(:));
subjects_summary = unique_subjects(ind3(:));

注意: 也适用于非数字值。


0

你还应该尝试查看findgroupssplitapply参考页面。在这里使用它们的最简单方法可能是将您的数据放入表格中:

 >> T = array2table(data, 'VariableNames', { 'Subject', 'Flow', 'On_Off', 'Values'});
 >> [gid,Tgrp] = findgroups(T(:,1:3));
 >> Tgrp.MeanValue = splitapply(@mean, T(:,4), gid)
 Tgrp =
   12×4 table
     Subject    Flow    On_Off    MeanValue
     _______    ____    ______    _________
     1          10      0         2.1333   
     1          10      1         2.4333   
     1          20      0         5.5333   
     1          20      1         5.8333   
     1          30      0         10.067   
     1          30      1         10.367   
     2          10      0         5.6333   
     2          10      1         5.9333   
     2          20      0         9.0333   
     2          20      1         9.3333   
     2          30      0         13.567   
     2          30      1         13.867   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接