【问题标题】:Counting rows without any NaNs in a struct计算结构中没有任何 NaN 的行
【发布时间】:2016-02-02 13:48:19
【问题描述】:

我需要计算结构数组中所有字段中没有任何 NaN 的结构。示例结构如下所示:

a(1).b = 11;
a(2).b = NaN;
a(3).b = 22;
a(4).b = 33;
a(1).c = 44;
a(2).c = 55;
a(3).c = 66;
a(4).c = NaN;

输出如下所示

Fields    b     c

1         44    11
2         55    NaN
3         66    22
4         NaN   33

没有 NaN 的结构是 1 和 3,所以这里总共应该有 2 个。

我尝试使用size(a, 2),但它只是告诉我数组中的结构总数。我需要它来计算 N (样本中的观察次数)。 NaN 不算作观察,因为它们在分析中被省略了。

对结构数组中没有任何 NaN 的结构进行计数的最简单方法是什么?

【问题讨论】:

    标签: arrays matlab struct


    【解决方案1】:

    我建议使用以下单行命令:

    nnz(~any(cellfun(@isnan,struct2cell(a))))
    

    struct2cell(a) 将您的结构转换为 3D 元胞数组 cellfun(@isnan,___) 将 isnan 应用于元胞数组的每个元素 ~any(__) 沿第一维工作并返回没有 NaN 的数组 nnz(__) 计算没有 NaN 的行数

    结果只是一个数字,在本例中为2。

    以下内容:

    find(~any(cellfun(@isnan,struct2cell(a))))
    

    会告诉你哪些行没有 NaN

    【讨论】:

    • 这个太棒了!不知道哪些,但非常好 +1
    • @AnderBiguri 感谢您的评论,添加了查找行索引
    • 好建议@Daniel。
    • 我删除了我的评论,因为令我惊讶的是它变慢了。我的建议是find(~any(isnan(cell2mat(struct2cell(a)))))。
    【解决方案2】:

    这会告诉你哪些没有 NaN

    for ii=1:size(a,2)
        hasNoNaNs(ii)=~any(structfun(@isnan,a(ii)));
    end
    

    它的工作方式是遍历每个结构,并在其中的每个元素中使用structfun 调用isnan,然后检查其中的any 是否为NaN 并否定结果,因此在没有NaNs的那些中给出1

    【讨论】:

    • 我不知道它是否一定会更快,但用any 替换你的sum 确实更好。您避免将类型从逻辑切换到双精度,然后再切换回逻辑。
    • @craigim 绝对是一种更聪明的方式!感谢您的提示!
    【解决方案3】:

    因为bsxfun 永远不会是错误的方法!

    sum(all(bsxfun(@le,cell2mat(struct2cell(a)),inf)))
    

    这是如何工作的:

    这会将结构转换为单元格,然后转换为矩阵:

    cell2mat(struct2cell(a))
    ans(:,:,1) =
        11
        44
    ans(:,:,2) =
       NaN
        55
    ans(:,:,3) =
        22
        66
    ans(:,:,4) =
        33
       NaN
    

    然后它使用bsxfun 来检查哪些元素小于或等于零。唯一不满足此条件的值是NaN。

    bsxfun(@le,cell2mat(struct2cell(a)),inf)
    ans(:,:,1) =
         1
         1
    ans(:,:,2) =
         0
         1
    ans(:,:,3) =
         1
         1
    ans(:,:,4) =
         1
         0
    

    然后,我们检查每个切片中的所有值是否为真:

    all(bsxfun(@le,cell2mat(struct2cell(a)),inf))
    ans(:,:,1) =
         1
    ans(:,:,2) =
         0
    ans(:,:,3) =
         1
    ans(:,:,4) =
         0
    

    最后,我们总结一下:

    sum(all(bsxfun(@le,cell2mat(struct2cell(a)),inf)))
    ans =
         2
    

    (顺便说一句:可以跳过bsxfun,但这有什么好玩的)

    sum(all(cell2mat(struct2cell(a))<=inf))
    

    【讨论】:

      【解决方案4】:

      使用arrayfun 迭代a 和structfun 迭代字段,你会得到一个没有NaNs 的元素的逻辑数组:

      >> arrayfun(@(x) ~any(structfun(@isnan, x)), a)
      ans =
           1     0     1     0
      

      现在你可以sum它

      >> sum(arrayfun(@(x) ~any(structfun(@isnan, x)), a))
      ans =
           2
      

      【讨论】:

        【解决方案5】:

        借鉴this not working answer 的想法,使用逗号分隔的列表:

        s=sum(~any(isnan([[a.b];[a.c]])));
        

        对字段名称进行硬编码可能看起来很愚蠢,但它会导致快速编码,因为它避免了迭代和元胞数组。

        将此方法推广到任意字段名称,您最终会得到以下解决方案:

        n=true(size(a));
        for f = fieldnames(a).'
            n(isnan([a.(f{1})]))=false;
        end
        n=sum(n(:));
        

        假设您有一个只有几个字段名的大型结构,这是非常有效的,因为它只是迭代字段名。

        【讨论】:

        • 尽管我接受了 Brainkz 的回答适合这个特定问题,但我最终还是将您的解决方案用于我的真实数据集,因为我在那里有一个字符串数据类型的字段,而 struct2cell 没有为它工作。非常感谢!
        【解决方案6】:

        第三种解决方案可能 - 根据您的数据可能不优雅:

        A = [[a.b];[a.c]];  %//EDIT -- Fixed based on @Daniel's correct solution
        IndNotNaN = find (~isnan(A));
        

        取决于你是否有很多结构,你必须连接 a.b, a.c ....a.n

        【讨论】:

        • 在此处使用逗号分隔列表的好主意,但它无法正常工作。我假设您使用 size [n 1] 结构进行测试,而不是使用 [1 n] 结构。
        • @Daniel 啊,对不起,我不在了,是的,你提供的答案好多了。我使用了 [n 1] 的结构。
        猜你喜欢
        • 1970-01-01
        • 2021-06-22
        • 2020-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-16
        • 2014-03-03
        • 2020-08-21
        相关资源
        最近更新 更多