テキストデータの操作#

テキストデータ型#

pandasでは、テキストデータを格納する方法は2つあります。

  1. object -dtype NumPy配列。

  2. StringDtype 拡張型。

テキストデータの格納には、StringDtype を使用することをお勧めします。

pandas 1.0より前は、object dtypeが唯一の選択肢でした。これは多くの理由で好ましくありませんでした。

  1. object dtype配列には、文字列と非文字列が誤って混在してしまう可能性があります。専用のデータ型を使用する方が良いでしょう。

  2. object dtypeは、DataFrame.select_dtypes() のようなデータ型固有の操作を妨げます。テキストのみを選択し、非テキストだがobject-dtypeの列を除外する明確な方法はありません。

  3. コードを読む際に、object dtype配列の内容は、'string' よりも分かりにくいです。

現在、文字列の object dtype配列と arrays.StringArray のパフォーマンスはほぼ同じです。今後の拡張により、StringArray のパフォーマンスが大幅に向上し、メモリオーバーヘッドが削減される予定です。

警告

StringArray は現在実験的なものとみなされています。実装とAPIの一部は予告なく変更される可能性があります。

後方互換性のために、object dtypeは、文字列のリストを推論する際のデフォルト型です。

In [1]: pd.Series(["a", "b", "c"])
Out[1]: 
0    a
1    b
2    c
dtype: object

string dtypeを明示的に要求するには、dtype を指定します。

In [2]: pd.Series(["a", "b", "c"], dtype="string")
Out[2]: 
0    a
1    b
2    c
dtype: string

In [3]: pd.Series(["a", "b", "c"], dtype=pd.StringDtype())
Out[3]: 
0    a
1    b
2    c
dtype: string

または、Series または DataFrame が作成された後に astype を使用します。

In [4]: s = pd.Series(["a", "b", "c"])

In [5]: s
Out[5]: 
0    a
1    b
2    c
dtype: object

In [6]: s.astype("string")
Out[6]: 
0    a
1    b
2    c
dtype: string

また、非文字列データに対して StringDtype/"string" をdtypeとして使用することもできます。これは string dtypeに変換されます。

In [7]: s = pd.Series(["a", 2, np.nan], dtype="string")

In [8]: s
Out[8]: 
0       a
1       2
2    <NA>
dtype: string

In [9]: type(s[1])
Out[9]: str

または、既存のpandasデータから変換します。

In [10]: s1 = pd.Series([1, 2, np.nan], dtype="Int64")

In [11]: s1
Out[11]: 
0       1
1       2
2    <NA>
dtype: Int64

In [12]: s2 = s1.astype("string")

In [13]: s2
Out[13]: 
0       1
1       2
2    <NA>
dtype: string

In [14]: type(s2[0])
Out[14]: str

動作の違い#

これらは、StringDtype オブジェクトの動作が object dtypeと異なる点です。

  1. StringDtype の場合、**数値**を出力する 文字列アクセサメソッド は、NA値の有無に応じてintまたはfloat dtypeではなく、常にNull許容整数dtypeを返します。**ブール値**を出力するメソッドは、Null許容ブールdtypeを返します。

    In [15]: s = pd.Series(["a", None, "b"], dtype="string")
    
    In [16]: s
    Out[16]: 
    0       a
    1    <NA>
    2       b
    dtype: string
    
    In [17]: s.str.count("a")
    Out[17]: 
    0       1
    1    <NA>
    2       0
    dtype: Int64
    
    In [18]: s.dropna().str.count("a")
    Out[18]: 
    0    1
    2    0
    dtype: Int64
    

    どちらの出力も Int64 dtypeです。object-dtypeと比較してください。

    In [19]: s2 = pd.Series(["a", None, "b"], dtype="object")
    
    In [20]: s2.str.count("a")
    Out[20]: 
    0    1.0
    1    NaN
    2    0.0
    dtype: float64
    
    In [21]: s2.dropna().str.count("a")
    Out[21]: 
    0    1
    2    0
    dtype: int64
    

    NA値が存在する場合、出力dtypeはfloat64です。ブール値を返すメソッドも同様です。

    In [22]: s.str.isdigit()
    Out[22]: 
    0    False
    1     <NA>
    2    False
    dtype: boolean
    
    In [23]: s.str.match("a")
    Out[23]: 
    0     True
    1     <NA>
    2    False
    dtype: boolean
    
  1. Series.str.decode() のような一部の文字列メソッドは、StringArray では使用できません。これは、StringArray はバイトではなく文字列のみを保持するためです。

  2. 比較演算では、arrays.StringArrayStringArray によってバッキングされた Series は、bool dtypeオブジェクトではなく、BooleanDtype を持つオブジェクトを返します。StringArray 内の欠損値は、numpy.nan のように常に不等号で比較されるのではなく、比較演算で伝播されます。

