Python结巴分词,字符串余弦相似度算法实现关键词筛选及整理

Python分词方法,返回json格式关键词数据

分词思路:

结巴分词,用字符串余弦相似度算法实现关键词筛选和整理。

字符串余弦相似性算法是通过利用我们初中就学过的三角函数中的余弦定理来计算两个字符串的相似度,它是定义在向量空间模型(Vector Space Model)中的。


一个思路是采用了递归,另一个是纯python方法整理。

python默认最大的递归深度为1000,超过之后就会出错。


几个关键点:

1.posseg 用法

每个词都有其词性,比如名词、动词、代词等,结巴分词的结果也可以带上每个词的词性,要用到jieba.posseg

from jieba import posseg
s = u'我想和女朋友一起去北京故宫博物院参观和闲逛。'
print('【Output】')
print([(x.word,x.flag) for x in psg.cut(s)])

# 输出:

'''
[(u'我', u'r'), (u'想', u'v'), (u'和', u'c'), (u'女朋友', u'n'), (u'一起', u'm'), 
(u'去', u'v'), (u'北京故宫博物院', u'ns'), (u'参观', u'n'), (u'和', u'c'), (u'闲逛', u'v'), (u'。', u'x')]
'''

2.Python sqrt() 函数

sqrt() 方法返回数字x的平方根。

import math

math.sqrt( x )
#注意:sqrt()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。

import math   # This will import math module

print "math.sqrt(100) : ", math.sqrt(100)
print "math.sqrt(7) : ", math.sqrt(7)
print "math.sqrt(math.pi) : ", math.sqrt(math.pi)

"""
以上实例运行后输出结果为:
math.sqrt(100) :  10.0
math.sqrt(7) :  2.64575131106
math.sqrt(math.pi) :  1.77245385091
"""

3.map函数

map() 会根据提供的函数对指定序列做映射。 

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

python中的map()函数是一个内置的高阶函数,一般用法是map(function, iterable)。

需要传入一个函数,这个函数可以是内置的,也可以是自己定义,也可以是匿名函数。第二个参数是一个可迭代对象,如列表,字符串等等。返回的是一个map对象,注意不是列表不能直接输出,可以通过for循环或者list()来显示。(python2返回的是列表)

def square(x):
    return x*x
a=map(square,[1,2,3]) 
print(a)        
#输出为<map object at 0x0033CFB0>   可以看出map返回的实际上是一个map对象
print(list(a))  
#输出为[1, 4, 9]   通过list()方式 显示出来   

#也可以通过for循环来取出内容
ls=[]
for i in a:
    ls.append(i)
print(ls)
#输出为[1, 4, 9]

#其实map,不止能传入一个可迭代对象做为参数。也可以传入两个。看例子就可以体会到这用法

ls1='ABC'
ls2='abc'
print(list(map(lambda x,y:x+y,ls1,ls2)))
#['Aa', 'Bb', 'Cc']

#若是传入的多个可迭代对象长度不相同,则按最短的长度进行处理(这是针对python3的)。具体用法如下:

ls1='ABC'
ls2='ab'
print(list(map(lambda x,y:x+y,ls1,ls2)))
#['Aa', 'Bb']


>>>def square(x) :            # 计算平方数
...     return x ** 2
... 
>>> map(square, [1,2,3,4,5])   # 计算列表各个元素的平方
[1, 4, 9, 16, 25]
>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
 
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]

4.python中的lambda函数用法

lambda函数的特点和使用场景


特点

1.从函数命名的角度:匿名,直接返回可供调用的值。

2.从输入输出的角度:支持多个输入参数,但只支持一个表达式。

3.从函数功能的角度:结构简单,无须定义函数名。但所能实现的功能也极其受限。

4.从访问变量的角度:只支持访问lambda自己定义的变量。不支持外部变量访问。

5.从运行效率的角度:lambda实际上仍开辟了一个内存单元,并没有提升运行效率。


使用场景

1.lambda函数复制给变量:减少函数定义的麻烦,同时支持代码复用。

add=lambda x,y:x+y
print(add(1,2))

2.lambda函数赋值给函数:利用lambda函数实现对已有函数的重新定义。

例如,为了把标准库time中的函数sleep的功能屏蔽(Mock),我们可以在程序初始化时调用:time.sleep=lambda x:None。这样,在后续代码中调用time库的sleep函数将不会执行原有的功能。例如,执行time.sleep(3)时,程序不会休眠3秒钟,而是什么都不做。 


