29

何千ものデータのサンプルを保持する構造体があります。各データポイントには複数のオブジェクトが含まれています。例えば:

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

... 等々。

番号6を含むすべての「.b」オブジェクトのインデックス番号を検索したい場合は、次の関数でうまくいくと思います。

find(Structure.b == 6)

...そして私は答えが「2」と「3」を含むことを期待します(上記の入力の場合)。

ただし、これは機能しません。正しい構文は何ですか、および/または最初にデータをより論理的な方法で配置できますか?

4

3 に答える 3

25

Structure.b構造体の配列の構文では、コンマで区切られたリスト[]が得られるため、ベクトルを取得するには、それらすべてを連結する必要があります(たとえば、角かっこを使用)。

find([Structure.b] == 6)

上記の入力の場合、結果は期待どおりです。

ans =
     2     3

Jonasが指摘したように、これは空の行列を含むフィールドがない場合にのみ機能します。これは、空の行列が連結結果に反映されないためです。

空のフィールドを持つ構造体の処理

これらのフィールドに空の行列が含まれている可能性がある場合は、それらをNaNsに変換するか(可能であれば...)、Rodyが提案するより安全なソリューションの1つを使用することを検討してください。

さらに、文字列を使用したこのための別の興味深い回避策を考えました。すべてを区切り文字列に連結して空のフィールドに関する情報を保持し、それをトークン化して戻すことができます(これは、セルに格納された数値を処理するよりも、MATLABで実行する方が簡単です)。

NaNJonasのコメントに触発されて、空のフィールドを次のように変換できます。

str = sprintf('%f,', Structure.b)
B = textscan(str, '%f', 'delimiter', ',', 'EmptyValue', NaN)

findこれにより、次のコンテンツに適用できますB

find(B{:} == 6)

ans =
     2
     3
于 2013-01-23T13:31:45.280 に答える
9

Jonasのコメントを使用したEitanTの回答に基づいて、より安全な方法が可能になります。

>> 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

ただし、(EitanTのバージョンと比較して)おそらくそれほど高速ではないため、必要な場合にのみこれを使用してください。

于 2013-01-23T13:49:44.280 に答える
9

この質問に対する別の答え!今回は、次の4つの方法のパフォーマンスを比較します。

  1. 私のオリジナルの方法
  2. EitanTの元の方法(空を処理しない)
  3. 文字列を使用したEitanTの改善された方法
  4. 新しい方法:単純なforループ
  5. 別の新しい方法:ベクトル化された、空に安全なバージョン

テストコード:

% 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(:))

仕事中の私のマシンでの結果(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

私のホームボックス(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

そのJITが大好きです:)

そしてすごい...2つのシステムがなぜそんなに異なって振る舞うのか誰もが知っていますか?

また、ほとんど知られていない事実-cellfun可能な文字列引数の1つを使用すると、信じられないほど高速になります(これは、匿名関数が必要とするオーバーヘッドの量を示します...)。

それでも、空がないことを絶対に確信できる場合は、EitanTの元の答えを探してください。それがMatlabの目的です。確信が持てない場合は、ループに進んでください。

于 2013-01-23T16:32:04.637 に答える