このドキュメントの残りの部分に記載されている内容は、stringobject dtypeの両方に等しく適用されます。

文字列メソッド#

SeriesとIndexには、配列の各要素を簡単に操作できる一連の文字列処理メソッドが備わっています。おそらく最も重要なことは、これらのメソッドは欠損値/NA値を自動的に除外することです。これらは str 属性を介してアクセスされ、一般的に対応する(スカラー)組み込み文字列メソッドと一致する名前を持っています。

In [24]: s = pd.Series(
   ....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   ....: )
   ....: 

In [25]: s.str.lower()
Out[25]: 
0       a
1       b
2       c
3    aaba
4    baca
5    <NA>
6    caba
7     dog
8     cat
dtype: string

In [26]: s.str.upper()
Out[26]: 
0       A
1       B
2       C
3    AABA
4    BACA
5    <NA>
6    CABA
7     DOG
8     CAT
dtype: string

In [27]: s.str.len()
Out[27]: 
0       1
1       1
2       1
3       4
4       4
5    <NA>
6       4
7       3
8       3
dtype: Int64
In [28]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])

In [29]: idx.str.strip()
Out[29]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')

In [30]: idx.str.lstrip()
Out[30]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')

In [31]: idx.str.rstrip()
Out[31]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')

Indexの文字列メソッドは、DataFrameの列をクリーンアップまたは変換するのに特に役立ちます。たとえば、先頭または末尾に空白がある列がある場合があります。

In [32]: df = pd.DataFrame(
   ....:     np.random.randn(3, 2), columns=[" Column A ", " Column B "], index=range(3)
   ....: )
   ....: 

In [33]: df
Out[33]: 
   Column A   Column B 
0   0.469112  -0.282863
1  -1.509059  -1.135632
2   1.212112  -0.173215

df.columns はIndexオブジェクトであるため、.str アクセサを使用できます。

In [34]: df.columns.str.strip()
Out[34]: Index(['Column A', 'Column B'], dtype='object')

In [35]: df.columns.str.lower()
Out[35]: Index([' column a ', ' column b '], dtype='object')

これらの文字列メソッドを使用して、必要に応じて列をクリーンアップできます。ここでは、先頭と末尾の空白を削除し、すべての名前に小文字を使用し、残りの空白をアンダースコアに置き換えています。

In [36]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")

In [37]: df
Out[37]: 
   column_a  column_b
0  0.469112 -0.282863
1 -1.509059 -1.135632
2  1.212112 -0.173215

注意

多くの要素が繰り返される Series がある場合(つまり、Series 内のユニークな要素の数が Series の長さよりもはるかに少ない場合)、元の Seriescategory 型の Series に変換してから、.str.<method> または .dt.<property> を使用した方が高速になる可能性があります。パフォーマンスの違いは、category 型の Series の場合、文字列操作が Series の各要素ではなく .categories に対して実行されるという事実によるものです。

文字列 .categories を持つ category 型の Series は、文字列型の Series と比較していくつかの制限があることに注意してください(たとえば、文字列を互いに追加することはできません。scategory 型の Series の場合、s + " " + s は機能しません)。また、list 型の要素を操作する .str メソッドは、そのような Series では使用できません。

警告

Seriesの型が推論され、許可される型(つまり、文字列)が決定されます。

一般的に、.str アクセサは文字列に対してのみ機能することを意図しています。ごくわずかな例外を除いて、他の用途はサポートされておらず、後で無効になる可能性があります。

文字列の分割と置換#

split のようなメソッドは、リストのSeriesを返します。

In [38]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="string")

In [39]: s2.str.split("_")
Out[39]: 
0    [a, b, c]
1    [c, d, e]
2         <NA>
3    [f, g, h]
dtype: object

分割されたリストの要素には、get または [] 表記を使用してアクセスできます。