3.利用lambda函数进行函数嵌套:利用lambda构造嵌套的内部和外部函数,实现闭包编程。

函数的返回值也可以是函数。例如return lambda x, y: x+y返回一个加法函数。这时,lambda函数实际上是定义在某个函数内部的函数,称之为嵌套函数,或者内部函数。对应的,将包含嵌套函数的函数称之为外部函数。内部函数能够访问外部函数的局部变量,这个特性是闭包(Closure)编程的基础,在这里我们不展开。


4.将lambda函数作为参数传递给其他函数。


#部分Python内置函数接收函数作为参数。

#典型的此类内置函数有这些。


- filter函数。此时lambda函数用于指定过滤列表元素的条件。例如filter(lambda x: x % 3 == 0, [1, 2, 3])指定将列表[1,2,3]中能够被3整除的元素过滤出来,其结果是[3]。

- sorted函数。此时lambda函数用于指定对列表中所有元素进行排序的准则。例如sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))将列表[1, 2, 3, 4, 5, 6, 7, 8, 9]按照元素与5距离从小到大进行排序,其结果是[5, 4, 6, 3, 7, 2, 8, 1, 9]。

- map函数。此时lambda函数用于指定对列表中每一个元素的共同操作。例如map(lambda x: x+1, [1, 2,3])将列表[1, 2, 3]中的元素分别加1,其结果[2, 3, 4]。

- reduce函数。此时lambda函数用于指定列表中两两相邻元素的结合条件。例如reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])将列表 [1, 2, 3, 4, 5, 6, 7, 8, 9]中的元素从左往右两两以逗号分隔的字符的形式依次结合起来,其结果是'1, 2, 3, 4, 5, 6, 7, 8, 9'。

————————————————

来源:CSDN博主「吉大秦少游」


实例参考:

传入多个参数的lambda函数

def sum(x,y):
      return x+y
用lambda来实现:

p = lambda x,y:x+y
print(p(4,6))

传入一个参数的lambda函数

a=lambda x:x*x
print(a(3))       # 注意:这里直接a(3)可以执行,但没有输出的,前面的print不能少
多个参数的lambda形式

a = lambda x,y,z:(x+8)*y-z
print(a(5,6,8))

匿名函数lambda:是指一类无需定义标识符(函数名)的函数或子程序。


lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。

要点:

1,lambda 函数不能包含命令, 

2,包含的表达式不能超过一个。 

说明:一定非要使用lambda函数;任何能够使用它们的地方,都可以定义一个单独的普通函数来进行替换。


我将它们用在需要封装特殊的、非重用代码上,避免令我的代码充斥着大量单行函数。 lambda匿名函数的格式:冒号前是参数,可以有多个,用逗号隔开,冒号右边的为表达式。其实lambda返回值是一个函数的地址,也就是函数对象。

a=lambda x:x*x
print(a)
print(a(3))

---->

<function <lambda> at 0x0000000002093E18>

9


5.递归函数

如果一个函数在内部调用自己本身,这个函数就是递归函数。

注意事项:

1,必须是自己调用自己;

2,必须有一个明确的递归结束条件,即为递归出口。


案例1:使用递归函数向控制台打印3,2,1

def print_num(num):
    print(num)
    if num==1:
        return
    print_num(num-1)
    print(num,'--->>')
print_num(3)

输出结果:


3

2

1

2 --->>

3 --->>



案例2:使用递归函数输出6的阶乘(1*2*3*4*5*6)

def jiecheng(num):
    if num==1:
        return 1
    ret=jiecheng(num-1)
    return num*ret
num=jiecheng(6)
print(num) # 720


建议查看廖雪峰python教程

https://www.liaoxuefeng.com/wiki/1016959663602400/1017268131039072


6.json用法

json.dumps()用于将字典形式的数据转化为字符串,json.loads()用于将字符串形式的数据转化为字典


json 中的ensure_ascii=False

json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False。


>>> import json

>>> print json.dumps('中国')

"\u4e2d\u56fd"


>>> import json

>>> print json.dumps('中国')

"\u4e2d\u56fd"

>>> print json.dumps('中国',ensure_ascii=False)

"中国"

>>>


附完整代码:

from jieba import posseg  #每个词都有其词性,比如名词、动词、代词等,结巴分词的结果也可以带上每个词的词性,要用到jieba.posseg
import math,json,time

