第一次个人编程作业

第一次个人编程作业

一、作业提交

代码Github链接

二、PSP表格

总体来说在开发和需求分析部分的时间占比较多

一开始选择的是gensim里的TfidfModel模型,但使用情况与作业不符合,不是一对一分析。调试了半天的时间没写出结果,最后取巧加入一个空文本,可以得到结果,但每个文本的相似度都高的离谱。最后放弃重来,使用jieba库里的extract_tags函数生成关键词-词频字典,再使用余弦向量法计算相似度,效果是不错的。
而其实在进行软工实践时,找资料、搞其他花里胡哨 专业人士该做的事情(比如写博客等)的时间占比是非常高的。

三、计算模块接口的设计与实现

1.流程图:

  • 使用jieba将传入的文本字符串分割,并根据词频和关键词数产生关键词-词频字典。使用jieba.analyse.extract_tags函数

  • 分别获取两个文本的词频向量,并整合,使两个向量的元素对应的关键词相同。

  • 对特征值进行预处理数据中每个数值减去最小值,除以极差。

  • 计算向量的余弦相似度

2.代码组织(函数关系):

两个关键函数:

     # 制作 关键词-词频 字典,获取词频向量
    def vector(self):
        self.vdict1 = {}
        self.vdict2 = {}
        # 参数含义:待提取文本,关键词数量(按重要性降序),是否同时返回权重
        # 返回值:关键词 词频
        top_keywords1 = jieba.analyse.extract_tags(self.str1, topK=1200, withWeight=True)
        top_keywords2 = jieba.analyse.extract_tags(self.str2, topK=1200, withWeight=True)
        for k, v in top_keywords1:
            # print(k,v)
            self.vdict1[k] = v
            # 获取词频向量
        for k, v in top_keywords2:
            # print(k,v)
            self.vdict2[k] = v
 # 整合关键词,数据预处理
    def mix(self):
        # 整合两个字典
        for key in self.vdict1:
            self.vdict2[key] = self.vdict2.get(key, 0)
            # 若vdict2的值存在,则返回dict[key],不存在则返回0;
        for key in self.vdict2:
            self.vdict1[key] = self.vdict1.get(key, 0)
        # vdict1和vdict2中分别包含文本1和2的关键词及其特征值

        def pre_treatment(vdict):
            # 对特征值进行预处理:数据中每个数值减去最小值,除以极差
            min_ = min(vdict.values())# 返回字典中最小值
            max_ = max(vdict.values())# 返回最大值
            mid_ = max_ - min_# 极差
            for key in vdict:
                vdict[key] = (vdict[key] - min_) / mid_
            return vdict

3.算法核心思想(简单叙述):

将文本制作成向量例如v[0,10,25,10,1,2,5],v[0]代表下标为0所指的关键词的词频。
通过计算两个向量的余弦值,可以表示两个向量的夹角。余弦值越大,夹角越小,说明重合度越高。

而问题是如何选取关键词制作词频向量,词频应该怎么处理更为合理。
因此,
1)使用jieba库中的jieba.analyse.extract_tags函数提取关键词和词频。(此处有设计TF-IDF算法思想)
2)使用数值减最小值除以极差的方式进行词频预处理。

整个计算模块的处理整理为Similarity类

四、性能分析

1.性能分析图

总耗时1.5s左右,性能还可以

2.各函数消耗时间:

Load_word_tag和set_new_path 函数是消耗最多的两个函数
Load_word_tag加载分词情况:

set_new_path与计算TFIDF值有关:

两个函数的消耗是为了得到关键词字典,必不可少。而其他可改进的地方暂时也想不到了
性能分析也到此结束。

五、单元测试

单元测试这块也调试了很久。主要是异常处理模块的问题。

1.单元测试部分代码:

class TestSimilarity(unittest.TestCase):
    def setUp(self):
        self.path1 = "./orig.txt"

    def tearDown(self):
        print("over!")

1.测试函数中把每一个单元的path1定义为原文本,其他每个单元给出一个文本路径进行测试。
2.共测试了12组
其中作业给出的测试样例共10组,再加上异常测试2组
整体调试情况:

