使用unique()函数在MATLAB中重构向量

3
假设一个200项的单元格数组 A 包含4种不同的字符串(每种重复50次)。 B 是一个包含整数的200项向量。
使用 [cellNos cellStartInd enumCells ] = unique(A) 得到哪些 A 中的项目等于唯一字符串之一(enumCells 是包含整数1-4的数组,枚举了这些字符串)。
我想利用这些信息创建一个4x50的值矩阵以提取 B 中与每个唯一字符串相关的值。换句话说,我想将 B 重新整形成一个矩阵,其中的列是按照A中每个唯一字符串排列的。
4个回答

4

假设您已经知道有多少次重复,并且所有字符串的重复频率相等,您可以执行以下操作:

%# sort to find where the entries occur (remember: sort does stable sorting)
[~,sortIdx] = sort(enumCells);

%# preassign the output to 50-by-4 for easy linear indexing
newB = zeros(50,4);

%# fill in values from B: first the 50 ones, then the 50 2's etc
newB(:) = B(sortIdx);

%# transpose to get a 4-by-50 array
newB = newB';

或者,更加紧凑地表达(感谢 @Rich C)。
[~,sortIdx] = sort(enumCells);
newB = reshape(B(sortIdx),50,4)';

1
+1 当你已经了解重复的情况时,这是最佳解决方案。你可以将最后三行代码简化为:newB = reshape(B(sortIdx), [50 4]); - Rich C
@Rich C:感谢您的建议!顺便折叠最后一行,您的解决方案需要额外进行转置。 - Jonas

3

对于一般情况,如果您有 N 个不同的字符串,并且每个字符串出现的次数不同 M_i,那么 B 中每组相应的值将具有不同的长度,您将无法将这些集合连接成数字数组。相反,您需要将这些集合存储在一个由 N 个元素组成的 单元格数组 中,并且可以使用函数 UNIQUEACCUMARRAY 来完成此操作:

>> A = {'a' 'b' 'b' 'c' 'a' 'a' 'a' 'c' 'd' 'b'};  %# Sample array A
>> B = 1:10;                                       %# Sample array B
>> [uniqueStrings,~,index] = unique(A)
>> associatedValues = accumarray(index(:),B,[],@(x) {x})

associatedValues = 

    [4x1 double]    %# The values 1, 5, 6, and 7
    [3x1 double]    %# The values 2, 3, and 10
    [2x1 double]    %# The values 4 and 8
    [         9]    %# The value 9

在每个字符串出现次数相同的特定情况下,上述代码仍然可以正常工作,并且您可以选择将输出从单元数组转换为所需的数字数组,如下所示:
associatedValues = [associatedValues{:}];


注意: 由于ACCUMARRAY不能保证所累加的项目的相对顺序,因此associatedValues单元格中的项目顺序可能与它们在向量B中的相对顺序不匹配。确保在B中原始的相对顺序被保持的一种方法是按照以下方式修改对ACCUMARRAY的调用:

 associatedValues = accumarray(index(:),1:numel(B),[],@(x) {B(sort(x))});

或者您可以对ACCUMARRAY的输入进行排序,以获得相同的效果:

[index,sortIndex] = sort(index);
associatedValues = accumarray(index(:),B(sortIndex),[],@(x) {x});

+1 很好的再次使用了 accumarray!说实话,在我想到 sort 的方法之前,我曾尝试过但失败了。 - Jonas
@Jonas:对于所有字符串出现次数相同的简单情况,你的解决方案绝对是正确的选择。 ;) - gnovice

0

如果我正确理解了你的问题,可以使用查找函数来完成。 http://www.mathworks.com/help/techdoc/ref/find.html

要创建您的矩阵,只需编写:

M(:,1) = B(find(enumCells==1));
M(:,2) = B(find(enumCells==2));
M(:,3) = B(find(enumCells==3));
M(:,4) = B(find(enumCells==4));

可能有更优雅的方法来完成它,但这应该可以工作。

编辑:您可以尝试使用“sort”来完成。 sort函数可以将排序的排列作为输出给出。 尝试:

[s perm] = sort(enumCells);
M = reshape(B(perm),50,4);

谢谢,这对于4个唯一字符串应该可行。实际上我正在寻找一种需要较少硬编码且可扩展的解决方案,因为实际问题与上述情况不同(即更加复杂)。 - aarslan

0

如果每个字符串的条目数量相同,则此方法将起作用;如果它们不同,请参考@gnovice的解决方案。

NumStrings = numel(CellNos);
M = zeros(size(B,1)/NumStrings,NumStrings);
for i = 1:NumStrings
    M(:,i) = B(strcmp(B,CellNos{i}));
end

此外,如果您事先知道唯一字符串是什么(例如 CellNos),这将允许您跳过相对较昂贵的唯一调用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接