テキストデータ操作#

テキストのデータ型#

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

  1. object -dtype NumPy 配列。

  2. StringDtype 拡張型。

テキストデータの保存にはStringDtype の使用をお勧めします。

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

  1. object dtype 配列には、誤って文字列と非文字列の混合を保存してしまう可能性があります。専用の dtype を持つ方が良いでしょう。

  2. object dtype は、DataFrame.select_dtypes() のような dtype 固有の操作を妨げます。非テキストだがまだ 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 ではなく、常に nullable integer dtype を返します。ブール出力を返すメソッドは、nullable boolean 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.StringArray および StringArray に基づく 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 型に変換し、その上で .str.<method> または .dt.<property> を使用する方が高速になることがあります。パフォーマンスの違いは、category 型の Series の場合、文字列操作が .categories に対して行われ、Series の各要素に対して行われないという事実から生じます。

文字列 .categories を持つ category 型の Series には、文字列型の Series と比較していくつかの制限があることに注意してください (例: 文字列同士を追加することはできません。s + " " + s は、scategory 型の Series の場合、機能しません)。また、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

rsplitsplit と似ていますが、逆方向、つまり文字列の末尾から文字列の先頭に向かって機能します。

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.removeprefix および str.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

結合#

Series または Index を、それ自体または他のオブジェクトと結合する方法はいくつかあり、すべて cat()、または Index.str.cat に基づいています。

単一の 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) と長さが一致する必要がありますが、Series および Index は任意の長さを持つことができます (位置合わせが 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 を、タプルや re.match オブジェクトにアクセスするために get() を必要とすることなく、同じインデックスを持つクリーンアップされた、またはより有用な文字列の Series や DataFrame に「変換」できます。一致が見つからず、結果に NaN のみが含まれる場合でも、結果の dtype は常に object です。

名前付きグループ (例:

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:140, in forbid_nonstring_types.<locals>._forbid_nonstring_types.<locals>.wrapper(self, *args, **kwargs)
    135     msg = (
    136         f"Cannot use .str.{func_name} with values of "
    137         f"inferred dtype '{self._inferred_dtype}'."
    138     )
    139     raise TypeError(msg)
--> 140 return func(self, *args, **kwargs)

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

ValueError: only one regex group is supported with Index

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

1グループ

>1グループ

インデックス

インデックス

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()

パターン/正規表現/文字列の出現箇所を、別の文字列または callable の戻り値で置換します。

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 と同等です。