2020-08-05--Pandas-04--文本數據處理

str屬性

文本數據也就是我們常說的字符串,Pandas 為 Series 提供了 str 屬性,通過它可以方便的對每個元素進行操作。

import pandas as pd
import numpy as np

index = pd.Index(data=["Tom", "Bob", "Mary", "James", "Andy", "Alice"], name="name")

data = {
    "age": [18, 30, np.nan, 40, np.nan, 30],
    "city": ["Bei Jing ", "Shang Hai ", "Guang Zhou", "Shen Zhen", np.nan, " "],
    "sex": [None, "male", "female", "male", np.nan, "unknown"],
    "birth": ["2000-02-10", "1988-10-17", None, "1978-08-08", np.nan, "1988-10-17"]
}

user_info = pd.DataFrame(data=data, index=index)

# 將出生日期轉為時間戳
user_info["birth"] = pd.to_datetime(user_info.birth)
print(user_info)
#         age        city      sex      birth
# name
# Tom    18.0   Bei Jing      None 2000-02-10
# Bob    30.0  Shang Hai      male 1988-10-17
# Mary    NaN  Guang Zhou   female        NaT
# James  40.0   Shen Zhen     male 1978-08-08
# Andy    NaN         NaN      NaN        NaT
# Alice  30.0              unknown 1988-10-17

在之前已經了解過,在對 Series 中每個元素處理時,我們可以使用 map 或 apply 方法。

比如,我想要將每個城市都轉為小寫,可以使用map()/apply()函數來處理。

user_info.city = user_info.city.map(lambda x: x.lower())
print(user_info)       # Series對象
# AttributeError: 'float' object has no attribute 'lower'

報錯信息:float類型的數據沒有lower屬性

但是如果數據中的數據類型過于復雜的話,什么類型都有,不能保證每個數據都有lower(),所以要使用str()

str()

# 將city轉為小寫,先轉為str類型
user_info.city = user_info.city.str.lower()
print(user_info)
#         age        city      sex      birth
# name
# Tom    18.0   bei jing      None 2000-02-10
# Bob    30.0  shang hai      male 1988-10-17
# Mary    NaN  guang zhou   female        NaT
# James  40.0   shen zhen     male 1978-08-08
# Andy    NaN         NaN      NaN        NaT
# Alice  30.0              unknown 1988-10-17
# str屬性限定初始值為objext類型Series對象

可以看到,通過 str 屬性來訪問之后用到的方法名與 Python 內置的字符串的方法名一樣。并且能夠自動排除缺失值。

我們再來試試其他一些方法。例如,統計每個字符串的長度。

# 統計city列的字符長度
user_info.city = user_info.city.str.len()
print(user_info)
#         age  city      sex      birth
# name                                 
# Tom    18.0   9.0     None 2000-02-10
# Bob    30.0  10.0     male 1988-10-17
# Mary    NaN  10.0   female        NaT
# James  40.0   9.0     male 1978-08-08
# Andy    NaN   NaN      NaN        NaT
# Alice  30.0   1.0  unknown 1988-10-17

這都是針對于Series對象的,返回的都是Series對象。

這里做的是原地操作:改變了原數據,將修改后的Series對象傳給user_info的city列(Series對象)中,達到類似于替換的效果。

替換和分割

使用 .str 屬性也支持替換與分割操作。

先來看下替換操作,例如:將空字符串替換成下劃線。

# 替換字符
user_info.city = user_info.city.str.replace(' ','_')
print(user_info)
#         age        city      sex      birth
# name                                       
# Tom    18.0   bei_jing_     None 2000-02-10
# Bob    30.0  shang_hai_     male 1988-10-17
# Mary    NaN  guang_zhou   female        NaT
# James  40.0   shen_zhen     male 1978-08-08
# Andy    NaN         NaN      NaN        NaT
# Alice  30.0           _  unknown 1988-10-17

replace 方法還支持正則表達式,例如將所有開頭為 s 的城市替換為空字符串。

