Python3 – 正規表現 – 選択演算子’|’とfindall/finditerの注意点

findall()finditer()のパターン文字列で選択演算子'|'を用いるとき、選択文字列の順序によって結果が変わってくる点に注意が必要。

たとえばreモジュール関数の場合、以下の例のようになる。

'aa|aaa'とした場合以下の順番でマッチしていく。

  • "a,|aa|,|aa|a,aaaa,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa|,aaaaa"
  • "a,|aa|,|aa|a,|aa|aa|,|aa|aaa"
  • "a,|aa|,|aa|a,|aa|aa|,|aa|aa|a"

'aaa|aa'とした場合は以下の順番でマッチしていく

  • "a,|aa|,aaa,aaaa,aaaaa"
  • "a,|aa|,|aaa|,aaaa,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,aaaaa"
  • "a,|aa|,|aaa|,|aaa|a,|aaa|aa"
  • "a,|aa|,|aaa|,|aaa|a,|aaa|aa|"

つまり、各マッチングの段階で選択演算子'|'の左側からマッチする部分をまず探し、該当しなければ演算子の右へと判定パターンを変えていく。

このため、演算子の左のパターンが右のパターンより短いと、先にそちらがマッチングされるので右の長いパターンがマッチしなくなることがある。

この動作は、正規表現オブジェクトのメソッドについても同じ。

 

 

Python3 – 正規表現 – エスケープとraw文字列

エスケープの問題

正規表現ではいくつかの文字がパターンとしての特別の意味を持つが、その文字そのものをマッチングの対象としたいときにはバックスラッシュ(\)でエスケープする。

さらに検索対象の文字列にバックスラッシュが含まれている場合には、バックスラッシュ自身をエスケープしなければならない(\\)。このようなケースは、ファイルパスの区切りにバックスラッシュが使われている場合やLATexの\begin~\endなど多くある。

下の例は”\bigin”という文字列を検索する場合。

  • 検索される側の文字列のバックスラッシュをエスケープしなければならない(3行目)
    • その文字列をprintしてみると意図した内容になっている(4行目と9行目)
  • 次にパターン文字の方で、”\begin”とそのまま試してみるとヒットしない(5行目と10行目)
    • これは最初の”\b”がPythonのエスケープシーケンス(バックスペース)として解釈されたため
  • そこでバックスラッシュをエスケープしてもヒットしない(6行目と11行目)
    • Pythonでは文字としてのバックスラッシュとして解釈されるが、今度は正規表現として解釈したとき単独の特殊文字としての’\’となるため
  • 2つのバックスラッシュそれぞれをエスケープするようにしてやっとヒットさせることができる(7行目と11行目)

raw文字列

Pythonでは、バックスラッシュを単なる文字として解釈するためのraw文字列が組み込まれていて、文字列リテラルの前に’r’か’R’を付けるだけでよい。”””で囲まれた複数行文字列でも同じ。

また、Python3で文字列変数の文字列をraw文字列に変換するには、repr()関数を使う。ただしその結果はシングルクォートで囲まれているため、それを取り除かなくてはいけない。

また複数行文字列の場合は、リテラルでのr指定と変数へのrepr()適用で結果が異なってくる。

 

Python3 – 正規表現 – MatchObject

概要

search()などのreモジュール関数正規表現オブジェクトメソッドが実行の結果マッチした場合に、MatchObjectオブジェクトが返される。以下、MatchObjectのオブジェクトをmatchオブジェクトと表現する。matchオブジェクトは常にブール値Trueを持ち、matchオブジェクトそのものを判定式に使ってマッチしたかどうかの判定ができる。

matchオブジェクトの文字列表現には、(最初に)ヒットした場所の範囲と、マッチした内容が含まれている。

正規表現・文字列情報

re

match.re

matchを生成した正規表現オブジェクト。

string

match.string

matchを生成したsearch()match()へ渡された文字列。

グループ指定しない場合のマッチング結果

group()、start()、end()、span()

