Подтвердить что ты не робот

Использование 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

... и т.д.

Если бы я хотел найти индексный номер всех объектов .b ', содержащих число 6, я бы ожидал, что следующая функция выполнит трюк:

find(Structure.b == 6)

... и я ожидаю, что ответ будет содержать "2" и "3" (для ввода, показанного выше).

Однако это не работает. Каков правильный синтаксис и/или я мог бы упорядочить мои данные более логичным образом?

4b9b3361

Ответ 1

Синтаксис Structure.b для массива structs дает вам список, разделенный запятыми, поэтому вам придется их конкатенировать (например, используя скобки []), чтобы получить вектор:

find([Structure.b] == 6)

Для ввода, показанного выше, результат будет таким, как ожидалось:

ans =
     2     3

Как отметил Джонас, это будет работать только в том случае, если нет полей, содержащих пустые матрицы, потому что пустые матрицы не будут отражаться в результате конкатенации.

Обработка структур с пустыми полями

Если вы подозреваете, что эти поля могут содержать пустые матрицы, либо конвертируйте их в NaN (если возможно...), либо рассмотрите использование одного из более безопасных решений, предложенных Роди.

Кроме того, я подумал о другом интересном обходном пути для этого, используя строки. Мы можем объединить все в строку с разделителями, чтобы хранить информацию о пустых полях, а затем ее подделывать (это, по моему скромному мнению, легче сделать в MATLAB, чем обрабатывать числовые значения, хранящиеся в ячейках).

Вдохновленный комментарием Джонаса, мы можем преобразовать пустые поля в NaN так:

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

и это позволяет применить find к содержимому B:

find(B{:} == 6)

ans =
     2
     3

Ответ 2

Основываясь на ответе 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), поэтому используйте только при необходимости.

Ответ 3

Еще один ответ на этот вопрос! На этот раз мы сравним производительность следующих 4 методов:

  • Мой оригинальный метод
  • Исходный метод EitanT (который не обрабатывает emtpies)
  • Улучшенный метод EitanT с использованием строк
  • Новый метод: простой для цикла
  • Еще один новый метод: векторизованная, безопасная версия

Тестовый код:

% 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 ядра), 4 ГБ оперативной памяти, 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

В моем Homebox (AMD Phenom (tm) II X6 1100T (6 ядер), 16 ГБ оперативной памяти, 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:)

и ничего себе... кто-нибудь знает, почему две системы ведут себя так по-другому?

Кроме того, малоизвестный факт - cellfun с одним из возможных аргументов строки невероятно быстро (что показывает, сколько накладных расходов анонимные функции требуют...).

Тем не менее, если вы можете быть абсолютно уверены, что нет пустых тарелок, обратитесь за оригинальным ответом EitanT; для чего предназначен Матлаб. Если вы не можете быть уверены, просто займитесь циклом.