2011-08-31 56 views
4

我試圖用功能性的方式解決這個問題,但我沒有太大的成功。如何從矩陣中選擇具有特定列中唯一條目的行?

假設有一個列表的列表,而且只需要挑中它具有在特定位置唯一項目名單。

例如,假設有一個矩陣,我們要選擇僅在第一列的獨特元素的行。

下面是一個例子:

輸入:

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

我想輸出是

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

不要緊,這 '行' 刪除,第一一個是好的,但任何一個都沒問題。

我試過選擇,DeleteCases,DeleteDuplicates,聯盟和其他一些東西,但無法得到它的工作。我不知道如何告訴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 

感謝

回答

8

這裏有一種方法:

DeleteDuplicates[list, [email protected]#1 === [email protected]#2 &] 

編輯

注意,定時和以下討論是基於M7

在反射一點,我發現這將是(至少)量級大型列表更快的溶液,和幅度有時兩個數量速度更快,對於這種特殊情況下(可能是更好的方法把它就是下面的解決方案將有不同的計算複雜性):

Clear[delDupBy]; 
delDupBy[nested_List, n_Integer] := 
    Module[{parts = nested[[All, n]], ord, unpos}, 
    ord = Ordering[parts]; 
    unpos = [email protected]@Prepend[Map[Length, [email protected][[ord]]], 1]; 
    nested[[[email protected][[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,[email protected]#[email protected]#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

我認爲這是一個重要的教訓:使用自定義測試時,應該注意內置函數,如UnionSort,DeleteDuplicates等。我在this Mathgroup線程中進行了更廣泛的討論,其中還有其他有洞察力的回覆。

最後,讓我提,here之前,正是這種問題已經被問(與強調效率)。我將在這裏重現一個解決方案,我給的情況下,當第一(或者,通常,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 = [email protected][dim < threshold, [email protected], parr]; 
    data[[If[preserveOrder, [email protected], parr]]] 
]; 

其工作原理是使用第一個(或通常爲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}}} 
+0

哇,太棒了!我不知道這些標準可以應用於「整個」列表,我認爲它只適用於迭代時的特定「元素」。學到了新東西。謝謝 – Nasser

+2

也許在某些情況下'Union'和'SameTest'可能有用?例如'Union [{{1,3},{1,2},{4,5}},SameTest - >(First @#1 == First @#2&)]'。不過,我更喜歡獅子座的方法。 – tomd

+0

@TomD'Union' with'SameTest'對於更大的列表也可能有嚴重的性能問題,請參閱我的評論和指向編輯中相關的過去討論的鏈接。 –

0

列昂尼德提供了一個漫長而徹底的答案,就像他經常這樣做。不過,我相信這是值得指出的是高效和簡潔的解決方案可以與有:

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

哪裏1是列索引比較。

在我的系統上,這比delDupBy快,但不如deleteDuplicatesBy快。

相關問題