Spec-Zone.ru › pandas 0.19

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

Серии и индексы оснащены набором методов обработки строк, которые облегчают работу с каждым элементом массива. Возможно, самое важное, эти методы автоматически исключают пропущенные/NA значения. К ним можно получить доступ через атрибут str и, как правило, они имеют названия, соответствующие эквивалентным (скалярным) встроенным методам строк:

In [1]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

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

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

In [4]: s.str.len()
Out[4]: 
0    1.0
1    1.0
2    1.0
3    4.0
4    4.0
5    NaN
6    4.0
7    3.0
8    3.0
dtype: float64
In [5]: idx = pd.Index([' jack', 'jill ', ' jesse ', 'frank'])

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

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

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

Методы строк в индексе особенно полезны для очистки или преобразования столбцов DataFrame. Например, у вас могут быть столбцы с ведущими или хвостовыми пробелами:

In [9]: df = pd.DataFrame(randn(3, 2), columns=[' Column A ', ' Column B '],
   ...:                   index=range(3))
   ...: 

In [10]: df
Out[10]: 
    Column A    Column B 
0    0.017428    0.039049
1   -2.240248    0.847859
2   -1.342107    0.368828

Поскольку df.columns является объектом Index, мы можем использовать аксессор .str

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

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

Затем эти методы строк можно использовать для очистки столбцов по мере необходимости. Здесь мы удаляем ведущие и хвостовые пробелы, приводя все имена к нижнему регистру и заменяя оставшиеся пробелы подчёркиваниями:

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

In [14]: df
Out[14]: 
   column_a  column_b
0  0.017428  0.039049
1 -2.240248  0.847859
2 -1.342107  0.368828

Примечание

Если у вас есть Series где много элементов повторяется (т.е. количество уникальных элементов в Series намного меньше длины Series), может быть быстрее преобразовать исходный Series в один из типов category и затем использовать .str.<method> или .dt.<property> на этом. Разница в производительности обусловлена тем, что для Series типа category, операции со строками выполняются над .categories а не над каждым элементом Series.

Обратите внимание, что Series типа category со строковыми .categories имеет некоторые ограничения по сравнению с Series типа строка (например, вы не можете складывать строки друг с другом: s + " " + s не будет работать, если s это Series типа category). Кроме того, методы .str, которые работают с элементами типа list, недоступны в таком Series.

Разделение и замена строк

Методы, такие как split возвращают серию списков:

In [15]: s2 = pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h'])

In [16]: s2.str.split('_')
Out[16]: 
0    [a, b, c]
1    [c, d, e]
2          NaN
3    [f, g, h]
dtype: object

Элементы в разбитых списках можно получить, используя обозначение get или [].

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

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

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

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

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

In [20]: s2.str.split('_', expand=True, n=1)
Out[20]: 
     0     1
0    a   b_c
1    c   d_e
2  NaN  None
3    f   g_h

rsplit аналогичен split, за исключением того, что он работает в обратном направлении, т. е. от конца строки к началу строки:

In [21]: s2.str.rsplit('_', expand=True, n=1)
Out[21]: 
     0     1
0  a_b     c
1  c_d     e
2  NaN  None
3  f_g     h

Методы, такие как replace и findall также принимают регулярные выражения:

In [22]: s3 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca',
   ....:                '', np.nan, 'CABA', 'dog', 'cat'])
   ....: 

In [23]: s3
Out[23]: 
0       A
1       B
2       C
3    Aaba
4    Baca
5        
6     NaN
7    CABA
8     dog
9     cat
dtype: object

In [24]: s3.str.replace('^.a|dog', 'XX-XX ', case=False)
Out[24]: 
0           A
1           B
2           C
3    XX-XX ba
4    XX-XX ca
5            
6         NaN
7    XX-XX BA
8      XX-XX 
9     XX-XX t
dtype: object

Необходимо соблюдать осторожность, чтобы учитывать регулярные выражения! Например, следующий код вызовет проблемы из-за значения регулярного выражения $:

# Consider the following badly formatted financial data
In [25]: dollars = pd.Series(['12', '-$10', '$10,000'])

# This does what you'd naively expect:
In [26]: dollars.str.replace('$', '')
Out[26]: 
0        12
1       -10
2    10,000
dtype: object

# But this doesn't:
In [27]: dollars.str.replace('-$', '-')
Out[27]: 
0         12
1       -$10
2    $10,000
dtype: object

# We need to escape the special character (for >1 len patterns)
In [28]: dollars.str.replace(r'-\$', '-')
Out[28]: 
0         12
1        -10
2    $10,000
dtype: object

Индексирование с помощью .str

Вы можете использовать обозначение [] для прямого индексирования по позициям. Если вы индексируете за пределами строки, результат будет NaN.

In [29]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan,
   ....:                'CABA', 'dog', 'cat'])
   ....: 

In [30]: s.str[0]
Out[30]: 
0      A
1      B
2      C
3      A
4      B
5    NaN
6      C
7      d
8      c
dtype: object

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

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

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

Новое в версии 0.13.0.

Предупреждение

В версии 0.18.0, extract получил аргумент expand. Когда expand=False, он возвращает Series, Index или DataFrame, в зависимости от объекта и шаблона регулярного выражения (то же поведение, что и до 0.18.0). Когда expand=True, он всегда возвращает DataFrame, что более согласуется и менее запутанно с точки зрения пользователя.

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

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

