拯救pandas計劃(13)——提取Series字符串中的數(shù)字并計算
拯救pandas計劃(13)——提取Series字符串中的數(shù)字并計算
最近發(fā)現(xiàn)周圍的很多小伙伴們都不太樂意使用pandas,轉(zhuǎn)而投向其他的數(shù)據(jù)操作庫,身為一個數(shù)據(jù)工作者,基本上是張口pandas,閉口pandas了,故而寫下此系列以讓更多的小伙伴們愛上pandas。
系列文章說明:
系列名(系列文章序號)——此次系列文章具體解決的需求
平臺:
windows 10 python 3.8 pandas >=1.2.4
/ 數(shù)據(jù)需求
需要對下列有著統(tǒng)一格式的字符串提取其中的數(shù)字進行計算。
import?pandas?as?pd
df?=?pd.DataFrame({
????'年區(qū)間':?['1年以內(nèi)',?'無要求',?'1-3年',?'??3-5年??',?'5年以上'],
????'薪資':?['1.3-1.5萬/月',?'6-8千/月',?'1.3萬/月',?'20-30萬/年',?'?30-50萬/年??']
})
df

/ 需求拆解
看標題中說到提取字符串中的數(shù)字,可以使用對列進行遍歷,一個個進行判斷,把數(shù)字拿出來,而且在上面說了統(tǒng)一格式的數(shù)據(jù),怎么給的數(shù)據(jù)看起來還不是統(tǒng)一的,是不是在唬人呢。
單個數(shù)據(jù)來看有點不好判斷,在這也不提供這種想法的代碼,轉(zhuǎn)念一想,整體看來,正則表達式還挺適合這種格式的字符串數(shù)據(jù)提取。之前在拯救pandas計劃(7)——對含金額標志的字符串列轉(zhuǎn)換為浮點類型數(shù)據(jù)中也稍有提到正則表達式在pandas里的使用,這次借助本文的例子看下正則表達式如何提取到所需要的數(shù)字并進行后續(xù)計算。
/ 需求處理
年區(qū)間列提取年數(shù)
① re
首先觀察下數(shù)據(jù)構(gòu)成,字符串中有含數(shù)字和不含數(shù)字的,對于不含數(shù)字的用0代替,含數(shù)字的中間可能有用-進行隔開,可以考慮 使用以下正則表達式:
import?re
re.compile(r'(\d+)?-?(\d+)')
不確定中間的-何時會出現(xiàn),如果出現(xiàn)也只會出現(xiàn)一次,所以在后面加一個?,前面的數(shù)字也難料,同樣加上?,用括號表示需要提取括號里的數(shù)據(jù),雖然可以對括號添加提取的數(shù)據(jù)做一個標簽,但在這里也不用這么做,能分清就行了。
目前有個想法,對提取的年數(shù)進行平均值計算,如果出現(xiàn)0.5就進行四舍五入取整到整年數(shù)。將上述的正則表達式運用到下面的這段函數(shù)里,由于提取到數(shù)字還是字符串格式,需要進行強轉(zhuǎn)成整型。
import?re
def?year_average(data):
????search_year?=?re.search(r'(\d+)?-?(\d+)',?data)
????def?average(args):
????????#?平均值計算
????????x?=?tuple(args)
????????length?=?len(x)
????????return?round(sum(x)?/?length,?0)
????#?如果能提取到數(shù)字則計算平均值
????if?search_year:
????????return?average([int(i)?for?i?in?search_year.groups()?if?i])
????#?否則返回?0?
????else:
????????return?0
df['平均年數(shù)']?=?df['年區(qū)間'].apply(year_average)
運行后數(shù)據(jù)顯示:

通過apply調(diào)用year_average函數(shù),進行正則查找提取并完成后續(xù)計算,邏輯上也比較清晰易懂,之前在拯救pandas計劃(7)——對含金額標志的字符串列轉(zhuǎn)換為浮點類型數(shù)據(jù)中有提到過pd.Series類如果為object類型或者string類型,是有個.str方法,可以針對字符串做一些特性操作,在這其中也有提取函數(shù).str.extract,同樣可以使用正則表達式。
② .str.extract
df_dash?=?df['年區(qū)間'].str.extract(r'(\d+)?-?(\d+)')

順利提取出來了,接下的操作和re部分的一樣,轉(zhuǎn)換成浮點型數(shù)據(jù)再計算平均值,可以看到行號為1的行中,兩列都為np.nan,所以在計算后還是np.nan,需要對np.nan用0填充,對計算結(jié)果進行四舍五入。
df['平均年數(shù)2']?=?df_dash.astype(float).mean(axis=1).fillna(0).round(0)
df

與上一次計算出來的結(jié)果是一致的。
(手動水?。涸瓌?chuàng)CSDN宿者朽命,https://blog.csdn.net/weixin_46281427?spm=1011.2124.3001.5343 ,公眾號A11Dot派)
提取 薪資列中的數(shù)字
在薪資列中,注意到數(shù)字之間多了小數(shù)點,單單小數(shù)點很好解決,為了跟年區(qū)間列的提取有一點區(qū)別,這里需要將年薪資轉(zhuǎn)換為月薪資,且以數(shù)字的形式顯示。
起初的想法是數(shù)字跟漢字分開提取,既然都需要參與計算,可先替換成數(shù)字再整體提取,在萬和千之前增加一個分隔符,避免跟前面的混淆,分隔符可以使用在正則表達式里不屬于元字符的那一類。
#?先替換成數(shù)字,regex=True,用正則方式匹配
df_dash?=?df['薪資'].replace({'萬':?'@10000',?'千':?'@1000',?'月':?'1',?'年':?'12'},?regex=True)

仔細觀察一番,正則表達式如下:
re.compile(r'(\d+\.?\d*)?-?(\d+\.?\d*)?@(\d+)/(\d+)')
使用.str.extract提?。?/p>
df_dash?=?df_dash.str.extract(r'(\d+\.?\d*)?-?(\d+\.?\d*)?@(\d+)/(\d+)').astype(float)

和設想的一樣,生成了4列數(shù)據(jù),在提取之后使用了astype類型轉(zhuǎn)換方法,類型均為float類型。
轉(zhuǎn)換為月薪資:
df_dash.apply(lambda?x:?(x[[0,?1]]?*?x[2])?/?x[3],?axis=1)

通過上述一系列操作,最終將字符串類型的數(shù)據(jù)轉(zhuǎn)換為浮點型數(shù)字格式,也達到了所想要的結(jié)果。
/ 總結(jié)
這里主要使用了.str.extract方法結(jié)合正則表達式提取相關信息,如需了解更多的使用場景可以查看官方文檔,該方法的使用對于正則表達式的掌握程度要求較高,需要理解字符串之間的最小相似類型,編寫適當?shù)谋磉_式,完成數(shù)據(jù)提取。
采桑獻春,移云遮陽。
于二零二二年四月十八日作
