質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Visual Studio Code

Visual Studio Codeとは、Microsoft社が開発したマルチプラットフォーム対応のテキストエディタです。Visual Studioファミリーの一員でもあります。拡張性とカスタマイズ性が高く、テキストエディタでありながら、IDEと遜色ない機能を備えることができます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

688閲覧

複数の駅名から最初の駅名だけを正規表現により抽出

Omihiro

総合スコア6

Visual Studio Code

Visual Studio Codeとは、Microsoft社が開発したマルチプラットフォーム対応のテキストエディタです。Visual Studioファミリーの一員でもあります。拡張性とカスタマイズ性が高く、テキストエディタでありながら、IDEと遜色ない機能を備えることができます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2024/02/19 15:16

実現したいこと

・一行に線区、駅名、徒歩、線区、駅名、徒歩のように、線区、駅名、徒歩の情報が複数個ある場合(一個しかない場合もあり)に、正規表現にて最初に駅名だけを抽出したいのですが、何度やっても一部分がうまく抽出できません。

徒歩や線区はうまく抽出できたので、それに習って行ってみてもうまく抽出できず、悩んでいます。

前提

python3にてvscodeで行っております。

正規表現を用いて最初の「徒歩○○分」までの文字列を抽出し、その後ろは抽出しない。新しいカラム「アクセス1」に格納

train['アクセス1'] = train['アクセス'].str.extract('(.?徒歩\d+分)') #正規表現を用いて最初の「徒歩○○分」までの文字列を抽出
test['アクセス1'] = test['アクセス'].str.extract('(.
?徒歩\d+分)') #正規表現を用いて最初の「徒歩○○分」までの文字列を抽出

train["アクセス"] #大元のデータ

