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
你的功能很慢而且不完整.首先,问题 -
str.join并返回它)if它出现的每个条件,您可以多次调用函数.接下来,您的函数会出现一些明显的低效问题,尤其是使用停用词删除代码.
您的stopwords结构是一个列表,in对列表的检查很慢.要做的第一件事就是将其转换为a set,使not in支票保持恒定时间.
你使用的nltk.word_tokenize是不必要的慢.
最后,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)
现在是繁重的工作.
str.lowerstr.replace str.split pd.DataFrame.isin+ 应用停用词删除pd.DataFrame.whereagg.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由于"隐藏"循环周围的开销减少,因此相同但速度更快.