In [40]: s2.str.split("_").str.get(1)
Out[40]: 
0       b
1       d
2    <NA>
3       g
dtype: object

In [41]: s2.str.split("_").str[1]
Out[41]: 
0       b
1       d
2    <NA>
3       g
dtype: object

expand を使用してDataFrameを返すように簡単に拡張できます。

In [42]: s2.str.split("_", expand=True)
Out[42]: 
      0     1     2
0     a     b     c
1     c     d     e
2  <NA>  <NA>  <NA>
3     f     g     h

元の SeriesStringDtype を持つ場合、出力列はすべて StringDtype になります。

分割数を制限することも可能です。

In [43]: s2.str.split("_", expand=True, n=1)
Out[43]: 
      0     1
0     a   b_c
1     c   d_e
2  <NA>  <NA>
3     f   g_h

rsplit は、文字列の末尾から先頭に向かって動作するという点を除いて、split と似ています。

In [44]: s2.str.rsplit("_", expand=True, n=1)
Out[44]: 
      0     1
0   a_b     c
1   c_d     e
2  <NA>  <NA>
3   f_g     h

replace は、オプションで 正規表現 を使用します。

In [45]: s3 = pd.Series(
   ....:     ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
   ....:     dtype="string",
   ....: )
   ....: 

In [46]: s3
Out[46]: 
0       A
1       B
2       C
3    Aaba
4    Baca
5        
6    <NA>
7    CABA
8     dog
9     cat
dtype: string

In [47]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
Out[47]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6        <NA>
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: string

バージョン 2.0 で変更されました。

regex=True を指定した単一文字のパターンも正規表現として扱われます。

In [48]: s4 = pd.Series(["a.b", ".", "b", np.nan, ""], dtype="string")

In [49]: s4
Out[49]: 
0     a.b
1       .
2       b
3    <NA>
4        
dtype: string

In [50]: s4.str.replace(".", "a", regex=True)
Out[50]: 
0     aaa
1       a
2       a
3    <NA>
4        
dtype: string

文字列のリテラル置換(str.replace()と同等)を行いたい場合は、各文字をエスケープするのではなく、オプションのregexパラメータをFalseに設定できます。この場合、patreplはどちらも文字列でなければなりません。

In [51]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="string")

# These lines are equivalent
In [52]: dollars.str.replace(r"-\$", "-", regex=True)
Out[52]: 
0         12
1        -10
2    $10,000
dtype: string

In [53]: dollars.str.replace("-$", "-", regex=False)
Out[53]: 
0         12
1        -10
2    $10,000
dtype: string

replaceメソッドは、置換として呼び出し可能オブジェクトも受け取ることができます。これは、re.sub()を使用して、すべてのpatで呼び出されます。呼び出し可能オブジェクトは、1つの位置引数(正規表現オブジェクト)を受け取り、文字列を返す必要があります。

# Reverse every lowercase alphabetic word
In [54]: pat = r"[a-z]+"

In [55]: def repl(m):
   ....:     return m.group(0)[::-1]
   ....: 

In [56]: pd.Series(["foo 123", "bar baz", np.nan], dtype="string").str.replace(
   ....:     pat, repl, regex=True
   ....: )
   ....: 
Out[56]: 
0    oof 123
1    rab zab
2       <NA>
dtype: string

# Using regex groups
In [57]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"

In [58]: def repl(m):
   ....:     return m.group("two").swapcase()
   ....: 

In [59]: pd.Series(["Foo Bar Baz", np.nan], dtype="string").str.replace(
   ....:     pat, repl, regex=True
   ....: )
   ....: 
Out[59]: 
0     bAR
1    <NA>
dtype: string

replaceメソッドは、re.compile()からコンパイルされた正規表現オブジェクトをパターンとして受け入れることもできます。すべてのフラグは、コンパイルされた正規表現オブジェクトに含まれている必要があります。

In [60]: import re

In [61]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)

In [62]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
Out[62]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6        <NA>
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: string

コンパイルされた正規表現オブジェクトでreplaceを呼び出す際にflags引数を含めると、ValueErrorが発生します。

In [63]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
---------------------------------------------------------------------------
ValueError: case and flags cannot be set when pat is a compiled regex

