Перемещение данных из строки в pandas dataframe с помощью регулярных выражений? - PullRequest
0 голосов
/ 20 февраля 2019

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

Пример.

(Names RED (property (x 123) (y 456) (type MT) (label ONE) (code XYZ)))
(Names GREEN (property (type MX) (label TWO) (x 789) (y 101)))

В этом случае не все должны быть прочитаны из каждой строки, в этом примере только «Имя», «x», «y», «label» и «code».Предполагая, что у меня есть пара сотен строк, которые выглядят как пример, можно ли легко получить нужные данные из каждой строки?В идеале я пытаюсь передать информацию в фрейм данных pandas, но вопрос в основном заключается в том, как правильно перестроить строки, учитывая тот факт, что нет реального шаблона.

Пример того, что DataFrame можетПохоже (если это помогает понять вопрос)

Names   x   y   label   code
RED    123 456   ONE    XYZ
GREEN  789 101   TWO 

Является ли регулярное выражение даже лучшим подходом к этой проблеме?Я не нашел никакой реальной картины, когда смотрел на все линии, поэтому она не может быть идеальной.

Ответы [ 4 ]

0 голосов
/ 20 февраля 2019

В случае идеально совпадающих скобок, вы можете рассмотреть pyparsing вместо регулярных выражений?

import pandas as pd
import pyparsing as pp

lines=[
    '(Names RED (property (x 123) (y 456) (type MT) (label ONE) (code XYZ)))',
    '(Names GREEN (property (type MX) (label TWO) (x 789) (y 101)))'
]

#create an empty dataframe with possible columns
df = pd.DataFrame(columns=['Names', 'x', 'y','type','label','code'])

for line in lines:
    res = pp.nestedExpr(opener='(', closer=')').parseString(line)
    #flatten first level
    l1 = list(itertools.chain.from_iterable(res))
    #flatten property
    l2 = list(itertools.chain.from_iterable(l1[2][1:]))
    #turn to dict
    d1 = l3=dict(itertools.zip_longest(*[iter(l2)] * 2, fillvalue=""))
    #add Name value
    d1.update({'Names': l1[1]})
    #add a row to the dataframe, 
    df = df.append(d1, ignore_index=True)

df = df.fillna('')
0 голосов
/ 20 февраля 2019

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

import pandas as pd

s = df.col.str.split('(', n=2)
df['Names'] = s.str[1].str.split().str[1]

s2 = s.str[2].str.extractall('[(](.*?)[)]')[0].str.split()

df = pd.concat([df, (pd.DataFrame(s2.values.tolist(), index=s2.index.get_level_values(0))
                       .pivot(columns=0, values=1))], axis=1)

Вывод:

                                                 col  Names code label type    x    y
0  (Names RED (property (x 123) (y 456) (type MT)...    RED  XYZ   ONE   MT  123  456
1  (Names GREEN (property (type MX) (label TWO) (...  GREEN  NaN   TWO   MX  789  101
0 голосов
/ 20 февраля 2019

Очень простая и понятная реализация (просто чтобы показать вам, что вы могли бы начать здесь, прежде чем задавать вопрос, и получить немного больше правдоподобия):

string1 = "(Names RED (property (x 123) (y 456) (type MT) (label ONE) (code XYZ)))"
string2 = "(Names GREEN (property (type MX) (label TWO) (x 789) (y 101)))"

names = []
x = []
y = []
label = []
code = []
split_string = string2.split(' ')

for i in range(0, len(split_string)):
    try:
        if "Names" in split_string[i]:
            names.append(split_string[i+1])
        if "x" in split_string[i]:
            x.append(split_string[i+1][:-1])
        if "y" in split_string[i] and split_string[i].find("y") <= 1:
            y.append(split_string[i+1][:-1])
        if "label" in split_string[i]:
            label.append(split_string[i+1][:-1])
        if "code" in split_string[i]:
            code.append(split_string[i+1][:-1])
    except IndexError:
        break
print(names, '\n', x, '\n', y, '\n', label, '\n', code, '\n')

Вывод (string1):

['GREEN'] 
['789'] 
['101))'] 
['TWO'] 
[] 

Вывод (строка2):

['RED'] 
['123'] 
['456'] 
['ONE'] 
['XYZ))'] 
0 голосов
/ 20 февраля 2019

Шаблон является регулярным, за исключением того, что свойства расположены в любом порядке, так что это, безусловно, выполнимо.Я сделал это в два шага - один regex, чтобы получить цвет в начале и извлечь строку свойств, и второй, чтобы извлечь свойства.

import re


inputs = [
'(Names RED (property (x 123) (y 456) (type MT) (label ONE) (code XYZ)))',
'(Names GREEN (property (type MX) (label TWO) (x 789) (y 101)))'
]

# Get the initial part, and chop off the property innerstring
initial_re = re.compile('^\(Names\s([^\s]*)\s\(property\s(.*)\)\)')
# Get all groups from (x 123) (y 456) (type MT) (label ONE) (code XYZ)
prop_re = re.compile('\(([^\s]*)\s([^\s]*)\)')

for s in inputs:
    parts = initial_re.match(s)
    color = parts.group(1)
    props = parts.group(2)
    # e.g. (x 123) (y 456) (type MT) (label ONE) (code XYZ)
    properties = prop_re.findall(props)
    # [('x', '123'), ('y', '456'), ('type', 'MT'), ('label', 'ONE'), ('code', 'XYZ')]
    print("%s: %s" % (color, properties))

Выходные данные:

RED: [('x', '123'), ('y', '456'), ('type', 'MT'), ('label', 'ONE'), ('code', 'XYZ')]
GREEN: [('type', 'MX'), ('label', 'TWO'), ('x', '789'), ('y', '101')]

Чтобы получить это в pandas, вы можете накапливать свойства в словаре списков (я сделал это ниже, используя defaultdict).Вам нужно что-то хранить для пустых значений, чтобы все столбцы имели одинаковую длину, здесь я просто храню None (или null).Наконец, используйте pd.DataFrame.from_dict, чтобы получить окончательный результат DataFrame.

import re
import pandas as pd
from collections import defaultdict

inputs = [
'(Names RED (property (x 123) (y 456) (type MT) (label ONE) (code XYZ)))',
'(Names GREEN (property (type MX) (label TWO) (x 789) (y 101)))'
]

# Get the initial part, and chop off the property innerstring
initial_re = re.compile('^\(Names\s([^\s]*)\s\(property\s(.*)\)\)')
# Get all groups from (x 123) (y 456) (type MT) (label ONE) (code XYZ)
prop_re = re.compile('\(([^\s]*)\s([^\s]*)\)')

columns = ['color', 'x', 'y', 'type', 'label', 'code']

data_dict = defaultdict(list)

for s in inputs:
    parts = initial_re.match(s)
    color = parts.group(1)
    props = parts.group(2)
    # e.g. (x 123) (y 456) (type MT) (label ONE) (code XYZ)
    properties = dict(prop_re.findall(props))
    properties['color'] = color

    for k in columns:
        v = properties.get(k)  # None if missing
        data_dict[k].append(v)


pd.DataFrame.from_dict(data_dict)

Окончательный результат -

   color    x    y type label  code
0    RED  123  456   MT   ONE   XYZ
1  GREEN  789  101   MX   TWO  None
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...