2011-09-26 153 views
0

我可以使用數百個JSON字符串。其中每個包含15-20個字的數組,按照一定的重量排序。如果值得注意的話,這個重量是這些詞在一些文本塊中找到的次數。找出像這樣構造的單詞陣列之間的相似性的最佳方式是什麼?比較字符串數組的相似性

我頭腦中的第一個想法是創建所有單詞的數值散列,並基本比較這些值以確定相似性。我並不是非常成功,因爲非常相似的字符串所產生的散列值並不是非常接近。經過一些關於字符串比較算法的研究,我來到Stackoverflow希望得到更多的指導。在此先感謝您,如果您需要更詳細的問題,請告訴我。

編輯1:澄清我想做的事情:我想根據這些詞中的每一個詞來確定兩個數組的相似程度。我還想考慮每個單詞在每個數組中的重量。例如:

var array1 = [{"word":"hill","count":5},{"word":"head","count":5}]; 
var array2 = [{"word":"valley","count":7},{"word":"head","count":5}]; 
var array3 = [{"word":"head", "count": 6}, {"word": "valley", "count": 5}]; 
var array4 = [{"word": "valley", "count": 7}, {"word":"head", "count": 5}]; 

在該示例中,陣列4和陣列2比陣列2和陣列3更相似的,因爲,儘管具有相同的話,其重量爲兩者相同的在陣列4和2.我希望這可以更容易理解。提前致謝。

+0

所以,你必須與每個Nm的話ñ陣列,並且要確定到底是什麼? –

+3

定義相似性... –

+0

我編輯了我的原始文章並做了一些說明。希望有助於和感謝您的興趣。 –

回答

3

我認爲,你想要的是「cosine similarity」,你可能也想看看vector space models。如果您在Java中編寫代碼,則可以使用開源代碼S-space包。

(於10月31日添加)向量的每個元素都是一個特定字符串的計數。你只需要將你的字符串數組轉換成這樣的向量。在你的例子中,你有三個詞 - 「山」,「頭」,「谷」。如果您的矢量按照該順序排列,則與陣列對應的矢量將爲

// array: #hill, #head, #valley 
array1: {5,  5,  0} 
array2: {0,  5,  7} 
array3: {0,  6,  5} 
array4: {0,  5,  7} 
+0

謝謝您的建議。儘管這是非常有用且有趣的材料,但在這種情況下,我並不想比較字符串本身的相似性。我只在乎他們是否相同。在這種情況下,我比較了字符串數組的相似性。 –

+0

@ Xavier - 是的,這就是餘弦相似性。矢量的每個元素都是一個特定字符串的計數。你只需要將你的字符串數組轉換成這樣一個向量。在你的例子中,你有三個詞 - 「山」,「頭」,「谷」。如果你的向量是這個順序的,那麼array1對應的向量就是{5,5,0}。 – kc2001

+0

有趣,kc2001。謝謝你回到我身旁。我仍然不完全明白,我不得不承認。在你解釋的情況下,只包含計數的向量如何幫助我比較數組?換句話說,在那個向量中的信息是包含實際字符串的信息,而不僅僅是字符串的計數?我看到一些研究Web的例子,他們在那裏製作字符串字母[abcde],然後是基於兩個字符串之間字符聯合的向量。這兩個向量然後使用餘弦相似性進行比較您是否在此建議類似的方法? –

1

鑑於每個陣列必須與其他陣列進行比較,您正在沿着Σ(n-1)乘以每個陣列中「單詞」的平均數量的線尋找大量處理。您需要存儲每個比較的分數,然後對其進行一些瞭解。

例如

var array1 = [{"word":"hill","count":5},{"word":"head","count":5}]; 
var array2 = [{"word":"valley","count":7},{"word":"head","count":5}]; 
var array3 = [{"word":"head", "count": 6}, {"word": "valley", "count": 5}]; 
var array4 = [{"word": "valley", "count": 7}, {"word":"head", "count": 5}]; 

// Comparison score is summed product of matching word counts 
function compareThings() { 

    var a, b, i = arguments.length, 
     j, m, mLen, n, nLen; 
    var word, score, result = []; 

    if (i < 2) return; 

    // For each array 
    while (i--) { 
    a = arguments[i]; 
    j = i; 

    // Compare with every other array 
    while (j--) { 
     b = arguments[j]; 
     score = 0; 

     // For each word in array 
     for (m=0, mLen = b.length; m<mLen; m++) { 
     word = b[m].word 

     // Compare with each word in other array 
     for (n=0, nLen=a.length; n<nLen; n++) { 

      // Add to score 
      if (a[n].word == word) { 
      score += a[n].count * b[m].count; 
      } 
     } 
     } 

     // Put score in result 
     result.push(i + '-' + j + ':' + score); 
    } 
    } 
    return result; 
} 

var results = compareThings(array1, array2, array3, array4); 

alert('Raw results:\n' + results.join('\n')); 
/* 
Raw results: 
3-2:65 
3-1:74 
3-0:25 
2-1:65 
2-0:30 
1-0:25 
*/ 