removeprefixremovesuffixは、Python 3.9で追加されたstr.removeprefixstr.removesuffixと同じ効果があります <https://docs.python.org/3/library/stdtypes.html#str.removeprefix>`__

バージョン1.4.0の新機能です。

In [64]: s = pd.Series(["str_foo", "str_bar", "no_prefix"])

In [65]: s.str.removeprefix("str_")
Out[65]: 
0          foo
1          bar
2    no_prefix
dtype: object

In [66]: s = pd.Series(["foo_str", "bar_str", "no_suffix"])

In [67]: s.str.removesuffix("_str")
Out[67]: 
0          foo
1          bar
2    no_suffix
dtype: object

連結#

cat()、またはIndex.str.catに基づいて、SeriesまたはIndexを自身または他のものと連結する方法はいくつかあります。

単一のSeriesを文字列に連結する#

Series(またはIndex)の内容は連結できます。

In [68]: s = pd.Series(["a", "b", "c", "d"], dtype="string")

In [69]: s.str.cat(sep=",")
Out[69]: 'a,b,c,d'

指定しない場合、区切り文字のキーワードsepはデフォルトで空の文字列sep='' になります。

In [70]: s.str.cat()
Out[70]: 'abcd'

デフォルトでは、欠損値は無視されます。na_repを使用すると、それらに表現を与えることができます。

In [71]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")

In [72]: t.str.cat(sep=",")
Out[72]: 'a,b,d'

In [73]: t.str.cat(sep=",", na_rep="-")
Out[73]: 'a,b,-,d'

SeriesとリストのようなものをSeriesに連結する#

cat()の最初の引数は、呼び出し元のSeries(またはIndex)の長さと一致する場合、リストのようなオブジェクトにすることができます。

In [74]: s.str.cat(["A", "B", "C", "D"])
Out[74]: 
0    aA
1    bB
2    cC
3    dD
dtype: string

na_repが指定されていない限り、どちらかの側の欠損値は、結果にも欠損値をもたらします。

In [75]: s.str.cat(t)
Out[75]: 
0      aa
1      bb
2    <NA>
3      dd
dtype: string

In [76]: s.str.cat(t, na_rep="-")
Out[76]: 
0    aa
1    bb
2    c-
3    dd
dtype: string

Seriesと配列のようなものをSeriesに連結する#

パラメータothersは2次元にすることもできます。この場合、行数は呼び出し元のSeries(またはIndex)の長さと一致する必要があります。

In [77]: d = pd.concat([t, s], axis=1)

In [78]: s
Out[78]: 
0    a
1    b
2    c
3    d
dtype: string

In [79]: d
Out[79]: 
      0  1
0     a  a
1     b  b
2  <NA>  c
3     d  d

In [80]: s.str.cat(d, na_rep="-")
Out[80]: 
0    aaa
1    bbb
2    c-c
3    ddd
dtype: string

Seriesとインデックス付きオブジェクトを、位置合わせを行ってSeriesに連結する#

SeriesまたはDataFrameとの連結では、joinキーワードを設定することにより、連結前にインデックスを調整できます。

In [81]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")

In [82]: s
Out[82]: 
0    a
1    b
2    c
3    d
dtype: string

In [83]: u
Out[83]: 
1    b
3    d
0    a
2    c
dtype: string

In [84]: s.str.cat(u)
Out[84]: 
0    aa
1    bb
2    cc
3    dd
dtype: string

In [85]: s.str.cat(u, join="left")
Out[85]: 
0    aa
1    bb
2    cc
3    dd
dtype: string

joinには通常のオプション('left', 'outer', 'inner', 'right'のいずれか)を使用できます。特に、位置合わせとは、異なる長さが一致する必要がなくなったことも意味します。

In [86]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")

In [87]: s
Out[87]: 
0    a
1    b
2    c
3    d
dtype: string

In [88]: v
Out[88]: 
-1    z
 0    a
 1    b
 3    d
 4    e
dtype: string

In [89]: s.str.cat(v, join="left", na_rep="-")
Out[89]: 
0    aa
1    bb
2    c-
3    dd
dtype: string

In [90]: s.str.cat(v, join="outer", na_rep="-")
Out[90]: 
-1    -z
 0    aa
 1    bb
 2    c-
 3    dd
 4    -e
dtype: string

othersDataFrameの場合も同じ位置合わせを使用できます。

