按字段名和一些非顺序索引选择一个结构元素

Pick a struct element by field name and some non-sequential index

我的意思是用一个结构来保存一个“table”:

% Sample data
% idx  idxstr  var1  var2  var3
%   1    i01    3.5  21.0   5
%  12    i12    6.5   1.0   3

第一行包含字段名称。 假设我创建了一个结构

ds2 = struct( ...
    'idx', { 1, 12 }, ...
    'idxstr', { 'i01', 'i12' }, ...
    'var1', { 3.5, 6.5 }, ...
    'var2', { 21, 1 }, ...
    'var3', { 5, 3 } ...
);

如何检索字段 var2 的值,对应于 idxstr 等于 'i01' 的行?

备注:

  1. 我无法确保 idxstr 个元素的长度始终为 3。
  2. 理想情况下,我会有一种方法也适用于包含字符串或任何其他类型变量的列 var2

PS:我认为 可以提供帮助。

假设 idxstr 可以超过 3 个字符(它总是 3 个字符有一个更短的版本),这就是我想出的(在 MATLAB 上测试过):

logical_index=~cellfun(@isempty,strfind({ds2(:).idxstr},'i01'))

您可以通过以下方式访问变量:

ds2(~cellfun(@isempty,strfind({ds2(:).idxstr},'i01'))).var2;
% using above variable
ds2(logical_index).var2;

你现在可以理解为什么MATLAB引入表了嘿嘿。

我整理了函数

function el = struct_pick(s, cdata, cnames, rname)
    % Pick an element from a struct by column and row name
    coldata = vertcat(s.(cdata));
    colnames = mat2cell(vertcat(s.(cnames)), ones(1, length(s)));
    % This assumes rname is a string
    flt = strcmp(colnames, rname);
    el = coldata(logical(flt));
endfunction

调用
% Pick an element by column and row name
cdata = 'var3';
cnames = 'idxstr';
rname = 'i01';
elem = struct_pick(ds2, cdata, cnames, rname);

它似乎可以完成这项工作。 我不知道这是否是一种不必要的人为做法。

仍然需要处理行名不是字符串的可能性,如

cnames = 'idx';
rname = 1;

EDIT:如果 idxstr 中的字符串长度不同,则会抛出 error: vertcat: cat: dimension mismatch。 Ander Biguri 的回答可以处理这种情况。

正如我在评论中提到的,我相信您对这项工作的结构类型有误。您应该拥有一个包含 'array' 字段的结构,而不是一组(有效的单行)结构。 (数字或单元格,视情况而定)。

例如

d = struct(
   'idx', [1, 12 ],
   'idxstr', {{'i01', 'i12'}},
   'var1', [3.5, 6.5],
   'var2', [21, 1],
   'var3', [5, 3]
);

有了这个结构,你的问题就变得容易处理了:

d.var2( strcmp( 'i01', d.idxstr ) )
% ans = 21

这也比 R / pandas 数据帧功能更具可比性(也可以通过名称和类似大小的数组有效地初始化)。


PS。请仔细注意用于 'idxstr' 字段的语法:有一个包含单个元素的 'outer' 元胞数组,这意味着您只创建一个结构,而不是结构数组。这个单个元素恰好是一个字符串元胞数组,其中此元胞数组与数值数组大小相同(即 'rows' 的数量相同)。

更新

作为对评论的回应,添加 'rows' 应该相当简单。这是一种方法:

function S = addrow( S, R )
    FieldNames = fieldnames( S ).';   NumFields  = length( FieldNames );
    for i = 1 : NumFields,
        S.( FieldNames{i} ) = horzcat( S.( FieldNames{i} ), R{i} );
    end
end

那么你可以简单地做:

d = addrow( d, {5, 'i011', 2.7, 10, 11} );

也许您可以使用 strcmp

尝试下面的代码
>> [ds2.var2](strcmp('i01',{ds2.idxstr}))
ans = 21