match.group()match.start()match.end()match.span()

group()はマッチした部分文字列、start()とend()はマッチした部分文字列の開始位置と終了位置、span()は(開始位置, 終了位置)のタプル。

グループ指定した場合のマッチング結果

group()

match.group()match.group([group1, ...])

引数がない場合は、マッチしたもの全てが返される。引数がある場合はグループを指定(数字のほかグループ名でも可)。

groups()

match.groups()

マッチしたグループを要素とするタプルとする。

lastindex

match.lastindex

複数グループの最終インデックス。

 

 

Python3 – 正規表現 – モジュール定数

ここでは正規表現の操作に使われるモジュール定数を整理する。

re.A/re. ASCII
\b\B\d\D\s\S\w\Wにおいて、ASCII文字のみでマッチングを行う。Unicodeパターンでのみ意味があり、バイト列パターンでは無視される。
re.I/re.IGNORECASE
英大文字・小文字を区別せずにマッチングを行う。{A-Z]のような表現で小文字ともマッチする。現在のロケールに影響を受けず、Unicode文字に対しても動作する。
re.L/re.LOCALE
\b\B\s\S\w\Wにおいて、現在のロケールに従ったマッチングを行う。バイト列でのみ意味を持つ。非推奨。
re.M/re.MULTILINE
デフォルトでは'^'は文字列全体の先頭に、'$'は文字列全体の末尾にのみマッチするが、このフラグにより、'^'は文字列の先頭と各行の先頭(各改行の直後)、'$'は文字列の末尾と各行の末尾(各改行のの直前)とマッチする。
re.S/re.DOTALL
デフォルトでは'.'は改行にマッチしないが、このフラグにより'.'を改行を含む任意の文字とマッチさせる。
re.X/re.VERBOSE
このフラグにより、パターン文字列内に改行・インデントを入れたりコメントを入れるなど、より読みやすい正規表現を書くことができる。コメントには'#'を使う。

Python3 – 正規表現 – 正規表現オブジェクトメソッド

概要

reモジュールではre.RegexObjectクラスが定義されている。パターン文字列をコンパイルするとRegexObjectのオブジェクトが生成され、そのパターンはそのプロパティとして保持される。このクラスの各メソッドで、パターンを任意のテキストに適用する。

reモジュール関数を使う場合は実行のたびにパターン文字列とフラグを指定し、その都度コンパイルされる。一方、正規表現オブジェクトのメソッドを使う場合は、一度パターンをコンパイルしておけば、その後の操作でコンパイルのオーバーヘッドが生じない。フラグはコンパイル時に指定する。

パターンの検索

search()

regex.search(string,[ pos[, endpos]])

re.search()関数と同じ機能で、stringの任意の位置で、最初にregexにマッチした時にMatchObjectのオブジェクトを返す。マッチしなければNoneを返す。

posは検索開始位置で先頭が0。デフォルト値は0で先頭から検索する。

endposは検索範囲の終了位置で、endpos-1文字目までが検索範囲となる。デフォルトでは文字列の最後まで検索する。

match()

regex.match(string[, pos[, endpos]])

re.match()関数と同じ機能で、regexstringの先頭でマッチするときだけMatchObjectオブジェクトを返す。posendposregex.search()と同じ意味。

fullmatch()

regex.fullmatch(string[, pos[, endpos]])

re.fullmatch()関数と同じ機能で、regexstring全体にマッチするときだけMatchObjectオブジェクトを返す。posendposregex.search()と同じ意味。

findall()

regex.findall(string[, pos[, endpos]])

re.findall()関数と同じ機能でregexにマッチする部分列のリストを返す。posendposregex.search()と同じ意味。

finditer()

regex.finditer(string[, pos[, endpos]])

re.finditer()関数と同じ機能で、regexにマッチする部分列のイテレータを返す。posendpossearch()と同じ意味。

分割

split()

regex.split(string, maxsplit=0)

re.split()関数と同じ機能で、regexにマッチする部分列でstringを切り分ける。posendpossearch()と同じ意味。