#结果保存字典
result={}
#Cache 缓存
cache={}

def simicos(str1,str2):
    """
    字符串余弦相似度算法实现, 添加缓存存储,减少分词带来的时间消耗
    提高计算效率
    :param str1:要比较的字符串1
    :param str2:要比较的字符串2
    :return:相似度值0.0-1.0,越接近1,相似度越高
    """
    global cache #全局变量
    if cache.get(str1):   #或者用 key in cache 方法也可以
        cut_str1=cache[str1]
    else:
        cut_str1=[w for w,t in posseg.cut(str1) if 'n' in t or 'v' in t]
        cache[str1]=cut_str1

    if cache.get(str2):   #或者用 key in cache 方法也可以
        cut_str2=cache[str2]
    else:
        cut_str2=[w for w,t in posseg.cut(str2) if 'n' in t or 'v' in t]
        cache[str2]=cut_str2

    all_words=set(cut_str1+cut_str2)
    freq_str1=[cut_str1.count(x) for x in all_words] #Python count() 方法用于统计字符串里某个字符出现的次数
    freq_str2 = [cut_str2.count(x) for x in all_words]
    sum_all=sum(map(lambda z,y:z*y,freq_str1,freq_str2))
    sqrt_str1=math.sqrt(sum(x ** 2 for x in freq_str1))
    sqrt_str2=math.sqrt(sum(x ** 2 for x in freq_str2))
    try:
        return sum_all/(sqrt_str1 * sqrt_str2)
    except ZeroDivisionError:  #ZeroDivisionError: division by zero 除数为0的错误。常见情况是在除法运算中除数的值为0.
        return 0.0


def calculate(keyword_list):
    """
    使用递归函数进行计算和分类
    有点:代码简单,容易理解
    缺点:python默认最大的递归深度为1000,超过之后就会出错
    :param keyword_list: 要分类的关键词列表
    :return: None
    """
    filter_word=[] #获取第一个词为母词
    muci=keyword_list.pop(0) #用该词作为分类
    result[muci]=[]
    print(len(keyword_list))
    while keyword_list:
        kw=keyword_list.pop(0)  #提取列表中的下一个词
        simi=simicos(muci,kw)
        if simi>=0.3:
            result[muci].append(kw)
        else:
            filter_word.append(kw)
    if filter_word: #如果列表中还有词,就进行递归计算
        calculate(filter_word)


def classify(keyword_list):
    """
    使用while循环的方法计算和分享
    :param keyword_list:  要分类的关键词队列
    :return:  None
    """
    muci=keyword_list.pop(0)  #第一个词
    result[muci]={"total":0,"list":[]}
    last=keyword_list[-1]  #最后一个词
    total_classify=0
    remain=len(keyword_list)
    while keyword_list:
        kw=keyword_list.pop(0) #提取列表中的下一个词
        simi=simicos(muci,kw)
        if simi >= 0.3:
            result[muci]["list"].append(kw)
            result[muci]["total"] += 1
            remain -= 1
        else:
            keyword_list.append(kw)
        if kw==last and keyword_list: #如果已经是最后一个了
            total_classify += 1
            print("Has been classified %d, remain %d" % (total_classify,remain))
            muci=keyword_list.pop(0)
            remain-=1
            result[muci]={"total":0,"list":[]}
            if keyword_list:
                last=keyword_list[-1]
            else:
                break


def result_filter(result_dict,bigthan=10):
    """
    结果过滤筛选函数
    由于使用该方法得到很多的分类,有些分类是没有关键词或者只有少数的关键词
    那么对于这些分类就可能不需要了,我们直接把他们过滤掉就好了
    :param result_dict:  要筛选的分类结果
    :param bigthan:  相关词数量大于等于该数量的分类将保存
    :return:  过滤后的结果
    """
    retfilter={}
    for item,values in result_dict.items():
        if values["total"]>bigthan:
            retfilter[item]=values
    return retfilter


if __name__=="__main__":
    t_start=time.time()
    print("start....")
    keywords=[key.strip() for key in open("keys.txt")]
    classify(keywords)
    filtered=result_filter(result)
    #结果保存在json中
    f=open("result3.json","w",encoding='utf-8')
    f.write(json.dumps(filtered,ensure_ascii=False))
    f.close()
    print("done,consume %.3f" % (time.time() - t_start))


运行效果:

pic_001.gif

分词效果:

我们应用在线json格式工具查看

比如:

json.cn

pic_002.jpg