Kaggle:Home Depot Product Search Revelant——数据预处理代码分析和实现

本文主要是介绍Kaggle:Home Depot Product Search Revelant——数据预处理代码分析和实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里,我们将实现对文本的预处理工作,下面所有的代码都可以在https://github.com/ChenglongChen/Kaggle_HomeDepot中找到。

加载一些必要的包:

import csv
import imp  # 这个模块是用来实现import方法的,比如可以实现释放全局的import琐# 意思是我可以将给定路径的.py文件以包的形式加载import nltk
import regex # 这是一个比re更强大的正则表达式的包
import numpy as np
import pandas as pd
import multiprocessing  # 实现多进程
from bs4 import BeautifulSoup
from collections import Counter

对于imp包,我们可以看看这样一个例子:这里的color_data.py是的一个Python文件,而COLOR_LIST是写在我们文件里的一个列表,这里的司仪就是我们.py文件当做模块读入,然后我们能直接访问里面的变量。

color_data=imp.load_source("","G:/kaggle/nlp/home_depot/Kaggle_HomeDepot-master/Data/dict/color_data.py")
color_data.COLOR_LIST

1 pattern_replace基类

我们初始化一个类,传入一个pattern_replace_pair_list,把满足pattern(正则)的替换为replace(tuple格式),并且去掉开头结尾的空格。

class BaseReplacer:def __init__(self,pattern_replace_pair_list=[]):self.pattern_replace_pair_list=pattern_replace_pair_listdef transform(self,text):for patter,replace in self.pattern_replace_pair_list:try:text=regex.sub(pattern,replace,text)except:passreturn regex.sub(r"\s+"," ",text).strip()

我们下面要将这个类作为父类,对文本进行一些处理,包括:

  • 移除括号中的单词或用点替换括号
  • 移除数字之间的逗号(比如10,000转换成10000)
  • 在数字(左)和字母(右)之间添加点和空格
  • 如果一个数字的左边有至少3个字母,则在数字和字母之间增加空格
  • 在字母之间用空格替换"/"和"\"
  • 由于产品描述是文本链而成的,所以会导致像firstSecond这样的词出现,要用正则表达式将其分开
  • 等等。。。。。。。。。。

1.1 大写转换为小写

class LowerCaseConverter(BaseReplacer):"""Traditional -> traditional"""def transform(self,text):return text.lower()

1.2 将文本中本应该分开的词分开

class LowerUpperCaseSplitter(BaseReplacer):def __init__(self):self.pattern_replace_pair_list=[(r"(\w)[\.?!]([A-Z])",r"\1 \2"),(r"(?<=( ))([a-z]+)([A-Z]+)",r"\2 \3"),]

这里使用了正则表达式,不了解正则表达式的同学可以看看http://www.regexlab.com/zh/regref.htm,或者自行百度。

这里主要将如“firstSecond”或者“first.Second”的形式替换成了“first Second”。可以看看如下实验:

lp=LowerUpperCaseSplitter()
text="hidden from viewDurable rich finishLimited lifetime warrantyEncapsulated panels"
print(lp.transform(text))text="product.Excellent"
print(lp.transform(text))

输出:

hidden from view Durable rich finish Limited lifetime warranty Encapsulated panels
product Excellent

1.3 进行一些单词的替换

这些替换包括了如果如fisrtsecond => first second,并且所给的数据中search term里面存在大量的拼写错误,我们要进行替换,还包括拓展了一些单词的缩写以及一些单词的stemming,lemma等等。

# 我们传入一个我们字典的路径
class WordReplacer(BaseReplacer):def __init__(self,replace_name):self.replace_name=replace_nameself.pattern_replace_pair_list=[]for line in csv.reader(open(self.replace_name)):if len(line)==1 and line[0].startswith("#"):continuetry:pattern=r"(?<=\W|^)%s(?=\W|$)" % line[0]replace=line[1]self.pattern_replace_pair_list.append((pattern,replace))except:print(line)pass

其中正则表达式表示r"(?<=\W|^)%s(?=\W|$)",我们所要替换的单词的左侧必须是非字母非数字,或者是字符串的开头,右侧必须是非字母非数字,或者是字符串的结尾。

例子:

# 单词拆分
wr=WordReplacer("G:/kaggle/nlp/home_depot/Kaggle_HomeDepot-master/Data/dict/word_replacer.csv")
text="zeroturn"
print(wr.transform(text))# 拼写纠正
text="repir"
print(wr.transform(text))

