统计单词出现次数(不是频率)
基本实现版本:
1 def cw_words(d = {}): 2 def makekey(s: str): 3 chars = set(r"""! '"#$%^&*()_+~=-><.,?/\}{][:;""") # 因为有反斜杠,所以加r,用集合,查找快 4 ret = [] 5 if 'path' in s: 6 for i, c in enumerate(s): 7 if c in chars: 8 ret.append(' ') # 遇到特殊字符,就用空格替代 9 else: 10 ret.append(c.lower()) 11 return ''.join(ret).split() 12 13 def makekey1(s: str): 14 chars = set(r"""! '"#$%^&*()_+~=-><.,?/\}{][:;""") # 因为有反斜杠,所以加r 15 ret = [] 16 start = 0 17 if 'path' in s: 18 for i, c in enumerate(s): # d.. ..a 19 if c in chars: 20 if start == i: # 如果紧挨着还是特殊字符start一定等于i 21 start += 1 # 加1 22 continue 23 ret.append(s[start:i].lower()) 24 start = i + 1 # 加1 是跳过这个不需要的特殊字符c 25 else: 26 if start < len(s): # 小于,说明还是有效的字符,而且一直到末尾 27 ret.append(s[start:].lower()) 28 return ret 29 30 with open('sample.txt', encoding='utf-8') as f: 31 for line in f: 32 words = line.split() 33 for i in words: 34 if 'path' not in i or i == []: 35 words.remove(i) 36 for wordlist in map(makekey, words): # 每个都拿上去 映射一次 37 for word in wordlist: 38 d[word] = d.get(word, 0) + 1 39 new_d = sorted(d.items(), key=lambda item: item[1], reverse=True) 40 print(new_d[0]) 41 return new_d[0] 42 43 cw_words()
优化版本:
1 # No 2 对之前版本的优化 封装 2 ''' 3 优化点: 4 1、对一些没必要的单词的过滤 5 2、没有必要 先把每一行切一遍,在到makekey中再切一遍,直接对每一行切,但是之前的chars中需要添加一个 空格了, 6 3、之前是把切割之后的放在列表中,这样导致用到了很大的额外空间,所以这里使用了yield生成器,用一个出一个 7 5、因为现在取前10 个,所以排序的时候, 没必要全部打印,所以只选取前10 个即可, 8 这里推荐使用的方法: 9 1、计数的方法,出一个+1 10 2、切片取10个,因为之前用了yield,所以这里并不是所有的都取出来,而是只去了10个 11 6、封装,尽量将所有的不需要别人看到的数据封装起来,此时,注意传参的问题。 12 13 14 # print('a','\n','------') 15 # a 16 # ------ 17 # print('a','\\n','------') # 转义 18 # a \n ------ 19 # print('a','/\n','------') 20 # a / 21 # ------ 22 ''' 23 def top10(filename): 24 def makekey(s: str): 25 chars = set(r"""! '"#$%^&*()_+~=-><.,?/\}{][:;""") # 因为有反斜杠,所以加r,用集合,查找快 26 key = s.lower() 27 ret = [] 28 for i, c in enumerate(key): 29 if c in chars: 30 ret.append(' ') # 遇到特殊字符,就用空格替代 31 else: 32 ret.append(c) 33 print(ret) # ['o', 's', ' ', 'p', 'a', 't', 'h'] ,连起来 ,在空白切开 34 return ''.join(ret).split() 35 36 def makekey1(line: str, chars = set("""! '"#$%^&*()_+~=-><.,?/\\}{][:; \n""") ): 37 # 因为有反斜杠,所以加r,加一个空格 38 39 # 这块切割是关键点,一个一个字符对应切 40 # i 是控制索引,start 是切割的开头 41 # 每次遇到特殊字符的时候,从start开始切,并且start要移动到现在i + 1 的位置,作为下次的起点 42 # 如果下次还是特殊字符,此时i = start,不切,继续往下,但是start继续往前,i+ 1 43 #如果某次开始都往下都没有在发现特殊字符,就进入else语句,并且,从之前 start开始到最后都是字母, 44 start = 0 45 for i, c in enumerate(line):# Split the pathname path into a pair (root, ext) such that root + ext == path 46 if c in chars: 47 if start == i: # 如果紧挨着还是特殊字符start一定等于i 48 start += 1 # 加1 49 continue 50 yield line[start:i] 51 start = i + 1 # 加1 是跳过这个不需要的特殊字符c 52 else: 53 if start < len(line): # 小于,说明还是有效的字符,而且一直到末尾 54 yield line[start:] 55 56 def wordcount(filename, encodiong='utf-8', ingore={'a','to','the'}): 57 d = {} 58 with open(filename, encoding='utf-8') as f: 59 for line in f: 60 # words = line.split() 61 for word in map(str.lower, makekey1(line)): # 每一行 都拿去切割,每次需要的时候去取回一个 ,给map 62 # 这里的先后顺序是:word 去map取,map在去makekey取 63 if word not in ingore: 64 d[word] = d.get(word, 0) + 1 65 return d 66 # no 1 通过 i 控制,和no2 的count是一样的 67 # for i, (k, v) in enumerate(sorted(d.items(), key=lambda item: item[1], reverse=True)): 68 # if i == n:break 69 # yield (k, v) 70 # no 3 71 # yield from sorted(d.items(), key=lambda item: item[1], reverse=True)[:n] # n通过传参获取 72 # no 2 73 def _top(d): 74 count = 1 75 for i in sorted(d.items(), key=lambda item: item[1], reverse=True): 76 yield i 77 count += 1 78 if count > 10: 79 break 80 return _top(wordcount(filename)) 81 82 # NO 1 for 循环迭代 83 # for i in top10('sample.txt'): 84 # print(i) 85 # NO 2list迭代器生成 86 print(list(top10('sample.txt'))) 87 88 ''' 89 单词统计: 90 1、利用map映射,只是没有用到reduce,大数据的处理,mapreduce 91 2、一般按照行处理,是比较合适的。 92 3、生成器、装饰器是python的精华,所以要往这个方向考虑,一般一批一批,一个一个都是可以使用生成器的 93 思路:返回一个列表,如果直接全部用可以用list,但是不需要可以惰性求值,通过for循环迭代 94 惰性求值:多个惰性求值一起使用,要一个,拿一个。 95 map(str.lower(), makekey(line)):先找map要一个,map 在找makekey要一个 96 出现次数 != 出现频率 97 98 排序取前几个: 99 1、全排,切片 100 2、计数,够了就不取了 101 4、如果遍历,首选set, dict 102 5、stopwords = set() 专用词,停止词,遇到就停止 103 '''