In [91]: f = d.loc[[3, 2, 1, 0], :]

In [92]: s
Out[92]: 
0    a
1    b
2    c
3    d
dtype: string

In [93]: f
Out[93]: 
      0  1
3     d  d
2  <NA>  c
1     b  b
0     a  a

In [94]: s.str.cat(f, join="left", na_rep="-")
Out[94]: 
0    aaa
1    bbb
2    c-c
3    ddd
dtype: string

Seriesと多数のオブジェクトをSeriesに連結する#

複数の配列のようなアイテム(具体的には:SeriesIndex、およびnp.ndarrayの1次元バリアント)をリストのようなコンテナ(イテレータ、dictビューなど)に組み合わせることができます。

In [95]: s
Out[95]: 
0    a
1    b
2    c
3    d
dtype: string

In [96]: u
Out[96]: 
1    b
3    d
0    a
2    c
dtype: string

In [97]: s.str.cat([u, u.to_numpy()], join="left")
Out[97]: 
0    aab
1    bbd
2    cca
3    ddc
dtype: string

渡されたリストのようなものの中で、インデックスのないすべての要素(例:np.ndarray)は、呼び出し元のSeries(またはIndex)の長さと一致する必要がありますが、SeriesIndexは任意の長さを持つことができます(join=Noneで位置合わせが無効になっていない限り)。

In [98]: v
Out[98]: 
-1    z
 0    a
 1    b
 3    d
 4    e
dtype: string

In [99]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
Out[99]: 
-1    -z--
0     aaab
1     bbbd
2     c-ca
3     dddc
4     -e--
dtype: string

異なるインデックスを含むothersのリストのようなものでjoin='right'を使用する場合、これらのインデックスの和集合が最終的な連結の基礎として使用されます。

In [100]: u.loc[[3]]
Out[100]: 
3    d
dtype: string

In [101]: v.loc[[-1, 0]]
Out[101]: 
-1    z
 0    a
dtype: string

In [102]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
Out[102]: 
 3    dd-
-1    --z
 0    a-a
dtype: string

.strによるインデックス付け#

[]表記法を使用して、位置の場所によって直接インデックスを付けることができます。文字列の末尾を超えてインデックスを付けると、結果はNaNになります。

In [103]: s = pd.Series(
   .....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   .....: )
   .....: 

In [104]: s.str[0]
Out[104]: 
0       A
1       B
2       C
3       A
4       B
5    <NA>
6       C
7       d
8       c
dtype: string

In [105]: s.str[1]
Out[105]: 
0    <NA>
1    <NA>
2    <NA>
3       a
4       a
5    <NA>
6       A
7       o
8       a
dtype: string

部分文字列の抽出#

各対象の最初の一致を抽出する(extract)#

extractメソッドは、少なくとも1つのキャプチャグループを持つ正規表現を受け入れます。

複数のグループを持つ正規表現を抽出すると、グループごとに1つの列を持つDataFrameが返されます。

In [106]: pd.Series(
   .....:     ["a1", "b2", "c3"],
   .....:     dtype="string",
   .....: ).str.extract(r"([ab])(\d)", expand=False)
   .....: 
Out[106]: 
      0     1
0     a     1
1     b     2
2  <NA>  <NA>

一致しない要素は、NaNで埋められた行を返します。したがって、乱雑な文字列のSeriesは、タプルにアクセスするためのget()re.matchオブジェクトを必要とせずに、同様のインデックスを持つSeriesまたはDataFrameに「変換」できます。結果のdtypeは、一致が見つからず、結果にNaNのみが含まれている場合でも、常にオブジェクトです。

名前付きグループのように

In [107]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(
   .....:     r"(?P<letter>[ab])(?P<digit>\d)", expand=False
   .....: )
   .....: 
Out[107]: 
  letter digit
0      a     1
1      b     2
2   <NA>  <NA>

オプションのグループのように

In [108]: pd.Series(
   .....:     ["a1", "b2", "3"],
   .....:     dtype="string",
   .....: ).str.extract(r"([ab])?(\d)", expand=False)
   .....: 
Out[108]: 
      0  1
0     a  1
1     b  2
2  <NA>  3

も使用できます。正規表現のキャプチャグループ名は列名に使用されることに注意してください。そうでない場合、キャプチャグループ番号が使用されます。

