Python 提高数据帧上文本清理的性能
我有一个df:Python 提高数据帧上文本清理的性能,python,performance,pandas,nltk,apply,Python,Performance,Pandas,Nltk,Apply,我有一个df: id text 1 This is a good sentence 2 This is a sentence with a number: 2015 3 This is a third sentence 我有一个文本清理功能: def clean(text): lettersOnly = re.sub('[^a-zA-Z]',' ', text) tokens = word_tokenize(lettersOnly.lower())
id text
1 This is a good sentence
2 This is a sentence with a number: 2015
3 This is a third sentence
我有一个文本清理功能:
def clean(text):
lettersOnly = re.sub('[^a-zA-Z]',' ', text)
tokens = word_tokenize(lettersOnly.lower())
stops = set(stopwords.words('english'))
tokens = [w for w in tokens if not w in stops]
tokensPOS = pos_tag(tokens)
tokensLemmatized = []
for w in tokensPOS:
tokensLemmatized.append(WordNetLemmatizer().lemmatize(w[0], get_wordnet_pos(w[1])))
clean = " ".join(tokensLemmatized)
return clean
get\u wordnet\u pos()
这是:
def get_wordnet_pos(treebank_tag):
if treebank_tag.startswith('J'):
return wordnet.ADJ
elif treebank_tag.startswith('V'):
return wordnet.VERB
elif treebank_tag.startswith('N'):
return wordnet.NOUN
elif treebank_tag.startswith('R'):
return wordnet.ADV
else:
return wordnet.NOUN
我正在将extractFeatures()
应用于熊猫列,并创建一个新列,结果如下:
df['cleanText'] = df['text'].apply(clean)
结果df:
id cleanText
1 good sentence
2 sentence number
3 third sentence
循环时间似乎呈指数增长。例如,使用%%timeit
,将其应用于五行,每循环运行17毫秒。300行以每循环800毫秒的速度运行。500行以每循环1.26秒的速度运行
我通过在函数外部实例化stops
和WordNetLemmatizer()
对其进行了修改,因为它们只需要调用一次
stops = set(stopwords.words('english'))
lem = WordNetLemmatizer()
def clean(text):
lettersOnly = re.sub('[^a-zA-Z]',' ', text)
tokens = word_tokenize(lettersOnly.lower())
tokens = [w for w in tokens if not w in stops]
tokensPOS = pos_tag(tokens)
tokensLemmatized = []
for w in tokensPOS:
tokensLemmatized.append(lem.lemmatize(w[0], get_wordnet_pos(w[1])))
clean = " ".join(tokensLemmatized)
return clean
在应用
行上运行%prun-l 10
,生成此表:
672542 function calls (672538 primitive calls) in 2.798 seconds
Ordered by: internal time
List reduced from 211 to 10 due to restriction <10>
ncalls tottime percall cumtime percall filename:lineno(function)
4097 0.727 0.000 0.942 0.000 perceptron.py:48(predict)
4500 0.584 0.000 0.584 0.000 {built-in method nt.stat}
3500 0.243 0.000 0.243 0.000 {built-in method nt._isdir}
14971 0.157 0.000 0.178 0.000 {method 'sub' of '_sre.SRE_Pattern' objects}
57358 0.129 0.000 0.155 0.000 perceptron.py:250(add)
4105 0.117 0.000 0.201 0.000 {built-in method builtins.max}
184365 0.084 0.000 0.084 0.000 perceptron.py:58(<lambda>)
4097 0.057 0.000 0.213 0.000 perceptron.py:245(_get_features)
500 0.038 0.000 1.220 0.002 perceptron.py:143(tag)
2000 0.034 0.000 0.068 0.000 ntpath.py:471(normpath)
672542个函数调用(672538个基元调用)只需2.798秒
订购人:内部时间
由于限制,名单从211个减少到10个
ncalls tottime percall cumtime percall文件名:lineno(函数)
4097 0.727 0.000 0.942 0.000感知器。py:48(预测)
45000.5840.000 0.5840.000{内置方法nt.stat}
3500 0.243 0.000 0.243 0.000{内置方法nt.\U isdir}
14971 0.157 0.000 0.178 0.000{方法'sub'的'u sre.sre_模式'objects}
57358 0.129 0.000 0.155 0.000感知器。py:250(添加)
4105 0.117 0.000 0.201 0.000{内置方法builtins.max}
184365 0.084 0.000 0.084 0.000感知器。py:58()
4097 0.057 0.000 0.213 0.000感知器。py:245(_get_特征)
500 0.038 0.000 1.220 0.002感知器。py:143(标签)
2000 0.034 0.000 0.068 0.000 ntpath.py:471(normpath)
可以预见,perceptron tagger似乎占用了大量资源,但我不确定如何简化它。此外,我不确定在哪里调用nt.stat
或nt.\u isdir
我应该如何更改函数或应用方法来提高性能?这个函数是Cython还是Numba的候选函数?我在这里看到的第一个明显的改进点是,整个
get\u wordnet\u pos
函数应该简化为字典查找:
def get_wordnet_pos(treebank_tag):
if treebank_tag.startswith('J'):
return wordnet.ADJ
elif treebank_tag.startswith('V'):
return wordnet.VERB
elif treebank_tag.startswith('N'):
return wordnet.NOUN
elif treebank_tag.startswith('R'):
return wordnet.ADV
else:
return wordnet.NOUN
相反,从集合
包中初始化defaultdict
:
import collections
get_wordnet_pos = collections.defaultdict(lambda: wordnet.NOUN)
get_wordnet_pos.update({'J' : wordnet.ADJ,
'V' : wordnet.VERB,
'N' : wordnet.NOUN,
'R' : wordnet.ADV })
然后,您将按如下方式访问查找:
get_wordnet_pos[w[1][0]]
下一步,如果要在多个地方使用,可以考虑预编译正则表达式。你得到的加速没有那么多,但这一切都很重要
pattern = re.compile('[^a-zA-Z]')
在函数内部,您可以调用:
pattern.sub(' ', text)
OTOH,如果您知道文本的来源,并且对可能看到的和可能看不到的内容有所了解,您可以预先编译一个字符列表,而使用str.translate
,这比基于笨拙正则表达式的替换要快得多:
tab = str.maketrans(dict.fromkeys("1234567890!@#$%^&*()_+-={}[]|\'\":;,<.>/?\\~`", '')) # pre-compiled use once substitution table (keep this outside the function)
text = 'hello., hi! lol, what\'s up'
new_text = text.translate(tab) # this would run inside your function
print(new_text)
'hello hi lol whats up'
tab=str.maketrans(dict.fromkeys(“1234567890!@$%^&*()u+-={}[]\'\'\'\”:;,/?\\\\\`,'')\ 35;预编译的use once替换表(将其保留在函数之外)
text='你好,嗨!哈哈,怎么了'
new_text=text.translate(tab)#这将在函数内部运行
打印(新文本)
“你好,嗨,哈哈,怎么了”
此外,我要说的是,word\u tokenize
太过分了——不管怎么说,你要做的就是去掉特殊字符,这样你就失去了word\u tokenize
的所有好处,这与标点符号等有很大的不同。你可以选择使用text.split()
最后,跳过clean=“”.join(tokenslemmazized)
步骤。只需返回列表,然后在最后一步中调用df.applymap(“.join)
我将基准测试留给您。我在这里看到的第一个明显的改进点是,整个
get\u wordnet\u pos
功能应该简化为字典查找:
def get_wordnet_pos(treebank_tag):
if treebank_tag.startswith('J'):
return wordnet.ADJ
elif treebank_tag.startswith('V'):
return wordnet.VERB
elif treebank_tag.startswith('N'):
return wordnet.NOUN
elif treebank_tag.startswith('R'):
return wordnet.ADV
else:
return wordnet.NOUN
相反,从集合
包中初始化defaultdict
:
import collections
get_wordnet_pos = collections.defaultdict(lambda: wordnet.NOUN)
get_wordnet_pos.update({'J' : wordnet.ADJ,
'V' : wordnet.VERB,
'N' : wordnet.NOUN,
'R' : wordnet.ADV })
然后,您将按如下方式访问查找:
get_wordnet_pos[w[1][0]]
接下来,你可以考虑预编译你的正则表达式,如果它在多个地方被使用。你得到的加速不是那么多,但是它很重要。
pattern = re.compile('[^a-zA-Z]')
在函数内部,您可以调用:
pattern.sub(' ', text)
OTOH,如果您知道文本的来源,并且对可能看到的和可能看不到的内容有所了解,您可以预先编译一个字符列表,而使用str.translate
,这比基于笨拙正则表达式的替换要快得多:
tab = str.maketrans(dict.fromkeys("1234567890!@#$%^&*()_+-={}[]|\'\":;,<.>/?\\~`", '')) # pre-compiled use once substitution table (keep this outside the function)
text = 'hello., hi! lol, what\'s up'
new_text = text.translate(tab) # this would run inside your function
print(new_text)
'hello hi lol whats up'
tab=str.maketrans(dict.fromkeys(“1234567890!@$%^&*()u+-={}[]\'\'\'\”:;,/?\\\\\`,'')\ 35;预编译的use once替换表(将其保留在函数之外)
text='你好,你好!哈哈,怎么了
new_text=text.translate(tab)#这将在函数内部运行
打印(新文本)
“你好,嗨,哈哈,怎么了”
此外,我要说的是,word\u tokenize
太过分了——不管怎么说,你要做的就是去掉特殊字符,这样你就失去了word\u tokenize
的所有好处,这对标点符号之类的东西来说真的很重要。您可以选择使用text.split()
最后,跳过clean=“.join(令牌已初始化)
步骤。只需返回列表,然后在最后一步中调用df.applymap(“.join)
我将基准测试留给您。如果没有您的数据和预期输出,就不能说了。添加了示例输入数据和清理功能的结果。我正在获得正确的输出-问题更多的是如何更快地获得正确的输出。有趣。单词的顺序重要吗?我猜是吗?是的,因为
cleanedText
随后被发送到矢量器中,以收集NGRAM、频率、tf idf权重,等等。我认为最明显的改进点是将get\u wordnet\u pos
减少为str
defaultdict
。如果没有您的数据和预期输出,就不能说了。添加了示例输入数据和清理功能的结果。我得到了p