Come restituire un'intersezione di array Ruby con elementi duplicati? (problema con i bigrammi nel coefficiente dei dadi)

StackOverflow https://stackoverflow.com/questions/1600168

Domanda

Sto provando a scrivere Dice's Coefficient, ma sto riscontrando un po 'di problemi con l'intersezione dell'array.

def bigram(string)
  string.downcase!
  bgarray=[]
  bgstring="%"+string+"#"
  bgslength = bgstring.length
  0.upto(bgslength-2) do |i|
    bgarray << bgstring[i,2]
   end
   return bgarray
 end

def approx_string_match(teststring, refstring)
  test_bigram = bigram(teststring) #.uniq
  ref_bigram = bigram(refstring)   #.uniq

  bigram_overlay = test_bigram & ref_bigram

  result = (2*bigram_overlay.length.to_f)/(test_bigram.length.to_f+ref_bigram.length.to_f)*100

  return result
end

Il problema è, come & amp; rimuove i duplicati, ottengo cose come questa:

string1="Almirante Almeida Almada"
string2="Almirante Almeida Almada"

puts approx_string_match(string1, string2) => 76.0%

Dovrebbe restituire 100.

Il metodo uniq lo inchioda, ma c'è una perdita di informazioni, che può portare a corrispondenze indesiderate nel set di dati specifico su cui sto lavorando.

Come posso ottenere un incrocio con tutti i duplicati inclusi?

È stato utile?

Soluzione

Come Yuval F ha detto che dovresti usare multiset . Tuttavia, non esiste multiset nella libreria standard di Ruby, dai un'occhiata a qui e qui .

Se le prestazioni non sono così importanti per la tua applicazione, puoi comunque farlo usando Array con un po 'di codice.

def intersect  a , b  
    a.inject([]) do |intersect, s|
      index = b.index(s)
      unless index.nil?
         intersect << s
         b.delete_at(index)
      end
      intersect        
    end
end

a=  ["al","al","lc" ,"lc","ld"]
b = ["al","al" ,"lc" ,"ef"]
puts intersect(a ,b).inspect   #["al", "al", "lc"]

Altri suggerimenti

Da questo link credo che non dovresti usare i set di Ruby ma piuttosto multiset, in modo che ogni bigram venga conteggiato il numero di volte che appare. Forse puoi usare questa gemma per i multiset. Questo dovrebbe dare un comportamento corretto per i bigram ricorrenti.

Ho giocato con questo, sulla base della risposta di @pierr, per un po 'e ho finito con questo.

a = ["al","al","lc","lc","lc","lc","ld"]
b = ["al","al","al","al","al","lc","ef"]
result=[]
h1,h2=Hash.new(0),Hash.new(0)
a.each{|x| h1[x]+=1}
b.each{|x| h2[x]+=1}
h1.each_pair{|key,val| result<<[key]*[val,h2[key]].min if h2[key]!=0}
result.flatten

= > [" al " ;, " al " ;, " lc "]

Potrebbe essere una specie di intersezione multiset di a & amp; b ma non crederci perché non l'ho provato abbastanza per esserne sicuro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top