la función N-gramo en vb.net -> crear gramos para las palabras en lugar de caracteres

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

  •  19-09-2019
  •  | 
  •  

Pregunta

Hace poco me enteré de n-gramas y la posibilidad de comparar fresco frecuencia de frases en un cuerpo de texto con él. Ahora estoy tratando de hacer una aplicación vb.net que consigue simplemente un cuerpo de texto y devuelve una lista de las frases utilizadas con mayor frecuencia (donde n> = 2).

he encontrado un # ejemplo C de cómo generar un n-gramo de un cuerpo de texto de modo Empecé con convertir el código de VB. El problema es que este código hace crear un gramo por cada carácter en lugar de uno por cada palabra. Los delimitadores que desea utilizar para las palabras es: VbCrLf mensaje = mensaje (nueva línea), vbTab (pestañas) y los siguientes caracteres: @ # $% ^ & * () _ + - = {} | \!?: \ " '¿ . /, <> '¡º × ÷'; «» []

¿Alguien tiene una idea de cómo puedo volver a escribir la siguiente función para este propósito:

   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
¿Fue útil?

Solución

Un n -gramo para las palabras es sólo una lista de longitud n que almacena estas palabras. Una lista de los n -grams es simplemente una lista de lista de palabras. Si desea almacenar la frecuencia, entonces usted necesita un diccionario que está indexado por estos n -grams. Para su caso especial de 2 gramos, se puede imaginar algo como esto:

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 ahora debe contener un diccionario con todos los dos pares consecutivos de palabras contenidas en el texto, y la frecuencia con la que aparecen (como un par consecutivo).

Este código requiere la clase 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

Por desgracia, este código no se compila en Mono porque el compilador de VB tiene problemas para encontrar la clase EqualityComparer genérico. Estoy por lo tanto, incapaz de probar si la GetHashCode implementationw funciona como se esperaba, sino que debería estar bien.

Otros consejos

Gracias Konrad mucho para este comienzo de una solución!

He intentado su código y dieron el siguiente resultado:

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
---------------------

Mi primera pregunta: ¿no deberían los 3 últimos pares de claves obtener un valor de 2 a medida que se encontraron dos veces en el texto

Segundo: La razón por la que entré en el enfoque de n-gramas es que no quiero limitar el número de palabras (n) a una longitud específica. ¿Hay una manera de hacer un enfoque dinámico que intenta encontrar la concordancia de frase más larga primero y luego dar un paso hasta el último recuento de palabras de 2?

Mi resultado objetivo de la consulta de ejemplo anterior es:

---------------------
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
---------------------

Hay un C similares enfoque ++ para este escrito por Hatem Mostafa sobre al codeproject.com: N-gramo y Pattern Fast Extracción Algoritmo

Por desgracia yo no soy un experto en C ++ y no tienen idea de cómo convertir este fragmento de código, ya que incluye una gran cantidad de memoria para el manejo .Net que no lo hace. El único problema con este ejemplo es que usted tiene que especificar la longitud mínima patrón de la palabra y yo quiero que sea dinámica del 2 al máximo encontrado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top