【问题标题】:MATLAB-ish way to keep common entry rows between two cell arrays?在两个单元格数组之间保留公共条目行的 MATLAB 方式?
【发布时间】:2014-04-01 22:16:20
【问题描述】:

作为 MATLAB 的新手,我发现自己总是使用类似 C++ 的循环,而不是利用基于矩阵的操作。今天又遇到一个问题。

假设我有两个双列表(单元格数组),第一列是用户 ID,第二列是相应的值。但是,它们在行中具有不同的大小。即

Table 1

1 2.56
2 7.4
3 7.7
...
100 83.4

Table 2

1 7.1
3 1.4
4 4.4
...
76 7.2

尽管行大小不同,但这两个表实际上共享一些共同的 ID。 现在我希望形成一个大小为Nx3 的新元胞数组,其中N 是公共ID 的数量,第一列和第二列分别是表1 和表2 中的值。即

New Table

1 2.56 7.1
3 7.7 1.4
...

同样,我可以使用循环来执行此操作,但我真的很想学习一种类似于 MATLAB 的方法。

【问题讨论】:

  • 您对任何答案是否满意。我个人认为ismember 是最“MATLAB-ish”的解决方案。 ;) 我用排序数据证明它更快。
  • @chappjc 是的,我同意。太棒了!非常感谢详细的分析! :D

标签: arrays matlab


【解决方案1】:

看看ismember,它对排序输入非常有效,因为在这种情况下是索引:

A = [1 2.56;
     2  7.4;
     3  7.7];
B = [1  7.1;
     3  1.4;
     4  4.4];

[tf,locb] = ismember(A(:,1),B(:,1))

您只需要这样的两个输出:tfA 中也存在于 B 中的行的映射,locbBA 中每个元素的位置(即与A 相同的长度,如果没有匹配则为零)。

因此,一个常见的习惯用法是用tf 索引locb

>> C = [A(tf,:) B(locb(tf),2:end)]
C =
    1.0000    2.5600    7.1000
    3.0000    7.7000    1.4000

考虑对输入进行排序时ismemberintersect 之间的速度差异。小数据:

N = 1e5;  A = [(1:N).' rand(N,1)];  B = [(1:N).' rand(N,1)];


>> tic; [tf,locb] = ismember(A(:,1),B(:,1)); toc
Elapsed time is 0.013419 seconds.
>> tic; [C, ia, ib] = intersect(A(:,1),B(:,1)); toc
Elapsed time is 0.050618 seconds.

虽然ismember快了好几倍,但是对于小数据来说并没有太大的优势。但是,对于大型排序数据集,请使用ismember

N = 1e7;  A = [(1:N).' rand(N,1)];  B = [(1:N).' rand(N,1)];

>> tic; [tf,locb] = ismember(A(:,1),B(:,1)); toc
Elapsed time is 0.892977 seconds.
>> tic; [C, ia, ib] = intersect(A(:,1),B(:,1)); toc
Elapsed time is 5.925537 seconds.

注意:如果您想真正利用排序输入的先验知识,有一个undocumented function called ismembc 可以跳过对issorted 的调用,比ismember 更快.另见here

【讨论】:

    【解决方案2】:

    这可以通过非常通用的bsxfun 函数巧妙地解决:

    C1 = {1 2.56; 2 7.4; 3 7.7; 100 83.4};
    C2 = {1 7.1; 3 1.4; 4 4.4; 76 7.2}; %// example data. Two-column cell arrays
    
    comp = bsxfun(@eq, [C1{:,1}], [C2{:,1}].'); %'// test all pairs for equality
    ind1 = any(comp,1); %// values of first col of C1 that coincide with some in C2
    ind2 = any(comp,2); %// values of first col of C2 that coincide with some in C1
    result = horzcat(C1(ind1,1), C1(ind1,2), C2(ind2,2)); %// build result
    

    注意

    • [C1{:,1}] 用于将元胞数组C1 的第一列转换为(数字)行向量。 Here 的原因。
    • any 的第二个参数指定它沿哪个维度进行操作。
    • ind1ind2logical indices

    此外,Matlab 中的数值数组(矩阵)比元胞数组更有效。如果单元格数组中的所有单元格都包含一个数字,就像您的情况一样,请考虑改用数字数组。当每个单元格必须包含不同大小或不同类型的对象时(例如,如果一列包含数字而另一列包含字符串),元胞数组非常有用。

    如果使用数值数组,这种情况下代码也会稍微简单一些:

    C1 = [1 2.56; 2 7.4; 3 7.7; 100 83.4];
    C2 = [1 7.1; 3 1.4; 4 4.4; 76 7.2]; %// example data. Two-column matrices
    
    comp = bsxfun(@eq, C1(:,1).', C2(:,1)); %'// test all pairs for equality
    ind1 = any(comp,1); %// values of first col of C1 that coincide with some in C2
    ind2 = any(comp,2); %// values of first col of C2 that coincide with some in C1
    result = [C1(ind1,1) C1(ind1,2) C2(ind2,2)]; %// build result
    

    【讨论】:

      【解决方案3】:

      传统上,我认为@ysakamto 的回答是最好的。但是,如果您使用的是较新版本的 Matlab(去年?),他们添加了一个 table 数据类型,它支持 SQL 类型的操作,例如 joinhttp://www.mathworks.com/help/matlab/ref/join.html

      【讨论】:

      • ismember 对于排序输入非常有效,在这种情况下就是这样。
      • 这很有可能,当我开始写我的时,只发布了@yskamto 的答案。有趣的是这个问题的有用解决方案的数量。也许 OP 会在他的数据上实现所有这些并为我们计时?
      • 我在答案中添加了性能比较。 ismember 快了好几倍,而且随着数据变大,差异也变大了。
      • 很有趣,谢谢。我实际上是指最初的提问者,并不是要为您建议更多的工作,但是查看替代解决方案的性能数据总是非常有帮助的。感谢您发布它。
      • 我知道。毕竟我来这里是为了好玩,而不是为了高薪! ;)
      【解决方案4】:

      我个人认为使用循环很好。用intersect怎么样?

      A = [1 2.56;2 7.4;3 7.7];
      B = [1 7.1; 3 1.4;4 4.4];
      
      [C, ia, ib] = intersect(A(:,1),B(:,1));
      
      D = [A(ia,:), B(ib,2:end)]
      

      【讨论】:

        猜你喜欢
        • 2021-10-07
        • 1970-01-01
        • 1970-01-01
        • 2015-07-20
        • 1970-01-01
        • 1970-01-01
        • 2011-01-20
        • 2020-12-14
        • 1970-01-01
        相关资源
        最近更新 更多