天天看點

使用Pandas: str.replace() 進行文本清洗

前段時間參加了Kaggle上的 Mercari Price Suggestion Challenge

比賽,收獲良多,過些時候準備進行一些全面的總結,本篇文章先談一個比賽中用到的小技巧。

這個比賽資料中有一個特征叫做 "item_description",大緻是一些商品描述,比如什麼時候買的、新舊程度如何、什麼牌子的等等。因為大部分都是Mercari這個網站(這個類似于國内的二手商品交易網站)上的使用者自己填的商品描述,是以是極盡雜亂之能事,會出現很多誇張的符号,比如這樣:

使用Pandas: str.replace() 進行文本清洗
另外的一大問題是用語規範不統一,比如

$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為要替換進去的字元串或函數。

下面是幾個簡單的例子,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"去掉。

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]。

2) 将1.000kg變為1kg,這裡因為要去除的

.

兩個字元位于中間,是以無法用上面的

rstrip()

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()

# 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

/