Pandas Series.str.extract: 文字列からパターンを抽出する


Pandas Series.str.extract: 文字列からパターンを抽出する

pandas.Series.str.extract メソッドは、pandas ライブラリの Series オブジェクトから、正規表現パターンに一致する部分文字列を抽出するために使用されます。データ分析において、テキストデータから特定の情報を取り出すために有用なツールです。

使い方

import pandas as pd

# データの準備
data = pd.Series(["2023-07-15 13:22:00", "2023-07-16 08:45:00", "2023-07-17 10:10:10"])
data.name = "日時"

# 年月日を抽出
extracted_data = data.str.extract(r"(\d{4})-(\d{1,2})-(\d{1,2})", expand=False)
print(extracted_data)

出力

      0     1     2
0  2023  07  15
1  2023  07  16
2  2023  07  17

解説

  • 上記の例では、data という Series オブジェクトに、日時情報を含む文字列が格納されています。
  • str.extract メソッドは、正規表現パターン r"(\d{4})-(\d{1,2})-(\d{1,2})" を引数として受け取り、data 内の各文字列から一致する部分文字列を抽出します。
  • r" は、正規表現パターンであることを示す記号です。
  • (\d{4}) は、4桁の数字を表すパターンです。
  • - は、文字通りのハイフンを表します。
  • expand=False オプションは、抽出結果を Series オブジェクトとして返すことを指定します。

応用例

  • ウェブスクレイピングから取得したHTMLテキストから特定の情報を取り出す
  • ログファイルからエラーメッセージを抽出する
  • 顧客情報データから住所や電話番号を抽出する
  • pandas.Series.str.extract メソッドは、複数のキャプチャグループを定義することができます。
  • 抽出結果を DataFrame オブジェクトとして返すことも可能です。


この例では、Beautiful Soup を使って HTML から住所をスクレイピングし、pandas.Series.str.extract で住所の各要素を抽出します。

import pandas as pd
from bs4 import BeautifulSoup

# HTML データ
html_data = """
<div class="address">
  <span class="street">123 Main Street</span>
  <span class="city">Anytown</span>,
  <span class="state">CA</span>
  <span class="zip-code">95743</span>
</div>
<div class="address">
  <span class="street">456 Elm Street</span>
  <span class="city">Springfield</span>,
  <span class="state">IL</span>
  <span class="zip-code">62704</span>
</div>
"""

# BeautifulSoup を使ってパース
soup = BeautifulSoup(html_data, 'lxml')

# 住所を含む要素を取得
address_elements = soup.find_all('div', class_='address')

# 住所情報を含む Series を作成
addresses = pd.Series([element.text for element in address_elements])

# 正規表現で住所の各要素を抽出
extracted_addresses = addresses.str.extract(
    r"""
    (?P<street>\w+ \w+ Street)  # 番地と通り名
    , (?P<city>\w+)           # 市区町村
    , (?P<state>\w{2})        # 都道府県
    \s (?P<zip-code>\d{5})     # 郵便番号
    """,
    expand=True
)

# 結果を表示
print(extracted_addresses)
      street        city  state  zip-code
0  123 Main Street  Anytown    CA      95743
1  456 Elm Street  Springfield  IL       62704

この例では、pandas.Series.str.extract を使って、ログファイルからエラーメッセージを抽出します。

import pandas as pd

# ログファイルデータ
log_data = """
ERROR: An error occurred while processing the data.
WARNING: Potential issue detected.
INFO: Completed task successfully.
ERROR: Unable to connect to the database.
"""

# ログデータを Series に格納
log_entries = pd.Series(log_data.splitlines())

# エラーメッセージのみを抽出
error_messages = log_entries.str.extract(r"^ERROR: (.*)", expand=False)

# 結果を表示
print(error_messages)
0    An error occurred while processing the data.
3    Unable to connect to the database.
import pandas as pd

# 顧客情報データ
customer_data = """
Alice Johnson, 123 Main St, Anytown, CA 95743, (555) 555-5555
Bob Williams, 456 Elm St, Springfield, IL 62704, (444) 444-4444
Charlie Brown, 789 Oak Ave, Oakville, NY 12345, (333) 333-3333
"""

# 顧客情報データを Series に格納
customer_info = pd.Series(customer_data.split('\n'))

# 住所と電話番号を抽出
extracted_info = customer_info.str.extract(
    r"""
    (?P<name>\w+ \w+),        # 氏名
    \s (?P<address>.+),        # 住所
    \s (?P<zip-code>\d{5}),  # 郵便番号
    \s \((?P<phone_number>\d{3}-\d{3}-\d{4})\)
    """,
    expand=True
)

# 結果を表示
print(extracted_info)


Pandas.Series.str.extract の代替方法

str.split() と str.get() の組み合わせ

  • 利点:
    • シンプルで分かりやすい構文
    • 特定のパターンのみを抽出したい場合に適している
  • 欠点:
    • 複数のキャプチャグループを抽出するには不向き
    • 正規表現よりも複雑なパターンを処理するのが難しい
import pandas as pd

data = pd.Series(["2023-07-15 13:22:00", "2023-07-16 08:45:00", "2023-07-17 10:10:10"])
data.name = "日時"

# 年月日を抽出
extracted_data = data.str.split('-').str.get([0, 1, 2])
print(extracted_data)
      0     1     2
0  2023  07  15
1  2023  07  16
2  2023  07  17

re.findall() 関数

  • 利点:
  • 欠点:
    • pandas APIの一部ではないため、データフレームに直接適用できない
    • 処理速度が pandas.Series.str.extract よりも遅い場合がある
import pandas as pd
import re

data = pd.Series(["2023-07-15 13:22:00", "2023-07-16 08:45:00", "2023-07-17 10:10:10"])
data.name = "日時"

# 年月日を抽出
def extract_date(text):
    return re.findall(r"(\d{4})-(\d{1,2})-(\d{1,2})", text)

extracted_data = data.apply(extract_date)
print(extracted_data)
0    [('2023', '07', '15')]
1    [('2023', '07', '16')]
2    [('2023', '07', '17')]
Name: 日時, dtype: object

lambda 関数

  • 利点:
  • 欠点:
import pandas as pd

data = pd.Series(["2023-07-15 13:22:00", "2023-07-16 08:45:00", "2023-07-17 10:10:10"])
data.name = "日時"

# 年月日を抽出
extracted_data = data.str.extract(lambda x: re.findall(r"(\d{4})-(\d{1,2})-(\d{1,2})", x))
print(extracted_data)
0    [('2023', '07', '15')]
1    [('2023', '07', '16')]
2    [('2023', '07', '17')]
Name: 日時, dtype: object

カスタム関数

  • 利点:
    • 複雑な抽出ロジックを柔軟に実装可能
    • コードの再利用性が高い
  • 欠点:
    • コード量が増える
    • テストが必要
import pandas as pd

def extract_date(text):
    match = re.search(r"(\d{4})-(\d{1,2})-(\d{1,2})", text)
    if match:
        return match.groups()
    else:
        return None

data = pd.Series(["2023-