输出:

zero turn
repair

1.3.1 关于单词拼写纠错

关于单词拼写纠错,这里有一个很有意思的做法是https://www.kaggle.com/steubk/fixing-typos/notebook,大概的思路就是使用google搜索的时候,如果所填的单词是错误的,那么google就会帮忙纠正,就像这样

然后这可以用爬虫爬下来,然后国内要科学上网。。。。所以我尝试着用requests+xpath写了的百度的版本,发现很鸡肋很鸡肋。。。以后有时间再看看。

import requests
from lxml import etree
import time
from random import randint# 首先是一个单词纠错
def fixing_typos(q):time.sleep(randint(0,2))url="https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd="+qheaders={'User-Agent':'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0'}response=requests.get(url,headers=headers)html=etree.HTML(response.text)new_word=html.xpath('//div[@class="c-gap-bottom-small f13"]/span/strong[2]/text()')[0]return new_wordif __name__=="__main__":print(fixing_typos("abaut"))

1.4 分割连接符连接的单词

 去掉字母之间的连接符,但是不会去掉数字之间的

class LetterLetterSplitter(BaseReplacer):def __init__(self):self.pattern_replace_pair_list=[(r"([a-zA-Z]+)[/\-]([a-zA-Z])",r"\1 \2")]

例子:

lls=LetterLetterSplitter()
text="Cleaner/Conditioner"
print(lls.transform(text))

输出:

Cleaner Conditioner

1.5 去掉字母和数字间的连接符

class DigitLetterSplitter(BaseReplacer):def __init__(self):self.pattern_replace_pair_list=[(r"(\d+)[\.\-]([a-zA-Z]+)",r"\1 \2"),(r"([a-zA-Z]+)[\.\-](\d+)",r"\1 \2"),]

例子:

dlp=DigitLetterSplitter()
text="1-Gang"
print(dlp.transform(text))

输出:

1 Gang

1.6 去掉数字之间的逗号

class DigitCommaDigitMerger(BaseReplacer):def __init__(self):self.pattern_replace_pair_list=[(r"(?<=\d+),(?=000)",r"")]

例子:

dcdm=DigitCommaDigitMerger()
text="1,000,000"
print(dcdm.transform(text))

输出:

1000000

1.7 将英语形式的number变换为阿拉伯数字

class NumberDigitMapper(BaseReplacer):def __init__(self):numbers = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten","eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen","nineteen", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,16, 17, 18, 19, 20, 30, 40, 50, 60, 70, 80, 90]self.pattern_replace_pair_list=[(r"(?<=\W|^)%s(?=\W|$)" % n,str(d)) for n,d in zip(numbers,digits)]

例子:

ndm=NumberDigitMapper()
text="two"
print(ndm.transform(text))

输出:2

1.8 统一单位

class UnitConverter(BaseReplacer):"""shadeMature height: 36 in. - 48 in.Mature widthPUT one UnitConverter before LowerUpperCaseSplitter"""def __init__(self):self.pattern_replace_pair_list = [(r"([0-9]+)( *)(inches|inch|in|in.|')\.?", r"\1 in. "),(r"([0-9]+)( *)(pounds|pound|lbs|lb|lb.)\.?", r"\1 lb. "),(r"([0-9]+)( *)(foot|feet|ft|ft.|'')\.?", r"\1 ft. "),(r"([0-9]+)( *)(square|sq|sq.) ?\.?(inches|inch|in|in.|')\.?", r"\1 sq.in. "),(r"([0-9]+)( *)(square|sq|sq.) ?\.?(feet|foot|ft|ft.|'')\.?", r"\1 sq.ft. "),(r"([0-9]+)( *)(cubic|cu|cu.) ?\.?(inches|inch|in|in.|')\.?", r"\1 cu.in. "),(r"([0-9]+)( *)(cubic|cu|cu.) ?\.?(feet|foot|ft|ft.|'')\.?", r"\1 cu.ft. "),(r"([0-9]+)( *)(gallons|gallon|gal)\.?", r"\1 gal. "),(r"([0-9]+)( *)(ounces|ounce|oz)\.?", r"\1 oz. "),(r"([0-9]+)( *)(centimeters|cm)\.?", r"\1 cm. "),(r"([0-9]+)( *)(milimeters|mm)\.?", r"\1 mm. "),(r"([0-9]+)( *)(minutes|minute)\.?", r"\1 min. "),(r"([0-9]+)( *)(°|degrees|degree)\.?", r"\1 deg. "),(r"([0-9]+)( *)(v|volts|volt)\.?", r"\1 volt. "),(r"([0-9]+)( *)(wattage|watts|watt)\.?", r"\1 watt. "),(r"([0-9]+)( *)(amperes|ampere|amps|amp)\.?", r"\1 amp. "),(r"([0-9]+)( *)(qquart|quart)\.?", r"\1 qt. "),(r"([0-9]+)( *)(hours|hour|hrs.)\.?", r"\1 hr "),(r"([0-9]+)( *)(gallons per minute|gallon per minute|gal per minute|gallons/min.|gallons/min)\.?", r"\1 gal. per min. "),(r"([0-9]+)( *)(gallons per hour|gallon per hour|gal per hour|gallons/hour|gallons/hr)\.?", r"\1 gal. per hr "),]