# 正則匹配替換字符
user_info.city = user_info.city.str.replace("^s.*", " ")
print(user_info)
#         age        city      sex      birth
# name                                       
# Tom    18.0   bei_jing_     None 2000-02-10
# Bob    30.0                 male 1988-10-17
# Mary    NaN  guang_zhou   female        NaT
# James  40.0                 male 1978-08-08
# Andy    NaN         NaN      NaN        NaT
# Alice  30.0           _  unknown 1988-10-17

分割

例如根據空字符串來分割某一列。

# 分割
user_info.city = user_info.city.str.split(' ')
print(user_info)
#         age            city      sex      birth
# name
# Tom    18.0   [Bei, Jing, ]     None 2000-02-10
# Bob    30.0  [Shang, Hai, ]     male 1988-10-17
# Mary    NaN   [Guang, Zhou]   female        NaT
# James  40.0    [Shen, Zhen]     male 1978-08-08
# Andy    NaN             NaN      NaN        NaT
# Alice  30.0            [, ]  unknown 1988-10-17

分割列表中的元素可以使用 .str.get(index).str[index]符號進行訪問:

# 分割并取值
user_info.city = user_info.city.str.split(' ').str[1]
print(user_info)
# .str返回:<pandas.core.strings.StringMethods object at 0x0000010A2E13A7C8>

# 數據修改為:
#         age  city      sex      birth
# name
# Tom    18.0  Jing     None 2000-02-10
# Bob    30.0   Hai     male 1988-10-17
# Mary    NaN  Zhou   female        NaT
# James  40.0  Zhen     male 1978-08-08
# Andy    NaN   NaN      NaN        NaT
# Alice  30.0        unknown 1988-10-17

設置參數 expand=True 可以輕松擴展此項以返回 DataFrame。

df = user_info.city.str.split(' ',expand=True)
print(df)
#            0     1     2
# name                    
# Tom      Bei  Jing      
# Bob    Shang   Hai      
# Mary   Guang  Zhou  None
# James   Shen  Zhen  None
# Andy     NaN   NaN   NaN
# Alice               None

提取子串

既然是在操作字符串,很自然,你可能會想到是否可以從一個長的字符串中提取出子串。答案是可以的。

1.提取第一個匹配的子串

extract('正則',expand=True)

extract 方法接受一個正則表達式并至少包含一個捕獲組,指定參數 expand=True 可以保證每次都返回 DataFrame。

例如,現在想要匹配空字符串前面的所有的字母,可以使用如下操作:

print(user_info)
#         age        city      sex      birth
# name
# Tom    18.0   Bei Jing      None 2000-02-10
# Bob    30.0  Shang Hai      male 1988-10-17
# Mary    NaN  Guang Zhou   female        NaT
# James  40.0   Shen Zhen     male 1978-08-08
# Andy    NaN         NaN      NaN        NaT
# Alice  30.0              unknown 1988-10-17

# 匹配空字符串之前的內容
c = user_info.city.str.extract('(\w+)\s+',expand=True)
print(c)               # DataFrame類型
#            0
# name
# Tom      Bei
# Bob    Shang
# Mary   Guang
# James   Shen
# Andy     NaN
# Alice    NaN

如果使用多個組提取正則表達式會返回一個 DataFrame,每個組只有一列。

例如,想要匹配出空字符串前面和后面的所有字母,操作如下:

# 匹配空格前后的所有字符串,返回DataFrame
c = user_info.city.str.extract('(\w+)\s+(\w+)',expand=True)
print(c)
#            0     1
# name
# Tom      Bei  Jing
# Bob    Shang   Hai
# Mary   Guang  Zhou
# James   Shen  Zhen
# Andy     NaN   NaN
# Alice    NaN   NaN

匹配所有子串

extract 只能夠匹配出第一個子串,使用 extractall 可以匹配出所有的子串。

例如,將所有組的空白字符串前面的字母都匹配出來,可以如下操作。

# 匹配所有空格之前的字符串
c = user_info.city.str.extractall("(\w+)\s+")
print(c)
#                  0
# name  match
# Tom   0        Bei
#       1       Jing
# Bob   0      Shang
#       1        Hai
# Mary  0      Guang
# James 0       Shen

測試是否包含子串

除了可以匹配出子串外,我們還可以使用 contains 來測試是否包含子串。例如,想要測試城市是否包含子串 “Hai”。