1つのグループを持つ正規表現を抽出すると、expand=Trueの場合、1つの列を持つDataFrameが返されます。

In [109]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"[ab](\d)", expand=True)
Out[109]: 
      0
0     1
1     2
2  <NA>

expand=Falseの場合、Seriesが返されます。

In [110]: pd.Series(["a1", "b2", "c3"], dtype="string").str.extract(r"[ab](\d)", expand=False)
Out[110]: 
0       1
1       2
2    <NA>
dtype: string

正確に1つのキャプチャグループを持つ正規表現でIndexを呼び出すと、expand=Trueの場合、1つの列を持つDataFrameが返されます。

In [111]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"], dtype="string")

In [112]: s
Out[112]: 
A11    a1
B22    b2
C33    c3
dtype: string

In [113]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=True)
Out[113]: 
  letter
0      A
1      B
2      C

expand=Falseの場合、Indexが返されます。

In [114]: s.index.str.extract("(?P<letter>[a-zA-Z])", expand=False)
Out[114]: Index(['A', 'B', 'C'], dtype='object', name='letter')

複数のキャプチャグループを持つ正規表現でIndexを呼び出すと、expand=Trueの場合、DataFrameが返されます。

In [115]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=True)
Out[115]: 
  letter   1
0      A  11
1      B  22
2      C  33

expand=Falseの場合、ValueErrorが発生します。

In [116]: s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[116], line 1
----> 1 s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)

File ~/work/pandas/pandas/pandas/core/strings/accessor.py:137, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
    132     msg = (
    133         f"Cannot use .str.{func_name} with values of "
    134         f"inferred dtype '{self._inferred_dtype}'."
    135     )
    136     raise TypeError(msg)
--> 137 return func(self, *args, **kwargs)

File ~/work/pandas/pandas/pandas/core/strings/accessor.py:2743, in StringMethods.extract(self, pat, flags, expand)
   2740     raise ValueError("pattern contains no capture groups")
   2742 if not expand and regex.groups > 1 and isinstance(self._data, ABCIndex):
-> 2743     raise ValueError("only one regex group is supported with Index")
   2745 obj = self._data
   2746 result_dtype = _result_dtype(obj)

ValueError: only one regex group is supported with Index

以下の表は、extract(expand=False)の動作をまとめたものです(最初の列に入力対象、最初の行に正規表現のグループ数)。

1グループ

1グループ以上

Index

Index

ValueError

Series

Series

DataFrame

各対象のすべての一致を抽出する(extractall)#

extract(最初の一致のみを返す)とは異なり、

In [117]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"], dtype="string")

In [118]: s
Out[118]: 
A    a1a2
B      b1
C      c1
dtype: string

In [119]: two_groups = "(?P<letter>[a-z])(?P<digit>[0-9])"

In [120]: s.str.extract(two_groups, expand=True)
Out[120]: 
  letter digit
A      a     1
B      b     1
C      c     1

extractallメソッドはすべての一致を返します。extractallの結果は、常にMultiIndexを持つDataFrameです。MultiIndexの最後のレベルはmatchという名前で、対象の順序を示します。

In [121]: s.str.extractall(two_groups)
Out[121]: 
        letter digit
  match             
A 0          a     1
  1          a     2
B 0          b     1
C 0          c     1

Seriesの各対象文字列に一致が1つだけある場合、

In [122]: s = pd.Series(["a3", "b3", "c2"], dtype="string")

In [123]: s
Out[123]: 
0    a3
1    b3
2    c2
dtype: string

このとき、extractall(pat).xs(0, level='match')extract(pat) と同じ結果を返します。

In [124]: extract_result = s.str.extract(two_groups, expand=True)

In [125]: extract_result
Out[125]: 
  letter digit
0      a     3
1      b     3
2      c     2

In [126]: extractall_result = s.str.extractall(two_groups)

In [127]: extractall_result
Out[127]: 
        letter digit
  match             
0 0          a     3
1 0          b     3
2 0          c     2

In [128]: extractall_result.xs(0, level="match")
Out[128]: 
  letter digit
0      a     3
1      b     3
2      c     2

Index.str.extractall をサポートしています。これは、デフォルトのインデックス(0から始まる)を持つ Series.str.extractall と同じ結果を持つ DataFrame を返します。

In [129]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[129]: 
        letter digit
  match             
