Работа с текстовыми данными#

Текстовые типы данных#

Существует два способа хранения текстовых данных в pandas:

  1. object -dtype массив NumPy.

  2. StringDtype расширенный тип.

Мы рекомендуем использовать StringDtype для хранения текстовых данных.

До pandas 1.0, object dtype был единственным вариантом. Это было неудачно по многим причинам:

  1. Вы можете случайно сохранить смесь строк и не-строк в object массив dtype. Лучше иметь выделенный dtype.

  2. object dtype нарушает операции, специфичные для dtype, такие как DataFrame.select_dtypes(). Нет чёткого способа выбрать просто текст, исключая нетекстовые, но все еще столбцы типа object.

  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

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

Или astype после Series или DataFrame создается

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" как тип данных для нестроковых данных, и он будет преобразован в string dtype:

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

In [8]: s
Out[8]: 
0       a
1       2
2    
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    
dtype: Int64

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

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

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

Различия в поведении#

Это места, где поведение StringDtype объекты отличаются от object dtype

  1. Для StringDtype, методы доступа к строкам которые возвращают числовой вывод всегда будет возвращать целочисленный тип данных с поддержкой NULL, а не тип int или float в зависимости от наличия значений NA. Методы, возвращающие логический вывод будет возвращать nullable boolean dtype.

    In [15]: s = pd.Series(["a", None, "b"], dtype="string")
    
    In [16]: s
    Out[16]: 
    0       a
    1    
    2       b
    dtype: string
    
    In [17]: s.str.count("a")
    Out[17]: 
    0       1
    1    
    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 выходной тип данных — float64. Аналогично для методов, возвращающих булевы значения.

    In [22]: s.str.isdigit()
    Out[22]: 
    0    False
    1     
    2    False
    dtype: boolean
    
    In [23]: s.str.match("a")
    Out[23]: 
    0     True
    1     
    2    False
    dtype: boolean
    
  1. Некоторые строковые методы, такие как Series.str.decode() недоступны на StringArray потому что StringArray содержит только строки, а не байты.

  2. В операциях сравнения, arrays.StringArray и Series поддерживается с помощью StringArray вернет объект с BooleanDtype, а не bool тип данных object. Пропущенные значения в StringArray будет распространяться в операциях сравнения, а не всегда сравниваться неравными, как numpy.nan.

Все остальное, что следует в остальной части этого документа, применяется одинаково к string и object тип данных.

Строковые методы#

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    
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    
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    
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), может быть быстрее преобразовать исходный Series к одному из типов category а затем использовать .str. или .dt. по этому поводу. Разница в производительности возникает из-за того, что для Series типа category, строковые операции выполняются на .categories и не на каждом элементе Series.

Обратите внимание, что Series типа category со строкой .categories имеет некоторые ограничения по сравнению с Series типа строка (например, вы не можете складывать строки друг с другом: s + " " + s не будет работать, если s является Series типа category). Также, .str методы, которые работают с элементами типа list недоступны на таком 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         
3    [f, g, h]
dtype: object

Элементы в разделенных списках можно получить с помощью get или [] обозначение:

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

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

Легко расширить это для возврата DataFrame с помощью expand.

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

Когда исходный Series имеет StringDtype, выходные столбцы будут все иметь тип StringDtype также.

Также можно ограничить количество разделений:

In [43]: s2.str.split("_", expand=True, n=1)
Out[43]: 
      0     1
0     a   b_c
1     c   d_e
2    
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    
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    
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        
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    
4        
dtype: string

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

Если вы хотите буквальную замену строки (эквивалентно str.replace()), вы можете установить необязательный regex параметр для False, а не экранируя каждый символ. В этом случае оба pat и repl должны быть строками:

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

The replace метод также может принимать вызываемый объект в качестве замены. Он вызывается на каждом pat используя re.sub(). Вызываемый объект должен ожидать один позиционный аргумент (объект регулярного выражения) и возвращать строку.

# 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       
dtype: string

# Using regex groups
In [57]: pat = r"(?P\w+) (?P\w+) (?P\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    
dtype: string

The 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        
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: string

Включая flags аргумент при вызове replace с компилированным объектом регулярного выражения вызовет исключение 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

removeprefix и removesuffix имеет тот же эффект, что и str.removeprefix и str.removesuffix добавлено в Python 3.9 <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    
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 также может быть двумерным. В этом случае количество строк должно соответствовать длине вызывающего 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    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

Такое же выравнивание может быть использовано, когда others является DataFrame:

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    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#

Несколько элементов, похожих на массивы (конкретно: Series, Index, и одномерные варианты np.ndarray) могут быть объединены в контейнер, подобный списку (включая итераторы, 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

Если используется join='right' для спискообразного объекта из others который содержит разные индексы, объединение этих индексов будет использоваться в качестве основы для окончательной конкатенации:

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    
6       C
7       d
8       c
dtype: string

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

Извлечение подстрок#

Извлечь первое совпадение в каждом субъекте (extract)#

The extract метод принимает регулярное выражение с хотя бы одной группой захвата.

Извлечение регулярного выражения с более чем одной группой возвращает 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    

Элементы, которые не совпадают, возвращают строку, заполненную NaN. Таким образом, Series из неупорядоченных строк может быть "преобразована" в Series или DataFrame с аналогичным индексом из очищенных или более полезных строк, без необходимости get() для доступа к кортежам или re.match объектов. Тип данных результата всегда object, даже если совпадение не найдено и результат содержит только NaN.

Именованные группы, такие как

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

и необязательные группы, такие как

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    3

также может использоваться. Обратите внимание, что любые имена групп захвата в регулярном выражении будут использоваться для имён столбцов; в противном случае будут использоваться номера групп захвата.

Извлечение регулярного выражения с одной группой возвращает DataFrame с одним столбцом, если expand=True.

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

Возвращает Series, если expand=False.

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

Вызов на Index с регулярным выражением, содержащим ровно одну группу захвата возвращает DataFrame с одним столбцом, если expand=True.

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[a-zA-Z])", expand=True)
Out[113]: 
  letter
0      A
1      B
2      C

Возвращает Index if expand=False.

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

Вызов на Index с регулярным выражением, содержащим более одной группы захвата, возвращает DataFrame if expand=True.

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

Вызывает ValueError if expand=False.

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

File ~/work/pandas/pandas/pandas/core/strings/accessor.py:140, in forbid_nonstring_types.._forbid_nonstring_types..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 группа

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[a-z])(?P[0-9])"

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

the extractall метод возвращает каждое совпадение. Результат extractall всегда является DataFrame с MultiIndex по его строкам. Последний уровень 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 имеет ровно одно совпадение,

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. Он возвращает DataFrame который имеет такой же результат, как Series.str.extractall с индексом по умолчанию (начинается с 0).

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

Примечание

Различие между match, fullmatch, и contains это строгость: fullmatch проверяет, соответствует ли вся строка регулярному выражению; match проверяет, есть ли совпадение регулярного выражения, которое начинается с первого символа строки; и contains проверяет, есть ли совпадение регулярного выражения в любой позиции строки.

Соответствующие функции в re пакет для этих трёх режимов сопоставления: re.fullmatch, re.match, и re.search, соответственно.

Методы, такие как match, fullmatch, contains, startswith, и endswith занимать дополнительный na аргумент, чтобы пропущенные значения могли рассматриваться как True или False:

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

String Index также поддерживает get_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 на каждом элементе, возвращая DataFrame с одной строкой для каждого элемента и одним столбцом для каждой группы захвата регулярного выражения

extractall()

Вызов re.findall на каждом элементе, возвращая 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