使用pandas进行基于NLTK的文本处理

ash*_*pen 7 python string nltk dataframe pandas

使用nltk时,标点符号和数字小写不起作用.

我的代码

stopwords=nltk.corpus.stopwords.words('english')+ list(string.punctuation)
user_defined_stop_words=['st','rd','hong','kong']                    
new_stop_words=stopwords+user_defined_stop_words

def preprocess(text):
    return [word for word in word_tokenize(text) if word.lower() not in new_stop_words and not word.isdigit()]

miss_data['Clean_addr'] = miss_data['Adj_Addr'].apply(preprocess)
Run Code Online (Sandbox Code Playgroud)

样本输入

23FLOOR 9 DES VOEUX RD WEST     HONG KONG
PAG CONSULTING FLAT 15 AIA CENTRAL 1 CONNAUGHT RD CENTRAL
C/O CITY LOST STUDIOS AND FLAT 4F 13-15 HILLIER ST SHEUNG HONG KONG
Run Code Online (Sandbox Code Playgroud)

预期产出

 floor des voeux west
 pag consulting flat aia central connaught central
 co city lost studios flat f hillier sheung
Run Code Online (Sandbox Code Playgroud)

cs9*_*s95 13

你的功能很而且不完整.首先,问题 -

  1. 您没有降低数据量.
  2. 你没有正确摆脱数字和标点符号.
  3. 你没有返回一个字符串(你应该加入列表str.join并返回它)
  4. 此外,具有文本处理的列表理解是引入可读性问题的主要方式,更不用说可能的冗余(对于if它出现的每个条件,您可以多次调用函数.

接下来,您的函数会出现一些明显的低效问题,尤其是使用停用词删除代码.

  1. 您的stopwords结构是一个列表,in对列表的检查很.要做的第一件事就是将其转换为a set,使not in支票保持恒定时间.

  2. 你使用的nltk.word_tokenize是不必要的慢.

  3. 最后,apply即使您正在使用很少有任何矢量化解决方案的NLTK ,您也不应该总是依赖它.几乎总有其他方法可以做同样的事情.通常情况下,即使是python循环也会更快.但这不是一成不变的.

首先,创建您的增强stopwords作为 -

user_defined_stop_words = ['st','rd','hong','kong'] 

i = nltk.corpus.stopwords.words('english')
j = list(string.punctuation) + user_defined_stop_words

stopwords = set(i).union(j)
Run Code Online (Sandbox Code Playgroud)

下一个修复是摆脱列表理解并将其转换为多行函数.这使事情变得更容易使用.你的函数的每一行应该专门用于解决一个特定的任务(例如,摆脱数字/标点符号,或删除停用词或小写) -

def preprocess(x):
    x = re.sub('[^a-z\s]', '', x.lower())                  # get rid of noise
    x = [w for w in x.split() if w not in set(stopwords)]  # remove stopwords
    return ' '.join(x)                                     # join the list
Run Code Online (Sandbox Code Playgroud)

举个例子.然后这将被apply列入你的专栏 -

df['Clean_addr'] = df['Adj_Addr'].apply(preprocess)
Run Code Online (Sandbox Code Playgroud)

作为替代方案,这是一种不依赖的方法apply.这应该适用于小句子.

将数据加载到一系列中 -

v = miss_data['Adj_Addr']
v

0            23FLOOR 9 DES VOEUX RD WEST     HONG KONG
1    PAG CONSULTING FLAT 15 AIA CENTRAL 1 CONNAUGHT...
2    C/O CITY LOST STUDIOS AND FLAT 4F 13-15 HILLIE...
Name: Adj_Addr, dtype: object
Run Code Online (Sandbox Code Playgroud)

现在是繁重的工作.

  1. 小写用 str.lower
  2. 使用消除噪音 str.replace
  3. 用单词分成单独的单元格 str.split
  4. 使用pd.DataFrame.isin+ 应用停用词删除pd.DataFrame.where
  5. 最后,使用加入数据框agg.

v = v.str.lower().str.replace('[^a-z\s]', '').str.split(expand=True)

v.where(~v.isin(stopwords) & v.notnull(), '')\
 .agg(' '.join, axis=1)\
 .str.replace('\s+', ' ')\
 .str.strip()

0                                 floor des voeux west
1    pag consulting flat aia central connaught central
2           co city lost studios flat f hillier sheung
dtype: object
Run Code Online (Sandbox Code Playgroud)

要在多列上使用它,请将此代码放在函数中preprocess2并调用apply-

def preprocess2(v):
     v = v.str.lower().str.replace('[^a-z\s]', '').str.split(expand=True)

     return v.where(~v.isin(stopwords) & v.notnull(), '')\
             .agg(' '.join, axis=1)\
             .str.replace('\s+', ' ')\
             .str.strip()
Run Code Online (Sandbox Code Playgroud)

c = ['Col1', 'Col2', ...] # columns to operate
df[c] = df[c].apply(preprocess2, axis=0)
Run Code Online (Sandbox Code Playgroud)

你仍然需要一个apply电话,但是由于列数很少,它的扩展性不会太差.如果你不喜欢apply,那么这里有一个循环的变种 -

for _c in c:
    df[_c] = preprocess2(df[_c])
Run Code Online (Sandbox Code Playgroud)

让我们看看我们的非loopy版本和原始版本之间的区别 -

s = pd.concat([s] * 100000, ignore_index=True) 

s.size
300000
Run Code Online (Sandbox Code Playgroud)

首先,进行健全检查 -

preprocess2(s).eq(s.apply(preprocess)).all()
True
Run Code Online (Sandbox Code Playgroud)

现在来了时间.

%timeit preprocess2(s)   
1 loop, best of 3: 13.8 s per loop
Run Code Online (Sandbox Code Playgroud)

%timeit s.apply(preprocess)
1 loop, best of 3: 9.72 s per loop
Run Code Online (Sandbox Code Playgroud)

这是令人惊讶的,因为apply它很少比非循环解决方案快.但是在这种情况下这是有意义的,因为我们已经进行了preprocess相当多的优化,并且大熊猫中的字符串操作很少被矢量化(它们通常是,但性能增益并不像您期望的那么多).

让我们看看我们是否可以做得更好,绕过apply,使用np.vectorize

preprocess3 = np.vectorize(preprocess)

%timeit preprocess3(s)
1 loop, best of 3: 9.65 s per loop
Run Code Online (Sandbox Code Playgroud)

apply由于"隐藏"循环周围的开销减少,因此相同但速度更快.