results.sort(function(a, b) { 
    a = a.split(':')[1]; 
    b = b.split(':')[1]; 
    return b - a; 
}); 

alert('Sorted results:\n' + results.join('\n')); 
/* 
Sorted results: 
3-1:74 
3-2:65 
2-1:65 
2-0:30 
3-0:25 
1-0:25 
*/ 

所以3-1(array4和array2)得分最高。幸運的是,比較只需要一種方法,您不必將a與b和b進行比較。

+0

感謝RobG。爲什麼你要通過乘以權重來計算相似性而不是像在這裏提供的其他建議中那樣減去它們?我喜歡它,因爲它在我測試的情況下做了我想要的,但它好像這個數字是任意的和不可預測的。例如,如果你有兩個數組有一個相同的詞,但是在一個數組中有很大的權重,它將會導致更類似於具有更少權重的更相似詞的數組。不過,這是一個好的開始,我感謝你的努力。 –

+0

我想是否添加或乘以「權重」取決於你的背景。在我完成的統計分析工作中,權重就像概率一樣,所以值乘以它們。一些現實世界的例子是帆船障礙(其中比賽的長度和條件各不相同,所以經過時間乘以差點)和調整測量控制網絡,其中每個測量具有不同的準確度(例如+ -10mm),因此具有不同的重量在調整中。 – RobG

+0

我明白,這當然取決於我想採取的方法。謝謝,RobG。 –

1

這是一個嘗試。該算法是不是很聰明(差別> 20是一樣的不具有同樣的話),但可能是一個有益的開端:

var wordArrays = [ 
    [{"word":"hill","count":5},{"word":"head","count":5}] 
    , [{"word":"valley","count":7},{"word":"head","count":5}] 
    , [{"word":"head", "count": 6}, {"word": "valley", "count": 5}] 
    , [{"word": "valley", "count": 7}, {"word":"head", "count": 5}] 
] 

function getSimilarTo(index){ 
    var src = wordArrays[index] 
     , values 

    if (!src) return null; 

    // compare with other arrays 
    weighted = wordArrays.map(function(arr, i){ 
     var diff = 0 
     src.forEach(function(item){ 
      arr.forEach(function(other){ 
       if (other.word === item.word){ 
        // add the absolute distance in count 
        diff += Math.abs(item.count - other.count) 
       } else { 
        // mismatches 
        diff += 20 
       } 
      }) 
     }) 
     return { 
      arr : JSON.stringify(arr) 
      , index : i 
      , diff : diff 
     } 
    }) 

    return weighted.sort(function(a,b){ 
     if (a.diff > b.diff) return 1 
     if (a.diff < b.diff) return -1 
     return 0 
    }) 
} 

/* 
getSimilarTo(3) 
[ { arr: '[{"word":"valley","count":7},{"word":"head","count":5}]', 
    index: 1, 
    diff: 100 }, 
    { arr: '[{"word":"valley","count":7},{"word":"head","count":5}]', 
    index: 3, 
    diff: 100 }, 
    { arr: '[{"word":"head","count":6},{"word":"valley","count":5}]', 
    index: 2, 
    diff: 103 }, 
    { arr: '[{"word":"hill","count":5},{"word":"head","count":5}]', 
    index: 0, 
    diff: 150 } ] 
*/ 
1

在嘗試比較之前按字排序數組。一旦完成,比較兩個數組就需要每個數組精確的1次通過。

排序陣列之後,這裏是一個比較算法(僞JAVA):


int compare(array1, array2) 
{ 
    returnValue = 0; 
    array1Index = 0 
    array2Index = 0; 

    while (array1Index < array1.length) 
    { 
    if (array2Index < array2.length) 
    { 
     if (array1[array1Index].word == array2[array2Index].word) // words match. 
     { 
     returnValue += abs(array1[array1Index].count - array2[array2Index].count); 
     ++array1Index; 
     ++array2Index; 
     } 
     else // account for the unmatched array2 word. 
     { 
     // 100 is just a number to give xtra weight to unmatched numbers. 
     returnValue += 100 + array2[array2Index].count; 
     ++array2Index; 
     } 
    } 
    else // array2 empty and array1 is not empty. 
    { 
     // 100 is just a number to give xtra weight to unmatched numbers. 
     returnValue += 100 + array1[array1Index].count; 
    } 
    } 

    // account for any extra unmatched array 2 values. 
    while (array2Index < array2.length) 
    { 
     // 100 is just a number to give xtra weight to unmatched numbers. 
     returnValue += 100 + array2[array2Index].count; 
    } 

    return returnValue; 
} 

+0

DwB,謝謝你的回答!您的方法很有趣,因爲它允許算法僅遍歷每個數組一次。但是我在這個實現中沒有看到,當你在array2中找不到一個單詞時會發生什麼?您將繼續使用inner else語句,直到第一個if條件失敗,並且即使您未嘗試使用array1中的任何其他單詞,但沒有找到匹配,您也會離開while循環。事實上,這種比較在這種情況下失敗了,因爲它會停留在無限循環中。感謝您在這一點上的建議,但這是一個非常有用的開始。 –