In [32]: pd.Series(['a1', 'b2', 'c3']).str.extract('([ab])(\d)', expand=False)
Out[32]: 
     0    1
0    a    1
1    b    2
2  NaN  NaN

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

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

In [33]: pd.Series(['a1', 'b2', 'c3']).str.extract('(?P<letter>[ab])(?P<digit>\d)', expand=False)
Out[33]: 
  letter digit
0      a     1
1      b     2
2    NaN   NaN

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

In [34]: pd.Series(['a1', 'b2', '3']).str.extract('([ab])?(\d)', expand=False)
Out[34]: 
     0  1
0    a  1
1    b  2
2  NaN  3

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

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

In [35]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=True)
Out[35]: 
     0
0    1
1    2
2  NaN

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

In [36]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=False)
Out[36]: 
0      1
1      2
2    NaN
dtype: object

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

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

In [38]: s
Out[38]: 
A11    a1
B22    b2
C33    c3
dtype: object

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

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

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

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

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

Он вызывает ValueError если expand=False.

>>> s.index.str.extract("(?P<letter>[a-zA-Z])([0-9]+)", expand=False)
ValueError: only one regex group is supported with Index

В таблице ниже обобщено поведение extract(expand=False) (входной объект в первом столбце, количество групп в регулярном выражении в первой строке)

1 группа >1 группа
Индекс Индекс ValueError
Серия Серия DataFrame

Извлечение всех совпадений в каждом объекте (extractall)

Новое в версии 0.18.0.

В отличие от extract (возвращает только первое совпадение),

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

In [43]: s
Out[43]: 
A    a1a2
B      b1
C      c1
dtype: object

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

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

метод extractall возвращает каждое совпадение. Результат extractall всегда является DataFrame с MultiIndex по строкам. Последний уровень MultiIndex называется match и указывает порядок в исходной строке.

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

Когда каждая строка в серии имеет ровно одно совпадение,

In [47]: s = pd.Series(['a3', 'b3', 'c2'])

In [48]: s
Out[48]: 
0    a3
1    b3
2    c2
dtype: object

тогда extractall(pat).xs(0, level='match') дает тот же результат, что и extract(pat).

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

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

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

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

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

Index также поддерживает .str.extractall. Он возвращает DataFrame, который имеет тот же результат, что и DataFrame с индексом по умолчанию (начинается с 0).

Новое в версии 0.19.0.

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

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

Проверка строк, которые соответствуют или содержат шаблон

Вы можете проверить, содержат ли элементы шаблон:

In [56]: pattern = r'[a-z][0-9]'

In [57]: pd.Series(['1', '2', '3a', '3b', '03c']).str.contains(pattern)
Out[57]: 
0    False
1    False
2    False
3    False
4    False
dtype: bool

или соответствуют шаблону:

In [58]: pd.Series(['1', '2', '3a', '3b', '03c']).str.match(pattern, as_indexer=True)
Out[58]: 
0    False
1    False
2    False
3    False
4    False
dtype: bool

Различие между match и contains заключается в строгости: match опирается на строгую re.match, а contains опирается на re.search.

Предупреждение

В предыдущих версиях match предназначался для извлечения групп, возвращая не очень удобную серию кортежей. Новый метод extract (описанный в предыдущем разделе) теперь предпочтительнее.

Это старое, устаревшее поведение match всё ещё является значением по умолчанию. Как показано выше, воспользуйтесь новым поведением, установив as_indexer=True. В этом режиме, match аналогичен contains, возвращая булеву серию. Новое поведение станет поведением по умолчанию в будущей версии.

Methods like match, contains, startswith, and endswith take
дополнительный аргумент na, чтобы пропущенные значения можно было рассматривать как True или False:
In [59]: s4 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])

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

Создание индикаторных переменных

Вы можете извлечь фиктивные переменные из столбцов строк. Например, если они разделены '|':

In [61]: s = pd.Series(['a', 'a|b', np.nan, 'a|c'])

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

Строки Index также поддерживают get_dummies , которое возвращает MultiIndex.

Новое в версии 0.18.1.

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

In [64]: idx.str.get_dummies(sep='|')
Out[64]: 
MultiIndex(levels=[[0, 1], [0, 1], [0, 1]],
           labels=[[1, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 1]],
           names=[u'a', u'b', u'c'])

См. также get_dummies().

Краткое описание методов

Метод Описание
cat() Конкатенация строк
split() Разделение строк по разделителю
rsplit() Разделение строк по разделителю, начиная с конца строки
get() Индексирование каждого элемента (получение i-го элемента)
join() Объединение строк в каждом элементе Series с заданным разделителем
get_dummies() Разделение строк по разделителю с возвратом DataFrame фиктивных переменных
contains() Возвращает булевый массив, если каждая строка содержит шаблон/регулярное выражение
replace() Замена вхождений шаблона/регулярного выражения другой строкой
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
upper() Эквивалентно str.upper
find() Эквивалентно str.find
rfind() Эквивалентно str.rfind
index() Эквивалентно str.index
rindex() Эквивалентно str.rindex
capitalize() Эквивалентно str.capitalize
swapcase() Эквивалентно str.swapcase
normalize() Возвращает нормальную форму Юникода. Эквивалентно 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

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/text.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API