将find与struct结合使用

Cap*_*rog 29 matlab struct find

我有一个包含数千个数据样本的结构.每个数据点包含多个对象.例如:

Structure(1).a = 7
Structure(1).b = 3
Structure(2).a = 2
Structure(2).b = 6
Structure(3).a = 1
Structure(3).b = 6
...
... (thousands more)
...
Structure(2345).a = 4
Structure(2345).b = 9
Run Code Online (Sandbox Code Playgroud)

... 等等.

如果我想找到包含数字6的所有'.b'对象的索引号,我希望以下函数可以解决这个问题:

find(Structure.b == 6)
Run Code Online (Sandbox Code Playgroud)

......我希望答案包含'2'和'3'(对于上面显示的输入).

但是,这不起作用.什么是正确的语法和/或我可以首先以更合理的方式安排我的数据?

Eit*_*n T 24

语法Structure.b为结构的数组给你一个逗号分隔的列表,所以你必须将它们串联所有的(例如,使用括号[]),以获得一个向量:

find([Structure.b] == 6)
Run Code Online (Sandbox Code Playgroud)

对于上面显示的输入,结果如预期:

ans =
     2     3
Run Code Online (Sandbox Code Playgroud)

正如Jonas所指出的,只有在没有包含空矩阵的字段时才会起作用,因为空矩阵不会反映在连接结果中.

处理具有空字段的结构

如果您怀疑这些字段可能包含空矩阵,请将它们转换为NaNs(如果可能的话......)或考虑使用Rody建议的更安全的解决方案之一.

另外,我已经想到了使用字符串的另一个有趣的解决方法.我们可以将所有内容连接成一个分隔的字符串,以保存有关空字段的信息,然后将其标记回来(在我看来,这在MATLAB中比在处理存储在单元格中的数值更容易完成).

受Jonas评论的启发,我们可以将空字段转换成NaN如下:

str = sprintf('%f,', Structure.b)
B = textscan(str, '%f', 'delimiter', ',', 'EmptyValue', NaN)
Run Code Online (Sandbox Code Playgroud)

这允许您申请find以下内容B:

find(B{:} == 6)

ans =
     2
     3
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这将返回空字段之后的字段的错误索引. (3认同)

Rod*_*uis 9

基于EitanT对Jonas评论的回答,一种更安全的方式可能是

>> S(1).a = 7;
   S(1).b = 3;
   S(2).a = 2;
   S(2).b = 6;
   S(3).a = 1;
   S(3).b = [];
   S(4).a = 1;
   S(4).b = 6;

>> find( cellfun(@(x)isequal(x,6),{S.b}) )
ans =
     2     4
Run Code Online (Sandbox Code Playgroud)

虽然它可能不是很快(与EitanT的版本相比),所以只在需要时使用它.


Rod*_*uis 9

这个问题的另一个答案!这次,我们将比较以下4种方法的性能:

  1. 我原来的方法
  2. EitanT的原始方法(不处理emtpies)
  3. EitanT使用字符串的改进方法
  4. 一种新方法:一个简单的for循环
  5. 另一种新方法:矢量化,emtpy-safe版本

测试代码:

% Set up test
N = 1e5;

S(N).b = [];
for ii = 1:N
    S(ii).b = randi(6); end

% Rody Oldenhuis 1
tic
sol1 = find( cellfun(@(x)isequal(x,6),{S.b}) );
toc

% EitanT 1
tic
sol2 = find([S.b] == 6);
toc

% EitanT 2
tic
str = sprintf('%f,', S.b);
values = textscan(str, '%f', 'delimiter', ',', 'EmptyValue', NaN);
sol3 = find(values{:} == 6);
toc


% Rody Oldenhuis 2
tic
ids = false(N,1);
for ii = 1:N
    ids(ii) = isequal(S(ii).b, 6);
end
sol4 = find(ids);
toc

% Rody Oldenhuis 3
tic
idx = false(size(S));
SS = {S.b};
inds = ~cellfun('isempty', SS);
idx(inds) = [SS{inds}]==6;
sol5 = find(idx);
toc

% make sure they are all equal
all(sol1(:)==sol2(:))
all(sol1(:)==sol3(:))
all(sol1(:)==sol4(:))
all(sol1(:)==sol5(:))
Run Code Online (Sandbox Code Playgroud)

我工作的机器上的结果(AMD A6-3650 APU(4核),4GB RAM,Windows 7 64位):

Elapsed time is 28.990076 seconds. % Rody Oldenhuis 1 (cellfun)
Elapsed time is 0.119165 seconds.  % EitanT 1 (no empties)
Elapsed time is 22.430720 seconds. % EitanT 2 (string manipulation)
Elapsed time is 0.706631 seconds.  % Rody Oldenhuis 2 (loop)
Elapsed time is 0.207165 seconds.  % Rody Oldenhuis 3 (vectorized)

ans =
     1
ans =
     1
ans =
     1
ans =
     1
Run Code Online (Sandbox Code Playgroud)

在我的Homebox上(AMD Phenom(tm)II X6 1100T(6核),16GB RAM,Ubuntu64 12.10):

Elapsed time is 0.572098 seconds.  % cellfun
Elapsed time is 0.119557 seconds.  % no emtpties
Elapsed time is 0.220903 seconds.  % string manipulation
Elapsed time is 0.107345 seconds.  % loop
Elapsed time is 0.180842 seconds.  % cellfun-with-string
Run Code Online (Sandbox Code Playgroud)

得爱JIT :)

哇...有谁知道为什么这两个系统的行为如此不同?

此外,鲜为人知的事实 - cellfun其中一个可能的字符串参数非常快(这表明匿名函数需要多少开销......).

尽管如此,如果你可以绝对确定没有空箱,那么请选择EitanT的原始答案; 这就是Matlab的用途.如果你不能确定,那就去循环吧.

  • +1:我总是低估那些讨厌的循环! (2认同)