Pandas 字串處理

Pandas 透過 .str accessor 提供了豐富的字串處理方法,可以對整個 Series 進行向量化的字串操作。

基本用法

import pandas as pd

s = pd.Series(['Alice', 'Bob', 'Charlie'])

# 使用 .str 存取字串方法
print(s.str.upper())
0      ALICE
1        BOB
2    CHARLIE
dtype: object

大小寫轉換

s = pd.Series(['Hello World', 'PYTHON', 'pandas'])

s.str.lower()      # 全部小寫
s.str.upper()      # 全部大寫
s.str.title()      # 每個單字首字母大寫
s.str.capitalize() # 只有第一個字母大寫
s.str.swapcase()   # 大小寫互換

移除空白

s = pd.Series(['  hello  ', ' world ', 'python'])

s.str.strip()      # 移除前後空白
s.str.lstrip()     # 移除左邊空白
s.str.rstrip()     # 移除右邊空白

# 移除特定字元
s.str.strip('!')

字串長度

s = pd.Series(['hello', 'world', 'python'])

print(s.str.len())
0    5
1    5
2    6
dtype: int64

搜尋與判斷

contains():是否包含

s = pd.Series(['apple', 'banana', 'cherry'])

print(s.str.contains('an'))
0    False
1     True
2    False
dtype: bool
# 不區分大小寫
s.str.contains('An', case=False)

# 使用正規表達式
s.str.contains(r'^a', regex=True)  # 以 a 開頭

# 搭配過濾
df = pd.DataFrame({'fruit': ['apple', 'banana', 'cherry']})
df[df['fruit'].str.contains('an')]

startswith() / endswith()

s.str.startswith('a')   # 是否以 a 開頭
s.str.endswith('y')     # 是否以 y 結尾

match():正規表達式匹配

s = pd.Series(['A123', 'B456', '789'])
s.str.match(r'^[A-Z]')  # 是否以大寫字母開頭

取代

replace()

s = pd.Series(['hello-world', 'foo-bar'])

# 簡單取代
s.str.replace('-', ' ')

# 使用正規表達式
s.str.replace(r'\d+', 'NUM', regex=True)

# 不區分大小寫
s.str.replace('HELLO', 'hi', case=False)

分割與合併

split()

s = pd.Series(['a,b,c', 'd,e,f'])

# 分割成 list
print(s.str.split(','))
0    [a, b, c]
1    [d, e, f]
dtype: object
# 展開成多個欄位
df = s.str.split(',', expand=True)
print(df)
   0  1  2
0  a  b  c
1  d  e  f
# 限制分割次數
s.str.split(',', n=1)  # 只分割一次

join()

s = pd.Series([['a', 'b'], ['c', 'd', 'e']])
s.str.join('-')
# 0      a-b
# 1    c-d-e

cat():串接字串

s = pd.Series(['a', 'b', 'c'])

# 串接所有元素
s.str.cat()           # 'abc'
s.str.cat(sep='-')    # 'a-b-c'

# 與另一個 Series 串接
s2 = pd.Series(['1', '2', '3'])
s.str.cat(s2, sep='-')  # ['a-1', 'b-2', 'c-3']

擷取與切片

索引存取

s = pd.Series(['hello', 'world'])

s.str[0]      # 第一個字元
s.str[-1]     # 最後一個字元
s.str[1:3]    # 切片

slice()

s.str.slice(0, 3)   # 前三個字元
s.str.slice(2)      # 從第三個字元開始

extract():正規表達式擷取

s = pd.Series(['a1b2', 'c3d4', 'e5f6'])

# 擷取數字
s.str.extract(r'(\d+)')

# 擷取多個群組
df = s.str.extract(r'([a-z])(\d)')
print(df)
   0  1
0  a  1
1  c  3
2  e  5

extractall():擷取所有匹配

s = pd.Series(['a1b2', 'c3d4'])
df = s.str.extractall(r'(\d)')
print(df)

填充與對齊

s = pd.Series(['a', 'bb', 'ccc'])

s.str.pad(5, side='left', fillchar='0')   # 左側填充
s.str.pad(5, side='right', fillchar='0')  # 右側填充
s.str.center(5, fillchar='-')             # 置中
s.str.zfill(5)                            # 左側補零

其他常用方法

s = pd.Series(['hello world', 'foo bar'])

s.str.count('o')       # 計算出現次數
s.str.find('o')        # 找出第一次出現的位置
s.str.rfind('o')       # 找出最後一次出現的位置
s.str.isdigit()        # 是否全部是數字
s.str.isalpha()        # 是否全部是字母
s.str.isalnum()        # 是否全部是英數字
s.str.isnumeric()      # 是否是數值字元
s.str.isspace()        # 是否全部是空白
s.str.repeat(2)        # 重複 2 次
s.str.wrap(5)          # 自動換行

處理空值

字串方法預設會忽略 NaN:

s = pd.Series(['hello', None, 'world'])
print(s.str.upper())
0    HELLO
1      NaN
2    WORLD
dtype: object

如果需要填補空值:

s.str.upper().fillna('')

實際應用

清理資料

df = pd.DataFrame({
    'name': ['  Alice  ', 'BOB', 'charlie']
})

df['name'] = df['name'].str.strip().str.title()

解析資料

df = pd.DataFrame({
    'email': ['alice@gmail.com', 'bob@yahoo.com']
})

# 提取 @ 前面的使用者名稱
df['username'] = df['email'].str.split('@').str[0]

# 提取網域
df['domain'] = df['email'].str.extract(r'@(.+)$')