1.9 消除Html标签

这里使用了bs4解析文本消除html标签,这里用了自己定义的parser,我之前在用的是bs4里面的html.parser

# 我们使用bs4解析html消除标签
class HtmlCleaner:def __init__(self,parser):self.parser=parserdef transform(self,text):bs=BeautifulSoup(text,self.parser)text=bs.get_text(separator=" ")return text

1.10 将一些乱码和标点替换成原始标点

class QuartetCleaner(BaseReplacer):def __init__(self):self.pattern_replace_pair_list = [(r"<.+?>", r""),# html codes(r"&nbsp;", r" "),(r"&amp;", r"&"),(r"&#39;", r"'"),(r"/>/Agt/>", r""),(r"</a<gt/", r""),(r"gt/>", r""),(r"/>", r""),(r"<br", r""),# do not remove [".", "/", "-", "%"] as they are useful in numbers, e.g., 1.97, 1-1/2, 10%, etc.(r"[ &<>)(_,;:!?\+^~@#\$]+", r" "),("'s\\b", r""),(r"[']+", r""),(r"[\"]+", r""),]

1.11 词形还原(lemma)和词根提取(stem)

在做lemmatizing的时候,我们首先使用nltk包中的宾州Tree Bank分词器进行分词(nltk中多个分词器的效果对比https://blog.csdn.net/zhuzuwei/article/details/80485318),然后使用wordnet词形还原器进行词形还原。

class Lemmatizer:def __init__(self):self.Tokenizer=nltk.tokenize.TreebankWordTokenizer()self.Lemmatizer=nltk.stem.WordNetLemmatizer()def transform(self,text):tokens=[self.Lemmatizer.lemmatize(word) for word in self.Tokenizer.tokenize(text)]return " ".join(tokens)

在做stemming的时候,这里可以选择nltk中的porter或者snowball进行词根提取(其实感觉这两个效果差不多)

# 词根提取
class Stemming:def __init__(self,stemmer_type="snowball"):self.stemmer_type=stemmer_typeif self.stemmer_type=="porter":self.stemmer=nltk.stem.PorterStemmer()elif self.stemmer_type=="snowball":self.stemmer=nltk.SnowballStemmer("english")def transform(self,text):tokens=[self.stemmer.stem(word) for word in text.split(" ")]return " ".join(tokens)

1.12 Query和Title拓展

因为在product title中的一些单词之间的结构能够帮助我们找到文档中的重要的内容,比如从’Husky 52 in. 10-Drawer Mobile Workbench with Solid Wood Top, Black’这个title中,我们就可以知道这个产品是workbench而不是wood top。对于product title这样的短文本,使用ngram进行丰富,这里我们使用的是trigram。

首先来实现unigrams、bigrams和trigrams

# unigrams,bigrams和trigrams实现
def _unigrams(words):assert type(words)==listreturn wordsdef _bigrams(words,join_string,skip=0):assert type(words)==listL=len(words)lst=[]if L>1:for i in range(L-1):for k in range(1,skip+2):if i+k<L:lst.append(join_string.join([words[i],words[i+k]]))else:lst=_unigrams(words)return lstdef _trigrams(words,join_string,skip=0):assert type(words)==listL=len(words)lst=[]if L>2:for i in range(L-2):for j in range(1,skip+2):for k in range(1,skip+2):if i+j+k<L:lst.append(join_string.join([words[i],words[i+j],words[i+j+k]]))else:lst=_bigrams(words,join_string,skip=0)return lst
# 更多的ngrams以此类推。。。

例子:

words=["I","am","Sun"]
# unigrams
print(_unigrams(words))
# bigrmas
print(_bigrams(words,"_",skip=0))
# trigrams
print(_trigrams(words,"_",skip=0))

输出:

['I', 'am', 'Sun']
['I_am', 'am_Sun']
['I_am_Sun']

然后来实现文本拓展,我们将文本中出现次数至少的90%,当做停用词,结合我们常用的停用词表,将这些从文本中去除,我们先对product_title使用trigrams模型对文本进行拓展,然后对于同一个搜索关键词(search_term),我们寻找其对应的出现最多的trigram结构,我们这个结构对于search_terms越重要。

class QueryExpansion:def __init__(self,ngram=3,stopwords_threshoold=0.9,base_stopwords=set()):self.df=df[["search_term","product_title"]].copy()self.ngram=ngramself.stopwords_threshoold=stopwords_threshooldself.stopwords=set(base_stopwords).union(self._get_customized_stopwords())# 根据所设置的阈值,将一些词设置成stopwords   def _get_customized_stopwords(self):words=" ".join(list(self.df["product_title"].values)).split(" ")counter=Counter(words)num_uniq=len(counter)num_stop=int((1.-self.stopwords_threshoold)*num_uniq)stopwords=set()for e,(w,c) in enumerate(counter):if e==num_stop:breakstopwords.add(w)return stopwords# 实现ngram模型def _ngram(self,text):tokens=text.split(" ")tokens=[word for word in tokens if word not in self.stopwords]return _trigrams(tokens," ")# 寻找出现次数最多的词,我们认为词出现的次数越多,对于这个search_term越重要def _get_alternative_query(self,df):res=[]for v in df:res+=vc=Counter(res)value,count=c.most_common()[0]return value# 目标找出对于同一个search_term出现次数最多的trigramdef build(self):self.df["title_ngram"]=self.df["product_title"].apply(self._ngram)corpus=self.df.groupby("search_term").apply(lambda df:self._get_alternative_query(df["title_ngram"]))corpus=corpus.reset_index()corpus.columns=["search_term","search_term_alt"]self.df=pd.merge(self.df,corpus,on="search_term",how="left")return self.df["search_term_alt"].values

1.13 处理product_title

# 首先定义一些正则表达式
color_data=imp.load_source("","G:/kaggle/nlp/home_depot/Kaggle_HomeDepot-master/Data/dict/color_data.py")
COLORS_PATTERN=r"(?<=\W|^)%s(?=\W|$)" % "|".join(color_data.COLOR_LIST)
UNITS=[" ".join(r.strip().split(" ")[1:]) for p,r in UnitConverter().pattern_replace_pair_list]
UNITS_PATTERN = r"(?:\d+[?:.,]?\d*)(?: %s\.*)?"%("|".join(UNITS))
DIM_PATTERN_NxNxN = r"%s ?x %s ?x %s"%(UNITS_PATTERN, UNITS_PATTERN, UNITS_PATTERN)
DIM_PATTERN_NxN = r"%s ?x %s"%(UNITS_PATTERN, UNITS_PATTERN)class ProductNameExtractor(BaseReplacer):def __init__(self):self.pattern_replace_pair_list = [# 去掉括号里的文本描述("[ ]?[[(].+?[])]", r""),# 去掉 "made in..."("made in [a-z]+\\b", r""),# 去掉连词符,逗号后面有空格,最多我们两个单词或数字("([,-]( ([a-zA-Z0-9]+\\b)){1,2}[ ]?){1,}$", r""),# Remove descriptions (prepositions staring with: with, for, by, in )("\\b(with|for|by|in|w/) .+$", r""),# 去掉colors和sizes("size: .+$", r""),("size [0-9]+[.]?[0-9]+\\b", r""),(COLORS_PATTERN, r""),# dimensions(DIM_PATTERN_NxNxN, r""),(DIM_PATTERN_NxN, r""),# measurement units(UNITS_PATTERN, r""),# others("(value bundle|warranty|brand new|excellent condition|one size|new in box|authentic|as is)", r""),# stop words("\\b(in)\\b", r""),# hyphenated words("([a-zA-Z])-([a-zA-Z])", r"\1\2"),# special characters("[ &<>)(_,.;:!?/+#*-]+", r" "),# numbers that are not part of a word("\\b[0-9]+\\b", r""),]# 使用我们上面定义的一系列的类进行文本处理def preprocess(self, text):pattern_replace_pair_list = [# Remove single & double apostrophes("[\"]+", r""),# Remove product codes (long words (>5 characters) that are all caps, numbers or mix pf both)# don't use raw string format("[ ]?\\b[0-9A-Z-]{5,}\\b", ""),]text = BaseReplacer(pattern_replace_pair_list).transform(text)text = LowerCaseConverter().transform(text)text = DigitLetterSplitter().transform(text)text = UnitConverter().transform(text)text = DigitCommaDigitMerger().transform(text)text = NumberDigitMapper().transform(text)text = UnitConverter().transform(text)return textdef transform(self,text):# super()函数在这里的意思是我调用了父类BaseReplace中的transform方法text=super().transform(self.preprocess(text))# 词形还原和词根提取text=Lemmatizer().transform(text)text=Stemming(stemmer_type="snowball").transform(text)# 取文本中的最后两个单词text=" ".join(text.split(" ")[-2:])return text

1.14 处理商品的属性

根据输出的不同格式写两种方法

# 根据输出的不同格式写两个方法
def _split_attr_to_text(text):attrs=text.split(" | ")return " ".join(attrs)def _split_attrs_to_list(text):attrs=text.split(" | ")if len(attrs)==1:return [[attrs[0],attrs[0]]]else:return [[n,v] for n,v in zip(attrs[::2],attrs[1::2])]

1.15 处理不同结构的数据输入输出和dataframe的多进程

一方面,不同的数据可能放在列表里,也可能放在数据框里,同时单个数据的格式可能是str,float,int,我们都需要把他们格式化为str进行处理;另一方面,我们对以列表的形式封装多个处理类。

这里使用了python中的multiprocessing实现多进程,我们使用进程池pool,然后两个异步方法Imap和map_async之间的区别可以看看这个https://stackoverflow.com/questions/26520781/multiprocessing-pool-whats-the-difference-between-map-async-and-imap。

class ProcessorWrapper:def __init__(self,processor):self.processor=processordef transform(self,input):if isinstance(input,str):out=self.processor.transform(input)elif isinstance(input,float) or isinstance(input,int):out=self.processor.transform(sytr(input))elif isinstance(input,list):out=[0]*len(input)for i in range(len(input)):out[i]=ProcessorWrapper(self.processor).transform(input[i])else:raise(ValueError("Currently not support type: %s" % type(input).__name__))return out# list处理器封装,我们需要对同一个列表使用多个处理器处理的情况
class ListProcessors:def __init__(self,processors):self.processors=processorsdef process(self,lst):for i in range(len(lst)):for processor in self.processors:lst[i]=ProcessorWrapper(processor).transform(lst[i])return lst# dataframe处理器封装
class DataFrameProcessor:def __init__(self,processors):self.processors=processorsdef process(self,df):for process in self.processors:df=df.apply(ProcessorWrapper(processor).transform)return df# 多进程的实现
class DataFrameParalleProcessor:def __init__(self,processors,n_jobs=4):self.processors=processorsself.n_jobs=n_jobsdef prosess(self,dfAll,columns):df_processor=DataFrameProcessor(self.processors)p=multiprocessing.Pool(self.n_jobs)dfs=p.imap(df_processor.process,[dfAll[col] for col in columns])for col,df in zip(columns,dfs):dfAll[col]=dfreturn dfAll

2.文本预处理

写完所有处理的类,就可以正式开始文本处理了。

2.1 首先,进行一个测试

首先,我们进行一个测试,检查一下我们的代码时候起作用。

def main():columns={"product_attribute_concat","product_description","product_brand", "product_color","product_title","search_term", }# 使用一个列表将放置要处理的进程类processors = [ # 单位标准化UnitConverter(),# 将一些连在一起的词分开LowerUpperCaseSplitter(),# 替换一些词WordReplacer(replace_name="G:/kaggle/nlp/home_depot/Kaggle_HomeDepot-master/Data/dict/word_replacer.csv"), # 去掉词与此之间的/\-LetterLetterSplitter(),# 对数字和词之间做一些处理DigitLetterSplitter(), # 去掉数字之间的逗号DigitCommaDigitMerger(), # 将英文的数字转换成阿拉伯数字NumberDigitMapper(),# 再做一次单位标准化UnitConverter(), # 去掉文本中一些没有用的符号QuartetCleaner(), # 去掉文本中的html标签HtmlCleaner(parser="html.parser"),# 转换为小写LowerCaseConverter(),# 词形还原Lemmatizer(),]# 词干提取stemmers = [Stemming(stemmer_type="snowball"), Stemming(stemmer_type="porter")][0:1]# 进行一个简单的测试text="1/2 inch rubber lep tipsBullet07 1,000,000 one"print("Original:")print(text)list_processor=ListProcessors(processors)print("After:")print(list_processor.process([text]))main()

输出:

Original:
1/2 inch rubber lep tipsBullet07 1,000,000 one
After:
['1/2 in. rubber lep tip bullet07 1000000 1']

这里的代码和大神的代码不同的是,我把单词小写化这步放在了词根提取的前面,因为我觉得我们做连贯的大小写单词的分离等步骤的时候都需要保持原有的大写。

2.2 使用pickle进行结构化数据的保存和加载

import pickle
def _save(fname,data,protocal=3):with open(fname,"wb") as f:pickle.dump(data,f,protocal)def _load(fname):with open(fname,"rb") as f:return pickle.load(f)

2.3 对原有数据进行读取和连接

import gc  # 垃圾回收
dfTrain=pd.read_csv("G:/kaggle/nlp/home_depot/train.csv",encoding="ISO-8859-1")
dfTest=pd.read_csv("G:/kaggle/nlp/home_depot/test.csv",encoding="ISO-8859-1")
dfAttr=pd.read_csv("G:/kaggle/nlp/home_depot/attributes.csv")
dfDesc=pd.read_csv("G:/kaggle/nlp/home_depot/product_descriptions.csv")test_size=len(dfTest)
dfTest["relevance"]=np.zeros((test_size))
dfAttr.dropna(how="all",inplace=True)
dfAttr['value']=dfAttr.value.astype("str")# 合并训练集和测试集
dfAll=pd.concat([dfTrain,dfTest],ignore_index=True,axis=0)
del dfTrain
del dfTest
gc.collect()# 将dfAll和dfDesc按照id合并
dfAll=pd.merge(dfAll,dfDesc,on="product_uid",how="left")
MISSING_VALUE_STRING = "MISSINGVALUE"
dfAll.fillna(MISSING_VALUE_STRING,inplace=True)
del dfDesc
gc.collect()# 拼接产品的品牌
dfBrand=dfAttr[dfAttr.name=="MFG Brand Name"][['product_uid','value']].rename(columns={"value":"product_brand"})  # 我们把value的列名替换成了product_brand
dfAll=pd.merge(dfAll,dfBrand,on="product_uid",how="left")
dfAll["product_brand"]=dfAll["product_brand"].values.astype("str")
dfAll.fillna(MISSING_VALUE_STRING,inplace=True)
del dfBrand
gc.collect()# 拼接产品颜色
color_columns = ["product_color", "Color Family", "Color/Finish", "Color/Finish Family"]
dfColor=dfAttr[dfAttr.name.isin(color_columns)][["product_uid","value"]].rename(columns={"value":"product_color"})
dfColor.dropna(how="all",inplace=True)
_agg_color=lambda df:" ".join(list(set(df["product_color"])))
dfColor=dfColor.groupby(["product_uid"]).apply(_agg_color)
dfColor=dfColor.reset_index(name="product_color")
dfColor['product_color']=dfColor['product_color'].values.astype(str)
# 拼接
dfAll=pd.merge(dfAll,dfColor,on="product_uid",how="left")
dfAll.fillna(MISSING_VALUE_STRING,inplace=True)
del dfColor
gc.collect()# 我们把同一个id的其他属性拼接在一起
_agg_attr=lambda df:" | ".join(df["name"]+" | "+df["value"])
dfAttr=dfAttr.groupby(["product_uid"]).apply(_agg_attr)
dfAttr=dfAttr.reset_index(name="product_attribute_concat")
dfAll=pd.merge(dfAll,dfAttr,on="product_uid",how="left")
dfAll.fillna(MISSING_VALUE_STRING,inplace=True)
del dfAttr
gc.collect()# 保存数据
_save("G:/kaggle/nlp/home_depot/all.raw.csv.pkl",dfAll)
dfInfo=dfAll[["id","relevance"]].copy()
_save("G:/kaggle/nlp/home_depot/info.csv.pkl",dfInfo)
del dfAll
del dfInfo
gc.collect()

2.4 数据处理

columns_to_proc={"product_attribute_concat","product_description","product_brand", "product_color","product_title","search_term", }
# 使用一个列表将放置要处理的进程类
processors = [ # 单位标准化UnitConverter(),# 将一些连在一起的词分开LowerUpperCaseSplitter(),# 替换一些词WordReplacer(replace_name="G:/kaggle/nlp/home_depot/Kaggle_HomeDepot-master/Data/dict/word_replacer.csv"), # 去掉词与此之间的/\-LetterLetterSplitter(),# 对数字和词之间做一些处理DigitLetterSplitter(), # 去掉数字之间的逗号DigitCommaDigitMerger(), # 将英文的数字转换成阿拉伯数字NumberDigitMapper(),# 再做一次单位标准化UnitConverter(), # 去掉文本中一些没有用的符号QuartetCleaner(), # 去掉文本中的html标签HtmlCleaner(parser="html.parser"),# 转换为小写LowerCaseConverter(),# 词形还原Lemmatizer(),
]
# 词干提取
stemmers = [Stemming(stemmer_type="snowball"), Stemming(stemmer_type="porter")
][0:1]dfAll=_load("G:/kaggle/nlp/home_depot/all.raw.csv.pkl")
columns_to_proc=[col for col in columns_to_proc if col in dfAll.columns]# 对search_term和product_title进行进行一些提取处理
ext=ProductNameExtractor()
dfAll["search_term_product_name"]=dfAll["search_term"].apply(ext.transform)
dfAll["product_title_product_name"]=dfAll["product_title"].apply(ext.transform)# 因为我们没办法科学上网,所以我们在单词拼写纠正的时候使用现成的字典
dicts=imp.load_source("","G:/kaggle/nlp/home_depot/my_bag/spell_check_dict.py")
def spell_check(text):spell_dict=dicts.spell_check_dictif text in spell_dict.keys():text=spell_dict[text]return text
dfAll["search_term"]=dfAll["search_term"].apply(spell_check)# 然后我们使用前面定义的各种类对数据进行处理
df_processor=DataFrameParalleProcessor(processors)
dfAll=df_processor.process(dfAll,columns_to_proc)# 对属性特征进行两种形式的处理
dfAll["product_attribute"]=dfAll["product_attribute_concat"].apply(_split_attr_to_text)
dfAll["product_attribute_list"]=dfAll["product_attribute_concat"].apply(_split_attrs_to_list)# 文本拓展
list_processor=ListProcessors(processors)
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
# 我们对于stopword也进行上面各种操作
stopwords=set(list_processor.process(list(ENGLISH_STOP_WORDS)))
qe=QueryExpansion(dfAll,ngram=3,stopwords_threshoold=0.9,base_stopwords=stopwords)
dfAll["search_term_alt"]=qe.build()# 保存数据
_save("G:/kaggle/nlp/home_depot/dfAll_preprocessing.pkl",dfAll)

最后的结果:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 240760 entries, 0 to 240759
Data columns (total 14 columns):
id                            240760 non-null int64
product_uid                   240760 non-null int64
product_title                 240760 non-null object
search_term                   240760 non-null object
relevance                     240760 non-null float64
product_description           240760 non-null object
product_brand                 240760 non-null object
product_color                 240760 non-null object
product_attribute_concat      240760 non-null object
search_term_product_name      240760 non-null object
product_title_product_name    240760 non-null object
product_attribute             240760 non-null object
product_attribute_list        240760 non-null object
search_term_alt               240760 non-null object
dtypes: float64(1), int64(2), object(11)
memory usage: 27.6+ MB

小结:

  • 这里使用了父类,避免代码重复
  • 使用多线程(虽然到最后我也没有实现),节省代码运行速度
  • 其实作者还使用了config文件,存放一些变量和路径,防止代码看起来很乱

好的,结束了文本预处理的工作,下面要开始特征提取了,我们将尝试词袋模型,tfidf,主题模型(Lsi,Lda)和词向量嵌入(word2vec,doc2vec)等等方法提取文本特征。

一些参考:

https://www.jianshu.com/p/7f61e11d8bfc

https://blog.csdn.net/sinat_15355869/article/details/80102311

https://www.kaggle.com/c/home-depot-product-search-relevance/discussion/18967

 

这篇关于Kaggle:Home Depot Product Search Revelant——数据预处理代码分析和实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/516120

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形