【问题标题】:How to select rows from matrix with unique entry in specific column?如何从特定列中具有唯一条目的矩阵中选择行?
【发布时间】:2011-08-31 09:17:41
【问题描述】:

我尝试使用功能性方式解决此问题,但没有取得太大成功。

假设有一个列表列表,只需要选择其中在特定位置具有唯一条目的列表。

例如,假设有一个矩阵,我们只想选择第一列中具有唯一元素的行。

这是一个例子:

输入:

list= {{ 1,2}, {1,3},{4,5}}

我希望输出是

list={{1,2},{4,5}}

删除哪个“行”无所谓,第一个可以,但是任何一个都可以。

我尝试了 Select、DeleteCases、DeleteDuplicates、Union 和其他一些东西,但无法正常工作。我不知道如何告诉 Mathematica 只寻找“独特”元素。联盟接近但它适用于完整的列表。即我不知道要为标准写什么,如

DeleteDuplicates[list, <now what?> ]

作为参考,这是我在 Matlab 中执行上述操作的方式:

EDU>> A=[1 2;1 3;4 5]

A =
     1     2
     1     3
     4     5

EDU>> [B,I,J]=unique(A(:,1));
EDU>> A(I,:)

ans =
     1     3
     4     5

谢谢

【问题讨论】:

    标签: wolfram-mathematica


    【解决方案1】:

    这是一种方法:

    DeleteDuplicates[list, First@#1 === First@#2 &]
    

    编辑

    请注意,下面的时间安排和讨论基于 M7

    经过一番思考,我找到了一个解决方案,对于大型列表来说,它(至少)会快一个数量级,有时会快两个数量级,对于这种特殊情况(可能,更好的说法是下面的解决方案将具有不同的计算复杂度):

    Clear[delDupBy];
    delDupBy[nested_List, n_Integer] :=
      Module[{parts = nested[[All, n]], ord, unpos},
        ord = Ordering[parts];
        unpos = Most@Accumulate@Prepend[Map[Length, Split@parts[[ord]]], 1];
        nested[[Sort@ord[[unpos]]]]];
    

    基准测试:

    In[406]:= 
    largeList = RandomInteger[{1,15},{50000,2}];
    
    In[407]:= delDupBy[largeList,1]//Timing
    Out[407]= {0.016,{{13,4},{12,1},{1,6},{6,13},{10,12},{7,15},{8,14},
                {14,4},{4,1},{11,9},{5,11},{15,4},{2,7},{3,2},{9,12}}}
    
    In[408]:= DeleteDuplicates[largeList,First@#1===First@#2&]//Timing
    Out[408]= {1.265,{{13,4},{12,1},{1,6},{6,13},{10,12},{7,15},{8,14},{14,4},
          {4,1},{11,9},{5,11},{15,4},{2,7},{3,2},{9,12}}}
    

    这特别值得注意,因为DeleteDuplicates 是一个内置函数。我可以盲目猜测DeleteDuplicates 与用户定义的测试使用的是二次时间成对比较算法,而delDupBy 在列表大小中是n*log n

    我认为这是一个重要的教训:在使用自定义测试时应该注意内置函数,例如UnionSortDeleteDuplicates 等。我在 this Mathgroup 线程中更广泛地讨论了它,其中还有其他有见地的回复。

    最后,让我提一下,在here 之前已经提出过这个问题(强调效率)。我将在这里重现我为第一个(或通常为n-th)元素是正整数(推广到任意整数很简单)的情况给出的解决方案。:

    Clear[sparseArrayElements];
    sparseArrayElements[HoldPattern[SparseArray[u___]]] := {u}[[4, 3]]
    
    Clear[deleteDuplicatesBy];
    Options[deleteDuplicatesBy] = {Ordered -> True, Threshold -> 1000000};
    deleteDuplicatesBy[data_List, n_Integer, opts___?OptionQ] := 
      Module[{fdata = data[[All, n]], parr, 
      rlen = Range[Length[data], 1, -1], 
      preserveOrder =  Ordered /. Flatten[{opts}] /. Options[deleteDuplicatesBy], 
      threshold =  Threshold /. Flatten[{opts}] /. Options[deleteDuplicatesBy], dim},
      dim = Max[fdata];
      parr = If[dim < threshold, Table[0, {dim}], SparseArray[{}, dim, 0]];
      parr[[fdata[[rlen]]]] = rlen;
      parr = sparseArrayElements@If[dim < threshold, SparseArray@parr, parr];
      data[[If[preserveOrder, Sort@parr, parr]]]
    ];
    

    它的工作方式是使用第一个(或通常是n-th)元素作为某些位置 我们预先分配的巨大表,利用它们是正整数)。在某些情况下,这可以给我们带来疯狂的表现。观察:

    In[423]:= hugeList = RandomInteger[{1,1000},{500000,2}];
    
    In[424]:= delDupBy[hugeList,1]//Short//Timing
    Out[424]= {0.219,{{153,549},{887,328},{731,825},<<994>>,{986,150},{92,581},{988,147}}}
    
    In[430]:= deleteDuplicatesBy[hugeList,1]//Short//Timing
    Out[430]= {0.032,{{153,549},{887,328},{731,825},<<994>>,{986,150},{92,581},{988,147}}}
    

    【讨论】:

    • 哇,太棒了!我不知道标准可以应用于“整个”列表,我认为它在迭代时一次只适用于特定的“元素”。学到了一些新东西。谢谢
    • 也许UnionSameTest 在某些情况下也有用?例如Union[{{1, 3}, {1, 2}, {4, 5}}, SameTest -&gt; (First@#1 == First@#2 &amp;)]。不过,我更喜欢 Leonid 的方法。
    • @TomD UnionSameTest 对于较大的列表也可能会出现严重的性能问题,请参阅我的 cmets 以及编辑中的一些相关过去讨论的链接。
    • @Nasser 标准适用于在单次比较期间比较的两个元素(标准的应用),而不是整个列表。在这种情况下,元素本身恰好是列表(主列表的子列表),因此我们需要提取相关部分以用于我们的标准。
    【解决方案2】:

    Leonid 提供了一个冗长而彻底的答案,他经常这样做。但是,我认为值得指出的是,可以通过以下方式获得高效而简洁的解决方案:

    First /@ GatherBy[hugeList, #[[1]] &]

    1 是要比较的列索引。

    在我的系统上,这比 delDupBy 快,但不如 deleteDuplicatesBy 快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      相关资源
      最近更新 更多