前段時間參加了Kaggle上的Mercari Price Suggestion Challenge比賽,收穫良多,過些時候準備進行一些全面的總結,本篇文章先談一個比賽中用到的小技巧。html
這個比賽數據中有一個特徵叫作 "item_description",大體是一些商品描述,好比何時買的、新舊程度如何、什麼牌子的等等。由於大部分都是Mercari這個網站(這個相似於國內的二手商品交易網站)上的用戶本身填的商品描述,因此是極盡雜亂之能事,會出現不少誇張的符號,好比這樣:python
另外的一大問題是用語規範不統一,好比 $1.100
和 $1.1
實際上是一個意思,然而在對文本進行特徵提取時就會被當成兩個特徵,這會使特徵變得過於稀疏,對模型的效果也會產生影響。所幸Pandas中提供了str.replace()
這個方法,能夠高效處理此類問題。正則表達式
str.replace()
的做用基本與re.sub()
等同,區別在於re.sub()
一次只能處理一個字符串,而str.replace()
能夠一次處理一整個Series,於是效率要高不少。str.replace()
的正式形式爲 Series.str.replace(pat, repl) ,其中pat爲想要尋找的模式,通常爲正則表達式,repl爲要替換進去的字符串或函數。iphone
下面是幾個簡單的例子,X表明一個Series,repl皆爲字符串:函數
X.str.replace(r"iphone\s+7", "iphone7") #爲了將iphone7視爲一個詞,把iphone 7轉換爲iphone7,去除空格。 X.str.replace(r"16gbiphone", "16gb iphone") #將16gbiphone轉換無16gb iphone,增長空格。 X.str.replace(r"fl\s?\.?\s?oz", "floz") #將fl.oz或fl . oz轉換爲floz
若是是一些比較複雜的狀況,則須要將repl自定義爲函數:網站
1) 將1.101000變爲1.101,即將後面的"0"去掉。3d
remove0 = lambda m:m.group(0).rstrip("0") X.str.replace(r"\d\.\d*[1-9]+0+", remove0)
上例中將repl定義爲一個匿名函數,m.group(0)
爲匹配到的全部字符串,注意其不會匹配到1.000的狀況,由於pat中存在[1-9]。code
2) 將1.000kg變爲1kg,這裏由於要去除的.
和0
兩個字符位於中間,因此沒法用上面的rstrip()
。htm
table1 = str.maketrans("","","0.") remove1 = lambda m:m.group(0).translate(table1) X.str.replace(r"\.0+[a-z]+", remove1)
上例中使用str.maketrans()
方法指定想要刪除的字符,再用translate()
刪除。這是python 3的寫法,python 2中可直接使用translate()
:blog
# python 2 remove1 = lambda m:m.group(0).translate(None,"0.") X.str.replace(r"\.0+[a-z]+", remove1)
3) 將0.0300kg轉換爲0.03kg。這裏因爲0.03自己存在0,因此不能用str.maketrans()
了,由於會將全部0都刪除。因此這裏用兩個正則表達式分別找到0.03和kg,再拼接起來:
def remove2(data): al1 = re.findall(r"\d+\.\d*[1-9]+0+",data.group(0)) al2 = re.findall(r"[a-z]+",data.group(0)) return al1[0].rstrip("0") + al2[0] X.str.replace(r"\d+\.\d*[1-9]+0+[a-z]+", remove2)
4) 將1.100%轉換爲1 100%, 這麼作的目的是1.100%可能會被轉換爲一個詞,而實際想要提取的確定只有100%:
def remove3(data): al1 = re.findall(r"\d+(?=\.)", data.group(0)) # 這裏使用了零寬斷言(?=),是爲了去除」.「 al2 = re.findall(r"100%", data.group(0)) return al1[0]+" "+al2[0] X.str.replace(r"\d+\.100%", remove3)
5) 商品中有不少衣服鞋子之類的,通常都標有尺碼,好比3",15」等。這裏要把後面的尺碼符號‘ 」 ’提取出來並用「colon」表示,讓模型識別出前面的數字3和15是表明尺碼大小。
def findcolon(data): al1 = re.findall(r'\d{1,2}\.\d{1,3}|\d{1,2}|1\d{2}', data.group(0)) return al1[0]+" colon " X.str.replace(r'(?:\d{1,2}\.\d{1,3}|\d{1,2}|1\d{2})(?:\s?\")', findcolon) # 匹配2.3「, 55", 132"等,轉換爲2.3 colon
由此,本文結合比賽中的例子介紹了幾種清洗文本的方法,另外Pandas中還提供了其餘不少有用的處理文本的方法,詳見文檔 Working with Text Data 。
/