0 0          a     1
  1          a     2
1 0          b     1
2 0          c     1

In [130]: pd.Series(["a1a2", "b1", "c1"], dtype="string").str.extractall(two_groups)
Out[130]: 
        letter digit
  match             
0 0          a     1
  1          a     2
1 0          b     1
2 0          c     1

パターンに一致またはパターンを含む文字列のテスト#

要素がパターンを含むかどうかを確認できます

In [131]: pattern = r"[0-9][a-z]"

In [132]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.contains(pattern)
   .....: 
Out[132]: 
0    False
1    False
2     True
3     True
4     True
5     True
dtype: boolean

または、要素がパターンに一致するかどうか

In [133]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.match(pattern)
   .....: 
Out[133]: 
0    False
1    False
2     True
3     True
4    False
5     True
dtype: boolean
In [134]: pd.Series(
   .....:     ["1", "2", "3a", "3b", "03c", "4dx"],
   .....:     dtype="string",
   .....: ).str.fullmatch(pattern)
   .....: 
Out[134]: 
0    False
1    False
2     True
3     True
4    False
5    False
dtype: boolean

注意

matchfullmatchcontains の違いは厳密さです。fullmatch は文字列全体が正規表現に一致するかどうかをテストします。match は文字列の最初の文字から始まる正規表現の一致があるかどうかをテストします。contains は文字列内の任意の位置に正規表現の一致があるかどうかをテストします。

これらの3つの一致モードに対応する re パッケージの関数は、それぞれ re.fullmatchre.matchre.search です。

matchfullmatchcontainsstartswithendswith などのメソッドは、欠損値を True または False と見なせるように、追加の na 引数を取ります。

In [135]: s4 = pd.Series(
   .....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   .....: )
   .....: 

In [136]: s4.str.contains("A", na=False)
Out[136]: 
0     True
1    False
2    False
3     True
4    False
5    False
6     True
7    False
8    False
dtype: boolean

インジケーター変数の作成#

文字列の列からダミー変数を抽出できます。たとえば、'|' で区切られている場合

In [137]: s = pd.Series(["a", "a|b", np.nan, "a|c"], dtype="string")

In [138]: s.str.get_dummies(sep="|")
Out[138]: 
   a  b  c
0  1  0  0
1  1  1  0
2  0  0  0
3  1  0  1

文字列 Indexget_dummies をサポートしており、MultiIndex を返します。

In [139]: idx = pd.Index(["a", "a|b", np.nan, "a|c"])

In [140]: idx.str.get_dummies(sep="|")
Out[140]: 
MultiIndex([(1, 0, 0),
            (1, 1, 0),
            (0, 0, 0),
            (1, 0, 1)],
           names=['a', 'b', 'c'])

get_dummies() も参照してください。

メソッドの概要#

メソッド

説明

cat()

文字列を連結する

split()

区切り文字で文字列を分割する

rsplit()

文字列の末尾から区切り文字で文字列を分割する

get()

各要素にインデックスを付ける(i番目の要素を取得する)

join()

Series の各要素の文字列を渡された区切り文字で結合する

get_dummies()

区切り文字で文字列を分割し、ダミー変数の DataFrame を返す

contains()

各文字列がパターン/正規表現を含む場合、ブール値の配列を返す

replace()

パターン/正規表現/文字列の出現を他の文字列、または出現を指定した呼び出し可能オブジェクトの戻り値に置き換える

removeprefix()

文字列からプレフィックスを削除する。つまり、文字列がプレフィックスで始まる場合にのみ削除する。

removesuffix()

文字列からサフィックスを削除する。つまり、文字列がサフィックスで終わる場合にのみ削除する。

repeat()

値を複製する (s.str.repeat(3)x * 3 と同等)

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 を呼び出し、各要素に1行、各正規表現キャプチャグループに1列を持つ DataFrame を返す

extractall()

各要素に対して re.findall を呼び出し、一致ごとに1行、各正規表現キャプチャグループに1列を持つ DataFrame を返す

len()

文字列の長さを計算する

strip()

str.strip と同等

rstrip()

str.rstrip と同等

lstrip()

str.lstrip と同等

partition()

str.partition と同等

rpartition()

str.rpartition と同等

lower()

str.lower と同等

casefold()

str.casefold と同等

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 と同等