置換

sub()

regex.sub(repl, string, count=0)

re.sub()関数と同じ機能でstring中のregexにマッチする部分をreplで置き換える。replには文字列を返す関数も指定可能。posendpossearch()と同じ意味。

subn()

regex.subn(repl, string, count=0)

re.subn()関数と同じ機能で、置換後にタプルで(置換後の文字列, 置換数)を返す。posendpossearch()と同じ意味。

 

Python3 – 正規表現 – モジュール関数

概要

reモジュールの関数は、パターンと文字列を直接指定してマッチングなどの操作を行う。

引数の中のflagsについては、reモジュールで定義された定数を指定する。複数のflagsを指定する場合は、ビットごとのOR('|'演算子)を使って組み合わせる。

パターンは実行に先立ってコンパイルされるので、同じパターンを複数回用いる場合には、re.compile()関数でパターンをコンパイルし、コンパイルされたRegexObjectオブジェクトのメソッドを用いる方がよい。

正規表現のコンパイル

compile()

re.compile(pattern, flags=0)

正規表現パターンを正規表現オブジェクトにコンパイルする。正規表現オブジェクトのメソッド群で、以下のモジュール関数と同等の操作を行うことができる。

以下のモジュール関数を使う場合はflagsを関数ごとに指定するが、正規表現オブジェクトを使う場合は、compile()関数の引数でflagsを指定する。

パターンの検索

re.search(pattern, string, flags=0)

stringの任意の位置で、最初にpatternにマッチした時にMatchObjectのオブジェクトを返す。マッチしなければNoneを返す。

match()

re.match(pattern, string, flags=0)

stringの先頭でpatternにマッチすればMatchObjectのオブジェクトを返す。マッチしなければNoneを返す。stringの途中ではマッチしない。

fullmatch()

re.fullmatch(pattern, string, flags=0)

patternstring全体にマッチしたときだけMatcObjectのオブジェクトを返し、それ以外はNoneを返す。

findall()

re.findall(pattern, string, flags=0)

string中でpatternにマッチする全ての部分文字列を要素とするリストを返す。マッチする部分がなければ空のリスト([])を返す。先頭からマッチした部分を取り除きながらサーチしていく。

finditer()

re.finditer(pattern, string, flags=0)

string中でpatternにマッチした結果のMatchObjectオブジェクトのイテレータを返す。マッチする部分がなければ空のイテレータを返す。先頭からマッチした部分を取り除きながらサーチしていく。

分割

split()

re.split(pattern, string, maxsplit=0, flags=0)

stringをすべてのpatternにマッチする部分で分割し、それらを要素とするリストを返す。patternにマッチする部分は除かれる。マッチする部分がなければ、stringを1つの要素とするリストが返される。

maxsplitに1以上の数nを指定すると、先頭から最大n個の分割が発生し、残りはリストの最後の要素となる。

patternが先頭の部分に一致する場合は、リストの最初は空文字列から始まり、最後の部分に一致する場合はリストの最終要素が空文字列になる。

空文字列('')では分割されない。patternとして空文字1文字を指定するとエラー。

置換

sub()

re.sub(pattern, repl, string, count=0, flags=0)

string中でpatternにマッチする部分文字列をreplの文字列で置換する。countで正の整数値を指定すると、先頭から最大その回数だけ置換を行う。

空文字列とのマッチは、前のマッチの直後以外に置換される。

replには文字列を返す関数を指定できる。

subn()

re.subn(pattern, repl, string, count=0, fkags=0)

sub()と同じ操作を行うが、タプルで(置換後の文字列, 置換数)を返す。

 

Python3 – エスケープシーケンス

Pythonのエスケープシーケンス一覧

