la función N-gramo en vb.net -> crear gramos para las palabras en lugar de caracteres
-
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
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.