Использование регулярных выражений для выборочного извлечения данных в пандах - PullRequest
0 голосов
/ 21 мая 2019

Я использую regex и pandas для чтения строк текста в файле и выборочного извлечения данных в кадр данных.

Скажем, у меня есть следующая строка текста

Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"

IЯ хочу вставить всю эту информацию в информационный фрейм, чтобы она выглядела следующим образом:

Name    Occupation    Age
Bob      Builder       42

Я хочу игнорировать чтение любой информации о втором человеке, потому что его занятие пустое.

Код:

with open(txt, 'r') as txt
    for line in txt:
        line = line.strip
        a = re.findall(r'Name : \"(\S+)\"', line)
        if a:
            b = re.findall(r'Occupation : \"(\S+)\"', line)
            if b:
                c = re.findall(r'Age : \"(\S+)\"', line)
                if c:
                    df = df.append({'Name' : a, 'Occupation' : b, 'Age' : c}, ignore_index = True)

Это вернет следующий (неправильный) фрейм данных

    Name        Occupation      Age
["Bob", "Jim"]  ["Builder"]  ["42","25"]

Я хочу изменить этот код, чтобы он никогда не включал ситуацию, которая "Джим "находится в. То есть, если у человека нет" занятия ", то не читайте его информацию в кадре данных.Вы также можете видеть, что этот код неверен, потому что теперь он говорит, что у «Джима» есть Занятие «Строителя».

Если бы мне дали следующую строку текста:

Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"

Полученный df будет:

    Name              Occupation             Age
["Bob", "Steve"]  ["Builder", "Clerk"]  ["42","110"]

Это удобно, потому что я больше не буду сталкиваться с какими-либо проблемами индексации, поэтому я могу затем расширить этот df до своей конечной цели (знаю, как это сделать):

Name  Occupation  Age
Bob   Builder     42
Steve Clerk       110

Ответы [ 3 ]

2 голосов
/ 21 мая 2019

На основании вашего комментария, что 3 клавиши Name, Occupation и Age всегда находятся в одном и том же порядке, поэтому мы можем использовать один шаблон регулярного выражения для извлечения значений поля и в то же время убедиться, что совпадающие значениянепусто.Ниже приведен пример использования Series.str.extractall () :

# example texts copied from your post
str="""
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"
Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"
"""

# read all lines into one field dataframe with column name as 'text'
df = pd.read_csv(pd.io.common.StringIO(str), squeeze=True, header=None).to_frame('text')

# 3 fields which have the same regex sub-pattern
fields = ['Name', 'Occupation', 'Age']

# regex pattern used to retrieve values of the above fields. There are 3 sub-patterns
# corresponding to the above 3 fields and joined by at least one white spaces(\s+)
ptn = r'\s+'.join([ r'{0}\s*:\s*"(?P<{0}>[^"]+)"'.format(f) for f in fields ])
print(ptn)
#Name\s*:\s*"(?P<Name>[^"]+)"\s+Occupation\s*:\s*"(?P<Occupation>[^"]+)"\s+Age\s*:\s*"(?P<Age>[^"]+)"

Где:

  • Подшаблон Name\s*:\s*"(?P<Name>[^"]+)" в основном делает то же самое, что и Name : "([^"]+)", но с необязательно от 0 до больше пробелов, окружающих двоеточие : и именованную группу захвата.
  • знак плюс + в "([^"]+)" предназначен для того, чтобы убедиться, что значение, заключенное в двойные кавычки, не ПУСТО, поэтому пропустит профиль Джима с момента его Занятия пустой.
  • Использование именованных групп захвата, чтобы мы могли иметь правильные имена столбцов после выполнения Series.str.extractall (), в противном случае получающиеся имена столбцов по умолчанию будут 0, 1 и 2.

Затем вы можете проверить результат из Series.str.extractall ():

df['text'].str.extractall(ptn)
          Name Occupation  Age
  match
0 0        Bob    Builder   42
  1      Steve      Clerk  110
1 0        Bob    Builder   42

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

df['text'].str.extractall(ptn).reset_index(level=1, drop=True)
###
    Name Occupation  Age
0    Bob    Builder   42
0  Steve      Clerk  110
1    Bob    Builder   42
0 голосов
/ 21 мая 2019

Вы говорите, что эти строки имеют фиксированный формат, сначала идет Name, затем следует Occupation, а затем Age.Вы можете использовать

df = pd.DataFrame()
pat = r'Name\s*:\s*"([^"]+)"\s*Occupation\s*:\s*"([^"]+)"\s*Age\s*:\s*"(\d+)"'
s='Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25" Name : "Steve" Occupation : "Clerk" Age : "110"'
for name, occupation, age in re.findall(pat, s):
    df = df.append({'Name' : name, 'Occupation' : occupation, 'Age' : age}, ignore_index = True)

Выход:

>>> df
   Age   Name Occupation
0   42    Bob    Builder
1  110  Steve      Clerk

Регулярное выражение:

Name\s*:\s*"([^"]+)"\s*Occupation\s*:\s*"([^"]+)"\s*Age\s*:\s*"(\d+)"

См. regex demo .Поскольку квантификатор в группах захвата установлен на + (одно или несколько вхождений), значения никогда не будут пустыми.Чтобы избежать пустых значений в первых двух, вы можете изменить шаблон как Name\s*:\s*"([^"]*[^\s"][^"]*)"\s*Occupation\s*:\s*"([^"]*[^\s"][^"]*)"\s*Age\s*:\s*"(\d+)", см. это демо .

Подробнее

  • Name - Name
  • \s*:\s* - :, заключенный в 0+ пробелов
  • " - двойная кавычка
  • ([^"]+) - группа1: один или несколько символов, отличных от "
  • " - двойная кавычка
  • \s* - 0+ пробелов
  • Occupation\s*:\s*"
  • ([^"]+) - Группа 2: один или несколько символов, отличных от "
  • "\s*Age\s*:\s*" - ", 0+ пробелов, Age, :, заключенных в 0+ пробелов и затем"
  • (\d+) - Группа 3: одна или несколько цифр
  • " - двойная кавычка
0 голосов
/ 21 мая 2019

Использование регулярных выражений -> re.finditer с группировкой регулярных выражений.

Пример:

import re
import pandas as pd

s = 'Name : "Bob" Occupation : "Builder" Age : "42" Name : "Jim" Occupation : "" Age : "25"'

name = re.findall(r'Name : \"(.*)\" ', s)
occupation = re.findall(r'Occupation : \"(.*)\" ', s)
age = re.findall(r'Age : \"(.*)\" ', s)

regexPattern = re.compile(r'Name : \"(?P<name>.*?)\"\s+Occupation : \"(?P<occupation>.*?)\"\s+Age : \"(?P<age>.*?)\"')

df = pd.DataFrame([i.groupdict() for i in regexPattern.finditer(s) if len(filter(None, i.groupdict().values())) == 3])
print(df)

Выход:

  age name occupation
0  42  Bob    Builder
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...