\\ バックスラッシュ('\')
\' シングルクォーテーション("'")
\" ダブルクォーテーション('"')
\a ベル
\b バックスペース
\f 改ページ
\r キャリッジリターン
\n 改行
\t 水平タブ
\v 垂直タブ
¥N{name} Unicodeデータベース中で名前nameを持つ文字
\uxxxx 16ビットの16進数xxxxを持つUnicode文字
\Uxxxxxxxx 32ビットの16進数xxxxxxxxを持つUnicode文字
\ooo 8進数oooを持つASCII文字
\xhh 16進数hhを持つASCII文字
\0 NULL
\+改行 改行の直前(文末)が\の場合は次の行が継続

 

Python3 – 正規表現

Pythonでの正規表現の扱い

Pythonでは、パターン・マッチングを以下のいずれかの方法で行い、その結果をMatchObjectオブジェクトとして返す。

  • パターンと文字列を指定して、reモジュールで定義された関数を実行する
  • パターン文字列を正規表現オブジェクトとしてコンパイルしておき、そのメソッドで文字列を指定して実行する

reモジュール関数を使う方法

モジュール関数で実行する例は次の通り。実行結果はMatchObjectのオブジェクトとして返されるが、その文字列表現のspanのところに4文字目~8-1文字目でヒットしたことが記録されている。

パターン文字列を正規表現オブジェクトにコンパイルする方法

正規表現オブジェクトにコンパイルして検索する方法は次の通り。検索結果は先と同じMatchObjectオブジェクト。

Python3 – 文字列とコレクション

リストと文字列

list()関数で文字列をリストに分解し、join()メソッドで区切り文字を''(空文字)で指定してリストを文字列に結合。

数値要素のリストを文字列要素のリストにするには、map()関数でstr()関数を適用する。

ただしmap()関数はいろいろと注意が必要

setと文字列

set()で文字列の重複した文字を集約したセットが得られる。文字列の順番は固定されず、実行ごとにも異なる。

 

 

ヒストグラムと確率密度曲線を重ねて描くとき

概要

ある確率分布に従っていると思われるデータのヒストグラムと、その確率分布の確率密度曲線を重ねて描きたい場合がある。

RやPythonのmatplotlibではヒストグラムを描く機能が提供されていて、縦軸に度数をとったり、頻度(確率)をとったり選択できる。

縦軸に頻度を選んだ場合は、そのまま確率密度の曲線を重ねて描けばよいが、度数の場合に確率密度を重ねる手順でちょっと戸惑ったので、その手順を記録。

一様分布の例

例として、0 ≤ x <1の値が一様な確率で現れる確率分布を考える。その確率密度曲線は以下のようになる。

histogram_and_probability_density_1

(1)    \begin{equation*} \int_0^\infty u(x) dx = \int_0^\infty 1 dx = \left[ x \right]_0^\infty = 1 \end{equation*}

この一様乱数を1000個発生させて、その度数をヒストグラムに表した場合を考える。

仮に各値が実際に全く一様に表れたとすると、グラフは以下のようになり、階級幅によって高さの値が変わってくる。

histogram_and_probability_density_2

ヒストグラムの定義を、「ある階級に含まれる度数が階級幅と高さの積」と定義すれば、上の縦軸の値は1000となり、階級幅を変えても高さは変わらないが、縦軸の値を度数とすることで、階級幅によって縦軸の値が変化することになってしまう。

一般論

次に、一般の確率密度関数を考える。

histogram_and_probability_density_3

確率密度関数をf(x)として、区間[x, x + dx)の間の確率P(x)は次式のようになる。

(2)    \begin{equation*} P(x) = f(x) dx \end{equation*}

両辺に全度数Nをかけると、その値は値xの階級におけるヒストグラムの高さに相当する。

(3)    \begin{equation*} N P(x) = N f(x) dx \end{equation*}

階級iの高さをhi、階級幅をwiとすると、次式は以下のように書ける。

(4)    \begin{equation*} h_i = N w_i f(x) \end{equation*}

したがって、縦軸が度数表示のヒストグラムに確率密度曲線を重ねて描く場合、階級幅wが一定なら、確率密度関数に総度数Nと階級幅wを乗じて描けばよい。