0 都営三田線西巣鴨駅徒歩4分埼京線板橋駅徒歩14分都電荒川線西ケ原四丁目駅徒歩7分
1 都営大江戸線勝どき駅徒歩5分有楽町線月島駅徒歩9分日比谷線築地駅徒歩20分
2 京王線笹塚駅徒歩6分京王線代田橋駅徒歩7分京王線明大前駅徒歩17分
3 総武線・中央線(各停)高円寺駅徒歩9分丸ノ内線(池袋-荻窪)新高円寺駅徒歩3分丸ノ内線(池袋...
4 京成金町線京成金町駅徒歩5分常磐線金町(東京都)駅徒歩7分京成金町線柴又駅徒歩17分
...
31463 都営三田線蓮根駅徒歩7分都営三田線西台駅徒歩10分都営三田線志村三丁目駅徒歩17分
31464 東急田園都市線三軒茶屋駅徒歩6分東急世田谷線西太子堂駅徒歩4分小田急小田原線世田谷代田駅徒歩13分
31465 東西線南砂町駅徒歩5分都営新宿線大島(東京都)駅徒歩26分東西線東陽町駅徒歩18分
31466 中央線(快速)中野(東京都)駅徒歩4分丸ノ内線(池袋-荻窪)新中野駅徒歩10分丸ノ内線(池袋...
31467 有楽町線千川駅徒歩5分有楽町線要町駅徒歩13分西武池袋線東長崎駅徒歩17分

train["アクセス1"] #最初の徒歩○○分までのデータを抽出

0 都営三田線西巣鴨駅徒歩4分
1 都営大江戸線勝どき駅徒歩5分
2 京王線笹塚駅徒歩6分
3 総武線・中央線(各停)高円寺駅徒歩9分
4 京成金町線京成金町駅徒歩5分
...
31463 都営三田線蓮根駅徒歩7分
31464 東急田園都市線三軒茶屋駅徒歩6分
31465 東西線南砂町駅徒歩5分
31466 中央線(快速)中野(東京都)駅徒歩4分
31467 有楽町線千川駅徒歩5分

該当のソースコード

python3

1# 正規表現を用いて最初の「徒歩○○分」までの文字列を抽出し、その後ろは抽出しない。新しいカラム「アクセス1」に格納 2train['アクセス1'] = train['アクセス'].str.extract('(.*?徒歩\d+分)') 3test['アクセス1'] = test['アクセス'].str.extract('(.*?徒歩\d+分)') 4 5# 駅名を抽出する関数 6def extract_station(access_string): 7 if pd.isnull(access_string): 8 return None 9 # 「何か線」に続く最初の駅名を抽出する正規表現 10 match = re.search(r"線(.*?)駅", access_string) 11 if match: 12 # 駅名が見つかった場合は、その駅名を返す 13 return match.group(1) + '駅' 14 else: 15 # 駅名が見つからなかった場合はNoneを返す 16 return None 17 18# 'アクセス1'カラムから駅名を抽出して'最寄り駅'カラムに格納 19train['最寄り駅'] = train['アクセス1'].apply(extract_station) 20test['最寄り駅'] = test['アクセス1'].apply(extract_station) 21 22 23print(train["最寄り駅"].isna().sum()) ⇒1667 (この数だけ、関数が適用されていないことが分かります) 24 25 26set(train["最寄り駅"]) - set(test["最寄り駅"]) 27⇒{'三ノ輪橋駅', 28 '上野広小路駅', 29 '京成上野駅', 30 '吉祥寺駅', 31 '大塚駅', 32 '川崎駅', 33 '栄町(東京都)駅', 34 '梶原駅', 35 '町屋二丁目駅', 36 '町屋駅', 37 '西武新宿駅', 38 '(快速)神田(東京都)駅', 39 '-東海道本線)赤羽駅', 40 '-横須賀線)大崎駅'} 41 42これらを抽出したいのですが、うまくいかないです。。

試したこと

def extract_station(access_string): # 特殊な駅名に対するリストを更新   special_stations = [ '三ノ輪橋駅', '上野広小路駅', '京成上野駅', '吉祥寺駅', '大塚駅', '川崎駅', '栄町(東京都)駅', '梶原駅', '町屋二丁目駅', '町屋駅', '西武新宿駅', '神田(東京都)駅', '赤羽駅', '大崎駅'] for station in special_stations: if station in access_string: return station # 一般的な「何か線」に続く最初の駅名を抽出する正規表現 match_other = re.search(r"線[\s\S]*?([^\s\-()\(\)]*?)駅", access_string) if match_other: return match_other.group(1) + '駅' else: return None # 'アクセス1'カラムから駅名を抽出して'最寄り駅'カラムに格納 train['最寄り駅'] = train['アクセス1'].apply(extract_station) test['最寄り駅'] = test['アクセス1'].apply(extract_station) print(train["最寄り駅"].isna().sum()) #重複の確認 set(train["最寄り駅"]) - set(test["最寄り駅"]) ↓ 1667 {'三ノ輪橋駅', '上野広小路駅', '京成上野駅', '川崎駅', '栄町(東京都)駅', '梶原駅', '町屋二丁目駅', '町屋駅', '西武新宿駅'} # 'アクセス1'カラムから駅名を抽出して'最寄り駅'カラムに格納 train['最寄り駅'] = train['アクセス1'].apply(extract_station) test['最寄り駅'] = test['アクセス1'].apply(extract_station) #最寄り駅のカラムに抽出できない値が存在しているか念のため確認。 train[train["最寄り駅"] == "三ノ輪橋駅"]↓

区      アクセス1     線区  最寄り駅   徒歩時間  賃料面積比対数(見にくい為、カラムを一部割愛)
19 都電荒川線三ノ輪橋駅徒歩3分 57  三ノ輪橋駅    3     7.802321

補足情報(FW/ツールのバージョンなど)

vscode
バージョン: 1.86.2 (user setup)
コミット: 903b1e9d8990623e3d7da1df3d33db3e42d80eda
日付: 2024-02-13T19:40:56.878Z
Electron: 27.2.3
ElectronBuildId: 26908389
Chromium: 118.0.5993.159
Node.js: 18.17.1
V8: 11.8.172.18-electron.0
OS: Windows_NT x64 10.0.22621

python3 インタープリタ3.11.4

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

「正規表現を用いて最初の『徒歩○○分』までの文字列を抽出」はできていて、そこから駅名だけを抽出する、ということで回答します。


前置き。

まず気に留めたほうがよいこととして、正規表現マッチングの原則 というものがあります。

正規表現によるマッチングは、原則として、対象文字列中の 最も左 の位置から、最も長く マッチし、既にマッチした範囲とは 重なり合わない

最も左

対象文字列中に、正規表現がマッチできる位置が複数あるときは、最も左の位置でマッチを開始する。

最も長く

可変長のマッチでは、可能な限り長い範囲にマッチする。

重なり合わない

対象文字列中で一度マッチした範囲には、再度マッチすることはない。

この「最左最長重なり合わない」を守るようにすると、正規表現によるマッチングをうまく使えるようになります。

近年はこの原則を守らないいわゆる「拡張正規表現」というものが使える環境も多いです。たとえば

  • *?+? などは「最長」原則が適用されず、「ある程度長く」マッチします。
  • (?=...) などには「重なり合わない」原則が適用されず、一度マッチした範囲に後続の正規表現が再びマッチできます。

これらは一見便利に見えますが、実際にはこれらを使うとマッチングの範囲の予測がつきづらくなって正規表現が期待通りの動作をしなくなることが多いです。

使うのは通常の正規表現、つまり ?*+{}.[]|() だけにするほうがよいです。それでできないことは正規表現以外の方法を考えましょう。


前置きが長くなりましたが、今回のように駅名を抽出するにはまず、その直前 (左隣) の線区名に正確にマッチする正規表現を作る必要があります。なぜなら、いきなり駅名にマッチさせようとしても、線区名の範囲に駅名と紛らわしいものがあると「最左」原則によって誤マッチしてしまうからです。逆に、線区名に正確にマッチできれば「重なり合わない」原則によって、マッチした範囲の直後から駅名が始まることが確定します。

線区名は「◯◯線」(そのあとに括弧書きがつくことがある) という形が基本ですが「線」がつかなかったり途中に「線」が入る例外もあります。

まず、基本については「最長」原則でマッチさせられます (下記で二重になっている括弧のうち内側のものは全角括弧で、正規表現の括弧ではありません)。

[^線]+線(([^)]+))?

これに例外を加えます。|で区切って並べた正規表現は順に試行され、マッチするものが見つかったらそれ以後の正規表現は試されません。下記ではたとえば「横浜市営地下鉄ブルーライン」にマッチしたらそれ以降の「◯◯線」などにマッチすることはありません。

(横浜市営地下鉄ブルーライン|総武線快速・横須賀線|その他例外...|[^線]+線)(([^)]+))?

