vb.net中的N-gram函数->;为单词而不是字符创建字符

vb.net中的N-gram函数->;为单词而不是字符创建字符,vb.net,text-mining,n-gram,Vb.net,Text Mining,N Gram,我最近发现了n-gram,以及将文本正文中短语的频率与它进行比较的一种很酷的可能性。现在,我正在尝试制作一个vb.net应用程序,它只获取一个文本体并返回一个最常用短语的列表(其中n>=2) 我找到了一个C#示例,说明如何从文本体生成n-gram,因此我开始将代码转换为VB。问题是,这段代码确实会为每个字符创建一克,而不是每个单词创建一克。我想用于单词的分隔符是:VbCrLf(新行)、vbTab(制表符)和以下字符:!@#$%^&*():“??/,:×÷”;«»[] 是否有人知道如何为此重写以下

我最近发现了n-gram,以及将文本正文中短语的频率与它进行比较的一种很酷的可能性。现在,我正在尝试制作一个vb.net应用程序,它只获取一个文本体并返回一个最常用短语的列表(其中n>=2)

我找到了一个C#示例,说明如何从文本体生成n-gram,因此我开始将代码转换为VB。问题是,这段代码确实会为每个字符创建一克,而不是每个单词创建一克。我想用于单词的分隔符是:VbCrLf(新行)、vbTab(制表符)和以下字符:!@#$%^&*():“??/,:×÷”;«»[]

是否有人知道如何为此重写以下函数:

   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
Friend共享函数generateGrams(ByVal text作为字符串,ByVal gramLength作为整数)作为字符串()
如果文本为Nothing或LSE text.Length=0,则
一无所获
如果结束
Dim grams作为新的ArrayList()
Dim长度为整数=text.length
如果长度
单词的n-gram就是存储这些单词的长度n的列表。n-gram的列表就是单词列表。如果你想存储频率,那么你需要一个由这些n-gram索引的字典。对于2-gram的特殊情况,你可以想象这样的情况:

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

不幸的是,这段代码没有在Mono上编译,因为VB编译器在查找泛型
EqualityComparer
类时遇到问题。因此,我无法测试
GetHashCode
实现是否按预期工作,但应该可以。非常感谢Konrad提供了这一解决方案的开始

我尝试了你的代码,得到了以下结果:

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
---------------------
我的第一个问题:最后3个键对不应该得到值2,因为它们在文本中被发现了两次吗

第二:我之所以采用n-gram方法,是因为我不想将字数(n)限制在特定的长度。有没有一种方法可以使动态方法尝试首先找到最长的短语匹配,然后降低到最后的字数2

我对上述示例查询的目标结果是:

---------------------
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
---------------------
Hatem Mostafa在C++项目中写了类似的C++方法:


遗憾的是,我不是C++专家,不知道如何转换这段代码,因为它包含了很多内存处理。NET没有。这个问题的唯一问题是,你必须指定最小的字模式长度,我希望它从2到max动态。

超级感谢!下面的帖子里有完整的答案和即将出现的问题:我哈。我在运行这段代码时遇到了一些问题。当我在VS2008中尝试它时,我第一次得到警告:函数等于,GetHashCode“在基类“Object”中隐藏了一个可重写的方法”。若要重写基本方法,必须将此方法声明为“重写”。在运行时,代码在“frequencies.Add(ngram,oldValue+1)”处中断,但“已添加具有相同键的项”除外“当试图插入同一个ngram的第二个匹配项时。@Majgel:阴影确实是个问题。正如我所写的,我无法测试代码。要更正错误,只需添加
重载
。我已经更正了答案。另一个错误也已更正。再次感谢您的快速回答!现在VS2008警告消失了。但是,上面描述的代码中断仍然存在。只有当字典中已经存在的n-gram试图插入时,才会发生这种情况。“frequencies.TryGetValue(ngram,oldValue)”和“frequencies.Add(ngram,oldValue+1)”都运行class Equals()函数并按假设返回True。然后出现异常“已添加具有相同密钥的项”。有什么想法吗?@Majgel:我也更改了那段代码:现在该方法不再使用
Add
而是使用索引访问器,即
frequencies(ngram)=oldValue+1
。首先,解决这个错误:该死!显然,数组的
GetHashCode
方法不起作用。不幸的是,字典在内部使用此方法。这就是奇怪结果的原因。请参阅我的更新答案以了解解决方法:我们需要定义一个对数组进行正确的相等性比较的类。关于您的另一个问题:我不确定您期望的输出是什么。可能出现多次的最长n克?我认为这将是最长公共子序列问题的一些变体,因此,它将是NP难的,即不可有效求解,因为它不仅包含两个不同的序列,而且包含原始序列中所有可能的子序列,最后一点:请为这个新问题创建一个新的问题线程。堆栈溢出
---------------------
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
---------------------