如何从矩阵中选择具有特定列中唯一条目的行?

Nas*_*ser 4 wolfram-mathematica

我尝试使用功能方式解决这个问题,但我没有取得多大成功.

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

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

这是一个例子:

INPUT:

list= {{ 1,2}, {1,3},{4,5}}
Run Code Online (Sandbox Code Playgroud)

我想要输出

list={{1,2},{4,5}}
Run Code Online (Sandbox Code Playgroud)

删除哪个"行"并不重要,第一个是好的,但任何一个都没问题.

我尝试过Select,DeleteCases,DeleteDuplicates,Union和其他一些东西,但无法让它工作.我不知道如何告诉Mathematica只寻找'独特'元素.联盟接近但它完整列表.即我不知道为标准写什么,如

DeleteDuplicates[list, <now what?> ]
Run Code Online (Sandbox Code Playgroud)

作为参考,这是我在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
Run Code Online (Sandbox Code Playgroud)

谢谢

Leo*_*rin 8

这是一种方式:

DeleteDuplicates[list, First@#1 === First@#2 &]
Run Code Online (Sandbox Code Playgroud)

编辑

请注意,下面的时间和讨论基于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]]]]];
Run Code Online (Sandbox Code Playgroud)

基准:

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}}}
Run Code Online (Sandbox Code Playgroud)

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

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

最后,让我提一下,在之前已经提出了这个问题(强调效率).我将在这里重现一个解决方案,当第一个(或通常是n第一个)元素是正整数时(我推荐给任意整数很简单),我给出了这个解决方案:

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]]]
];
Run Code Online (Sandbox Code Playgroud)

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

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}}}
Run Code Online (Sandbox Code Playgroud)

  • 或许`Union`和`SameTest`在某些情况下也可能有用吗?例如`Union [{{1,3},{1,2},{4,5}},SameTest - >(First @#1 == First @#2&)]`.不过,我更喜欢列昂尼德的方法. (2认同)