これで文字列先頭からマッチさせれば、マッチの終わったところから駅名が始まるはずですね。また駅名の後ろは「徒歩x分」で終わることも決まっています。文字列の末尾が「徒歩x分」にマッチしなければならないようにする (下記では$が最後についていますから、これがマッチできる「最も左」の位置は文字列末尾だけです) と、「重なり合わない」原則により、駅名はその前で終わります。

^(横浜市営地下鉄ブルーライン|総武線快速・横須賀線|その他例外...|[^線]+線)(([^)]+))?(.+)徒歩\d+分$

したがって、上記の正規表現では3つ目のグループにあたる (.+) が駅名にマッチします。

投稿2024/02/20 03:03

編集2024/02/20 09:42
ikedas

総合スコア4345

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Omihiro

2024/02/20 09:46

解決しました。丁寧な解説を有難うございます。正規表現の原則やルールをこれを機にしっかり振り返ろうと思います。 他のお二方の表記の仕方も勉強になりました。有難うございました。 また、set(train["最寄り駅) - set(test["最寄り駅])の差分で抽出できていなかった最寄り駅がいくつかあったことも今回の悩みの種だったのですが、差分で出てくる駅名=それぞれを抽出する正規表現が間違っていると思い込んでおりました。 それぞれの表記の問題で、if文でそれぞれの駅名を一つに統一させることにしました。
ikedas

2024/02/20 14:16

できれば、質問のタグに「正規表現」も加えておいてください (VSCodeは全く関係ないし)。
ikedas

2024/03/18 13:16

質問のタグに「正規表現」も加えておいてください (VSCodeは全く関係ないし)。
guest

0

正規表現にて最初に駅名だけを抽出したい

質問文に記載のあるデータのみの対応ですが、以下の様になります。

python

1train['最寄り駅'] = train['アクセス'].str.extract(r'^(?:.+?線)(.+?駅)')[0]\ 2 .str.extract(r'(?:.*?[))])?(.+?駅)') 3 4print(train)
アクセス最寄り駅
都営三田線西巣鴨駅徒歩4分埼京線板橋駅徒歩14分都電荒川線西ケ原四丁目駅徒歩7分西巣鴨駅
都営大江戸線勝どき駅徒歩5分有楽町線月島駅徒歩9分日比谷線築地駅徒歩20分勝どき駅
京王線笹塚駅徒歩6分京王線代田橋駅徒歩7分京王線明大前駅徒歩17分笹塚駅
総武線・中央線(各停)高円寺駅徒歩9分丸ノ内線(池袋-荻窪)新高円寺駅徒歩3分丸ノ内線(池袋...高円寺駅
京成金町線京成金町駅徒歩5分常磐線金町(東京都)駅徒歩7分京成金町線柴又駅徒歩17分京成金町駅
都営三田線蓮根駅徒歩7分都営三田線西台駅徒歩10分都営三田線志村三丁目駅徒歩17分蓮根駅
東急田園都市線三軒茶屋駅徒歩6分東急世田谷線西太子堂駅徒歩4分小田急小田原線世田谷代田駅徒歩13分三軒茶屋駅
東西線南砂町駅徒歩5分都営新宿線大島(東京都)駅徒歩26分東西線東陽町駅徒歩18分南砂町駅
中央線(快速)中野(東京都)駅徒歩4分丸ノ内線(池袋-荻窪)新中野駅徒歩10分丸ノ内線(池袋...中野(東京都)駅
有楽町線千川駅徒歩5分有楽町線要町駅徒歩13分西武池袋線東長崎駅徒歩17分千川駅

投稿2024/02/19 17:09

編集2024/02/19 18:47
melian

総合スコア19825

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

  • (各停)など、全角括弧で囲われた文字列が路線名の最後に入ったり、そうでなかったりする
  • 駅名には '線' は含まれない

という前提なら、下記ではどうでしょうか。

python

1match = re.search(r'(?:(.+?))?([^線]+駅)', access_string)

どのようなパターンがあるか不明なので、対応しきれないパターンは例外的な処理を入れるのも良いかと思います。
ごく一部のパターンのために正規表現が複雑になると、メンテナンスが大変になるからです。

投稿2024/02/19 16:47

Eggpan

総合スコア2743

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問