2.正常测试组:

    def test_rep(self):
        print("\ntest rep")
        path2 = "./orig_0.8_rep.txt"
        s = similarity.Similarity(self.path1, path2).similar()
        print('%.2f' % s)
        # 对比判断测试是否正确
        self.assertGreaterEqual(s, 0)
        self.assertLessEqual(s, 1)

3.异常测试组:

def test_null(self):
        print("\n空文本测试:")
        path2 = "./null.txt"
        similarity.Similarity(self.path1, path2)
        self.assertRaises(similarity.emptyError)
    def test_pathError(self):
        print("\n路径测试:")
        path2 = "./error_path.txt"
        self.assertRaises(similarity.pathError,similarity.Similarity,self.path1,path2)

*(第二个异常测试老是测试不成功,后来发现是assertRaises函数的参数设置出错了,
1-正确执行原文件应该抛出的异常类,2-原文件的主类,3、4-传入主类的测试参数,以上为我的理解,具体请参考使用文档)

4.测试覆盖率截图

*说明:我的测试文档载入的是similarity.py,这是当初写代码的时候使用的名称。后面发现要用main.py作为入口文件名,便新建了main.py,两个文档内容完全一致。

六、模块异常处理

1.共设计了两个异常模块

# 异常处理类
class emptyError(Exception) :
    def __init__(self):
        print("文本为空,请重新输入!")

class pathError(Exception) :
    def __init__(self):
        print("文本路径不存在,请重新输入!")

2.主类里的使用情况:

        def read(doc_path):
            # 过滤文本:除去字符,返回字符串

            #路径异常处理
            if os.path.exists(doc_path) ==False:
                #print("路径异常,请重新输入!")
                raise pathError
            #文本处理
            doc = open(doc_path, encoding='UTF-8')
            punch = '~`!#$%^&*()_+-=|\';:/.,?><~·!@#¥%……&*()——+-=“”:’;、。,?》《{} \n'
            doc_txt = re.sub(r"[%s]+" % punch, "", doc.read())
            doc.close()
            # 空文本异常
            try:
                if len(doc_txt) == 0 :
                    raise emptyError
            except emptyError:
                pass
            return doc_txt
        self.str1 = read(path1)
        self.str2 = read(path2)

3.说明:

1)空文本异常处理:输入的文本内容为空时,读取到的doc_txt长度为0,抛出emptyError异常,输出提示。
2)路径异常处理:输入的历经不存在时,抛出pathError异常,输出提示。

4.测试样例:

def test_null(self):
        print("\n空文本测试:")
        path2 = "./null.txt"
        similarity.Similarity(self.path1, path2)
        self.assertRaises(similarity.emptyError)
    def test_pathError(self):
        print("\n路径测试:")
        path2 = "./error_path.txt"
        self.assertRaises(similarity.pathError,similarity.Similarity,self.path1,path2)

5.结果:

七、个人总结

1.绝对绝对不要拖作业!!尤其是软工实践!!血的教训o(╥﹏╥)o

错过周末黄金时期;周一电脑突然出故障,幸好当晚修好;周二开始写代码,算法和模型与作业要求不符,调试不出结果;周三继续调试,仍旧不行;周三下午换算法,重写程序;周三晚上编写单元测试;周四下午异常处理;周四晚上码博客,就是现在,被ddl追着打字【泪目】

2.我变强了,也变秃了xx

重新拾起python,我还是那个小白= =不过这次的作业让我对python的熟悉程度更高了些
对GitHub的基本操作和Git的使用也有了一定了解(虽然还是不得不面对一堆英文,但经过这几天,看GitHub的界面都觉得亲切许多了ne)

3.作业感受:

不得不说,这次的作业调试代码不是最难的,而是做单元测试、性能分析(虽然我也没分析出个啥)、使用GitHub、写博客等这些事情比较繁琐。
总之,算是接触了更多编程的衍生事务了吧。

下次,一定不拖le!!

posted @ 2020-09-17 21:53  mbeforeu  阅读(251)  评论(3编辑  收藏  举报