# 匹配包含city項包含Hai的數據,返回Series,數據為bool值
c = user_info.city.str.contains('Hai')
print(c)
# name
# Tom      False
# Bob       True
# Mary     False
# James    False
# Andy       NaN
# Alice    False
# Name: city, dtype: object

# 將上邊返回的bool值的Series作為花式索引
user_info = user_info[c]
print(user_info)             # 返回DataFrame對象
#        age        city   sex      birth
# name
# Bob   30.0  Shang Hai   male 1988-10-17

返回的bool值的Series對象可以作為花式索引,在DataFrame中獲取相應的數據項。、

當然了,正則表達式也是支持的。例如,想要測試是否是以字母 “S” 開頭。

c = user_info.city.str.contains("^S")

生成啞變量

這是一個神奇的功能,通過 get_dummies 方法可以將字符串轉為啞變量,sep 參數是指定啞變量之間的分隔符。來看看效果吧。、

# 啞變量
c = user_info.city.replace(' ','').str.get_dummies()
print(c)
#        Bei Jing   Guang Zhou  Shang Hai   Shen Zhen  zhengzhou
# name                                                          
# Tom            1           0           0          0          0
# Bob            0           0           1          0          0
# Mary           0           1           0          0          0
# James          0           0           0          1          0
# Andy           0           0           0          0          1
# Alice          0           0           0          0          0

這樣,它提取出了 Bei, Guang, Hai, Jing, Shang, Shen, Zhen, Zhou 這些啞變量,并對每個變量下使用 0 或 1 來表達。實際上與 One-Hot(狂熱編碼)是一回事。聽不懂沒關系,之后將機器學習相關知識時會詳細介紹這里。

方法摘要

方法 描述
cat() 連接字符串
split() 在分隔符上分割字符串
rsplit() 從字符串末尾開始分隔字符串
get() 索引到每個元素(檢索第i個元素)
join() 使用分隔符在系列的每個元素中加入字符串
get_dummies() 在分隔符上分割字符串,返回虛擬變量的DataFrame
contains() 如果每個字符串都包含pattern / regex,則返回布爾數組
replace() 用其他字符串替換pattern / regex的出現
repeat() 重復值(s.str.repeat(3)等同于x * 3 t2 >)
pad() 將空格添加到字符串的左側,右側或兩側
center() 相當于str.center
ljust() 相當于str.ljust
rjust() 相當于str.rjust
zfill() 等同于str.zfill
wrap() 將長長的字符串拆分為長度小于給定寬度的行
slice() 切分Series中的每個字符串
slice_replace() 用傳遞的值替換每個字符串中的切片
count() 計數模式的發生
startswith() 相當于每個元素的str.startswith(pat)
endswith() 相當于每個元素的str.endswith(pat)
findall() 計算每個字符串的所有模式/正則表達式的列表
match() 在每個元素上調用re.match,返回匹配的組作為列表
extract() 在每個元素上調用re.search,為每個元素返回一行DataFrame,為每個正則表達式捕獲組返回一列
extractall() 在每個元素上調用re.findall,為每個匹配返回一行DataFrame,為每個正則表達式捕獲組返回一列
len() 計算字符串長度
strip() 相當于str.strip
rstrip() 相當于str.rstrip
lstrip() 相當于str.lstrip
partition() 等同于str.partition
rpartition() 等同于str.rpartition
lower() 相當于str.lower
upper() 相當于str.upper
find() 相當于str.find
rfind() 相當于str.rfind
index() 相當于str.index
rindex() 相當于str.rindex
capitalize() 相當于str.capitalize
swapcase() 相當于str.swapcase
normalize() 返回Unicode標準格式。相當于unicodedata.normalize
translate() 等同于str.translate
isalnum() 等同于str.isalnum
isalpha() 等同于str.isalpha
isdigit() 相當于str.isdigit
isspace() 等同于str.isspace
islower() 相當于str.islower
isupper() 相當于str.isupper
istitle() 相當于str.istitle
isnumeric() 相當于str.isnumeric
isdecimal() 相當于str.isdecimal
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。