利用python进行word_count

来源:互联网 发布:鞋子价格,淘宝 编辑:程序博客网 时间:2024/06/06 04:14



一、动机

Word count就是统计文章中每个单词出现的次数,在自然语言处理和机器学习中是一个很基础的工作。Word count是如此的常见以至于学习Spark等工具时,给出的第一个例子就是它(有点像hello world)。于是这里就记录一下用python(主要是pandas模块)要如何进行word count,及一些基础的优化。

二、基础

1、思路

文章以txt文件的形式存在于D\english_article\文件夹及其子文件夹下,那么首先要设计一个函数来把这些文件的地址都读进来,这里用递归的方法。

files = []

file_num = 0

 

def get_path(main_path):

   global files

   global file_num

   try:

       tmp = os.listdir(main_path)

       for fil in tmp:

           dir_path = main_path+'\\'+fil

           if '.txt' in fil:

               files.append(dir_path)

               file_num += 1

           else:

               get_path(dir_path)

   except Exception as e:

       print e.message

 

        对于读入的字符串,可以利用内建的split函数以空格来划分。然而要考虑到可能单词之间会出现多个空格,所以这里用python的正则表达式模块来划分:

        re.split(re.compile(r'\s*'), article)

        对于划分好的字符串,要考虑它是不是我们要统计的单词,还是其他字符。这个可以用string对象的isalpha()函数来判断。

        对于需要记录的单词,还要把他们都变成小写,以防止同一个单词被分成好几类,这个用string对象的lower()函数来转换。

Pandas有一个函数value_counts,专门用来统计一个list中各项出现的次数,并返回一个Series。同时Series对象有add函数,可以将多次统计结果加在一起。


2、代码

用了一部分函数式编程的思想,最后打印出使用次数最多的前30个单词及其使用次数。

import sys

import pandas as pd

import re

from pandas import Series

import os

files = []

file_num = 0

 

def get_path(main_path):

   global files

   global file_num

   try:

       tmp = os.listdir(main_path)

       for fil in tmp:

           dir_path = main_path+'\\'+fil

           if '.txt' in fil:

               files.append(dir_path)

               file_num += 1

           else:

               get_path(dir_path)

   except Exception as e:

       print e.message

 

path = 'D:\\english_article'

get_path(path)

counter = Series()

print ' '

num = 1

for txt_file in files:

   with open(txt_file) as s:

       a = pd.value_counts(filter(lambda x: x.isalpha(), map(lambda x:x.lower(), re.split(re.compile(r'\s*'), s.read()))))

       counter = counter.add(a, fill_value=0)

       sys.stdout.write('\r%d/%d' % (num, file_num+1))

       num += 1

print ' '

counter =counter.sort_values(ascending=False)

print counter[:30]

三、优化

我用的IDEpycharm专业版,我觉得和裸写代码相比,最大的好处就是他自带图形化的profile工具。现在用这个工具分析一下,发现总共用时128342ms

        如图,其中88.8%的时间都用在了join函数上,62.1%的时间用在了union函数上。这说明大部分时间都花在了Series对象的并操作上。既然如此,考虑到文章并不大,可以全部读到内存里,就在这里优化。全部读入,只统计一次。

import pandas aspd

import re

import os

files = []

file_num = 0

article = ''

 

defget_path(main_path):

   global files

   global file_num

   try:

       tmp = os.listdir(main_path)

       for fil in tmp:

           dir_path = main_path+'\\'+fil

           if '.txt' in fil:

               files.append(dir_path)

               file_num += 1

           else:

               get_path(dir_path)

   except Exception as e:

       print e.message

 

path ='D:\\english_article'

get_path(path)

print ' '

num = 1

for txt_file infiles:

   with open(txt_file) as s:

       article += s.read()

print ' '

counter =pd.value_counts(map(lambda x: x.lower(), filter(lambda x: x.isalpha(),re.split(re.compile(r'\s*'),article))))

counter =counter.sort_values(ascending=False)

printcounter[:30]

这次用时20670ms,比之前快了6倍!

不过对于之前的获得所有文件地址操作和读取所有文件的内容操作,python本身就自带相应的模块和函数os.walkfileinput.input ,套用他们对程序重写如下:

import pandas as pd

import re

import os

import fileinput

files = []

path = 'D:\\english_article'

 

for data in os.walk(path):

   files.extend(map(lambda x: os.path.join(data[0], x), data[2]))

article =''.join(list(fileinput.input(files)))

counter = pd.value_counts(map(lambda x:x.lower(), filter(lambda x: x.isalpha(),re.split(re.compile(r'\s*'),article))))

counter =counter.sort_values(ascending=False)

print counter[:30]

实际有用的代码只有5行,确实是非常精简。但运行时间变成了31256ms,看来有时候确实不是代码越简单速度越快嘛。

 

0 0