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 |