利用Python实现TF-IDF算法进行文章分类

一、TF-IDF算法

       TF(Term Frequency)指词频,IDF(Inverse Document Frequency)指逆向文件频率。它的主要思想是:如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。简单来说,就是计算字符在文章中的重要性,并给它赋予相应的权,权重大越重要越能作为区分文章的工具。举个例子:“的”这个字在文章中往往出现最多,但是在其他文章中也很多出现,因此认为“的”重要性小,而比如这篇文章中大量出现“数学”,而其他文章中“数学”出现很少,说明对这篇文章来说,“数学”很重要,这篇文章也大概是讲数学的。

二、文章的向量化与分类方式

      那么,知道了每个字符的重要性后如何给文章分类呢?向量就登场了。我们生活在一个三维的空间之中,我们绝大多数人都能够理解的只有直线向量、平面向量和空间向量,它们分别像是这样a=(-3)(直线向量),b=(5,7)(平面向量),c=(1,6,-4)(空间向量)。而向量在更多维度下的表现对我们来说是不可见的,但是是可算的,比如五维的向量e1=(2,5,1,7,13)、e2=(14,6,4,9,10),我们没法把它们画出来,但是我们可以利用向量外积计算它们之间的夹角的余弦值,在每条维度上的数值都大于零时,夹角的范围便被限定为[0, ],所以余弦值越大,夹角越小,相反余弦值越小,夹角越大。而当我们将每一个字符都作为一个维度,文章中它们的权重作为它们的坐标,那么文章就可以用一条高维向量来表示,不同的文章会有不同的向量,当两篇文章所对应的向量夹角小的时候,说明具有区分度词或短语在这两篇文章中重合率越高,也就说明两篇文章越相似,相反向量夹角大,就说明两篇文章差别越大。将向量夹角小的文章分为一类,也就完成了将相似的文章分为一类这一任务,正确率不亚于人工。

三、python实现

首先我们需要一个字典,利用字典建立向量,这里我找了一个两千五百字的常用字字典命名为dictionary.txt

read_dictionary.py

def txttermnumber(files):
    """计算txt文件中每个字出现的个数"""
    # 打开文件
    fr = open(files, 'r', encoding='UTF-8')
    # 读取文件所有行
    content = fr.readlines()
    contentLines = ''

    characers = []
    stat = {}

    # 依次迭代所有行
    for line in content:
        # 去除空格
        line = line.strip()
        if len(line) == 0:
            continue
        contentLines = contentLines + line
        # 统计每一字出现的个数
        for x in range(0, len(line)):
            # 如果字符第一次出现 加入到字符数组中
            if not line[x] in characers:
                characers.append(line[x])
            # 如果是字符第一次出现 加入到字典中
            if line[x] not in stat:
                stat[line[x]] = 0
            # 出现次数加一
            stat[line[x]] += 0

    # 对字典进行倒数排序 从高到低 其中e表示dict.items()中的一个元素,
    # e[1]则表示按 值排序如果把e[1]改成e[0],那么则是按键排序,
    # reverse=False可以省略,默认为升序排列

    return stat

其次需要将文章转化为向量形式,注意在IDF中作为语料库的文案我是找的论文,地址为dll/

txt_turnto_vector.py

from read_dictionary import txttermnumber
from math import log


def tf(file):
    """计算词频"""
    dictionary = txttermnumber("dictionary.txt")
    # 打开文件
    fr = open(file, 'r', encoding='UTF-8')
    # 读取文件所有行
    content = fr.readlines()
    contentLines = ''

    characers = []
    stat = {}

    # 依次迭代所有行
    for line in content:
        # 去除空格
        line = line.strip()
        if len(line) == 0:
            continue
        contentLines = contentLines + line
        # 统计每一字出现的个数
        for x in range(0, len(line)):
            # 如果字符第一次出现 加入到字符数组中
            if not line[x] in characers:
                characers.append(line[x])
            # 如果是字符第一次出现 加入到字典中
            if line[x] not in stat:
                stat[line[x]] = 0
            # 出现次数加一
            stat[line[x]] += 1

    # 对字典进行倒数排序 从高到低 其中e表示dict.items()中的一个元素,
    # e[1]则表示按 值排序如果把e[1]改成e[0],那么则是按键排序,
    # reverse=False可以省略,默认为升序排列
    stat = sorted(stat.items(), key=lambda e: e[1], reverse=True)
    # 打印stat 每个字和其出现的次数 stat经过排序后变成二元组
    for i in stat:
        # 将stat里面所有在字典中的字输出加入到字典中
        if i[0] in dictionary.keys():
            dictionary[i[0]] += i[1]
        else:
            continue

    fr.close()
    for a in dictionary.keys():
        dictionary[a] = dictionary[a]/len(contentLines)
    return dictionary


def idf():
    """计算逆向文件频率"""
    n = 1
    dictionary = txttermnumber("dictionary.txt")

    while n < 67:
        n = str(n)
        file = 'dll/' + n + '.txt'
        fr1 = open(file, 'r', encoding='UTF-8')
        # 读取文件所有行
        content = fr1.readlines()
        contentLines = ''

        characers = []
        stat = {}

        # 同上
        for line in content:
            line = line.strip()
            if len(line) == 0:
                continue
            contentLines = contentLines + line
            for x in range(0, len(line)):
                if not line[x] in characers:
                    characers.append(line[x])
                if line[x] not in stat:
                    stat[line[x]] = 0
                stat[line[x]] += 1

        fr1.close()
        # 将文字出现的文章数附到字典中
        for i in stat:
            if i[0] in dictionary.keys():
                dictionary[i[0]] += 1
            else:
                continue
        n = int(n)
        n += 1
        # 计算字典的idf值
        for a in dictionary.keys():
            dictionary[a] = log(66/(dictionary[a]+1), 10)
    return dictionary


print(tf("TF-IDF.txt"))
print(tf("search.txt"))
print(idf())

最后进行向量运算

tfidf.py

from txt_turnto_vector import tf, idf


def tf_idf(file):
    """计算tf-idf"""
    tfidf = {}
    for i in tf(file).keys():
        tfidf[i] = tf(file)[i]*(idf()[i])
    return tfidf


def vector_inner_product(vector1, vector2):
    """用于计算两向量的内积"""
    n = 0
    num = 0
    while n < 2500:
        num += vector1[n]*vector2[n]
        n += 1
    num1 = 0
    num2 = 0
    for i in vector1:
        num1 += i**2
    for a in vector2:
        num2 += a**2
    num1 = num1**0.5
    num2 = num2**0.5
    cos12 = num/(num1 * num2)
    return cos12

 最后选择两篇文章可以计算出他们之间的cos值

你可能感兴趣的:(分类,servlet,java,python,算法)