Domanda

Di recente ho scoperto su n-grammi e la possibilità fresco per confrontare la frequenza di frasi in un corpo di testo con esso. Ora sto cercando di fare un app vb.net che ottiene semplicemente un corpo del testo e restituisce una lista delle frasi più frequentemente utilizzate (dove n> = 2).

Ho trovato un C # esempio di come generare un n-gram da un corpo del testo così ho iniziato con la conversione del codice da VB. Il problema è che questo codice non creare un grammo per carattere invece di uno per ogni parola. I delimitatori che voglio usare le parole è: VbCrLf (nuova linea), vbTab (schede) ei seguenti caratteri: @ # $% ^ & * () _ + - = {} | \:!? \ " '¿ . /, <> '¡º × ÷'; «» []

Qualcuno ha un'idea di come posso riscrivere la seguente funzione per questo scopo:

   Friend Shared Function GenerateNGrams(ByVal text As String, ByVal gramLength As Integer) As String()
    If text Is Nothing OrElse text.Length = 0 Then
        Return Nothing
    End If

    Dim grams As New ArrayList()
    Dim length As Integer = text.Length
    If length < gramLength Then
        Dim gram As String
        For i As Integer = 1 To length
            gram = text.Substring(0, (i) - (0))
            If grams.IndexOf(gram) = -1 Then
                grams.Add(gram)
            End If
        Next

        gram = text.Substring(length - 1, (length) - (length - 1))
        If grams.IndexOf(gram) = -1 Then
            grams.Add(gram)

        End If
    Else
        For i As Integer = 1 To gramLength - 1
            Dim gram As String = text.Substring(0, (i) - (0))
            If grams.IndexOf(gram) = -1 Then
                grams.Add(gram)

            End If
        Next

        For i As Integer = 0 To (length - gramLength)
            Dim gram As String = text.Substring(i, (i + gramLength) - (i))
            If grams.IndexOf(gram) = -1 Then
                grams.Add(gram)
            End If
        Next

        For i As Integer = (length - gramLength) + 1 To length - 1
            Dim gram As String = text.Substring(i, (length) - (i))
            If grams.IndexOf(gram) = -1 Then
                grams.Add(gram)
            End If
        Next
    End If
    Return Tokeniser.ArrayListToArray(grams)
End Function
È stato utile?

Soluzione

Un n -gram per le parole è solo una lista di lunghezza n che memorizza queste parole. Un elenco di n -grams è quindi semplicemente un elenco di elenco di parole. Se si desidera memorizzare la frequenza, allora avete bisogno di un dizionario che viene indicizzato da questi n -grams. Per il vostro caso particolare di 2 grammi, si può immaginare qualcosa di simile:

Dim frequencies As New Dictionary(Of String(), Integer)(New ArrayComparer(Of String)())
Const separators as String = "!@#$%^&*()_+-={}|\:""'?¿/.,<>’¡º×÷‘;«»[] " & _
                             ControlChars.CrLf & ControlChars.Tab
Dim words = text.Split(separators.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)

For i As Integer = 0 To words.Length - 2
    Dim ngram = New String() { words(i), words(i + 1) }
    Dim oldValue As Integer = 0
    frequencies.TryGetValue(ngram, oldValue)
    frequencies(ngram) = oldValue + 1
Next

frequencies dovrebbe ora contenere un dizionario con tutte le due coppie consecutive di parole contenute nel testo, e la frequenza con cui compaiono (come coppia consecutiva).

Questo codice richiede la classe ArrayComparer:

Public Class ArrayComparer(Of T)
    Implements IEqualityComparer(Of T())

    Private ReadOnly comparer As IEqualityComparer(Of T)

    Public Sub New()
        Me.New(EqualityComparer(Of T).Default)
    End Sub

    Public Sub New(ByVal comparer As IEqualityComparer(Of T))
        Me.comparer = comparer
    End Sub

    Public Overloads Function Equals(ByVal a As T(), ByVal b As T()) As Boolean _
            Implements IEqualityComparer(Of T()).Equals
        System.Diagnostics.Debug.Assert(a.Length = b.Length)
        For i As Integer = 0 to a.Length - 1
            If Not comparer.Equals(a(i), b(i)) Then Return False
        Next

        Return True
    End Function

    Public Overloads Function GetHashCode(ByVal arr As T()) As Integer _
            Implements IEqualityComparer(Of T()).GetHashCode
        Dim hashCode As Integer = 17
        For Each obj As T In arr
            hashCode = ((hashCode << 5) - 1) Xor comparer.GetHashCode(obj)
        Next

        Return hashCode
    End Function
End Class

Purtroppo, questo codice non viene compilato su Mono perché il compilatore VB ha problemi a trovare la classe EqualityComparer generica. Sono quindi in grado di verificare se il GetHashCode implementationw funziona come previsto, ma dovrebbe andare bene.

Altri suggerimenti

Grazie Konrad molto per questo inizio di una soluzione!

Ho cercato il tuo codice ed ho ottenuto il seguente risultato:

Text = "Hello I am a test Also I am a test"
(I also included whitespace as a separator)

frequencies now has 9 items:
---------------------
Keys: "Hello", "I"
Value: 1
---------------------
Keys: "I", "am"
Value: 1
---------------------
Keys: "am", "a"
Value: 1
---------------------
Keys: "a", "test"
Value: 1
---------------------
Keys: "test", "Also"
Value: 1
---------------------
Keys: "Also", "I"
Value: 1
---------------------
Keys: "I", "am"
Value: 1
---------------------
Keys: "am", "a"
Value: 1
---------------------
Keys: "a", "test"
Value: 1
---------------------

La mia prima domanda:? Non dovrebbero ultimi 3 coppie di chiavi ottenere un valore di 2 come sono trovato due volte nel testo

Secondo: La ragione per cui ho preso l'approccio n-gram è che io non voglio limitare il numero di parole (n) per una lunghezza specifica. C'è un modo per rendere un approccio dinamico che cerca di trovare la più lunga partita frase prima e poi passo fino all'ultimo wordcount di 2?

Il mio risultato obiettivo per la query di esempio di cui sopra è:

---------------------
Match: "I am a test"
Frequency: 2
---------------------
Match: "I am a"
Frequency: 2
---------------------
Match: "am a test"
Frequency: 2
---------------------
Match: "I am"
Frequency: 2
---------------------
Match: "am a"
Frequency: 2
---------------------
Match: "a test"
Frequency: 2
---------------------

C'è un simile approccio C ++ per questo scritto da Hatem Mostafa sopra a codeproject.com: N-gram e pattern rapida estrazione Algoritmo

Purtroppo io non sono esperto C ++ e non hanno idea di come convertire questo pezzo di codice in quanto include un sacco di memoria gestione che .Net non lo fa. L'unico problema con questo esempio è che si deve specificare la lunghezza minima modello parola e io voglio che sia dinamico da 2 a max trovato.

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