Извлечь данные x и y из текста - PullRequest
1 голос
/ 18 июня 2020

Я очень новичок в python и знаю немного о структурах данных и извлечении вещей, и мне было интересно, может ли кто-нибудь указать мне в правильном направлении. У меня есть текстовый файл с такой структурой (с 17000 или около того точками данных). Это не XML (хотя и выглядит похоже), данные выглядят именно так:

< begin >
    < datapt > 1 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000097 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >

< begin >
    < datapt > 2 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000245 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >

Я хотел бы извлечь все значения x и значения y в этом тексте (используя python), и создайте файл .csv, который имеет два отдельных столбца для значений x и значений y.

Как мне начать создавать этот конкретный код? Может мне быть проще с RegEx? Более того, следует ли мне попытаться как-то преобразовать это в формат, подобный XML, чтобы было намного проще просто вставить его в excel?

Любые указатели приветствуются !!

Ответы [ 4 ]

0 голосов
/ 19 июня 2020

Вот довольно общее c решение для регулярных выражений.

Оно основано на разбиении файла на блоки (разделенные < begin > и < end >):

Демо

А затем синтаксический анализ этих ключей, пар значений в блоках в сопоставление:

Демо

Вот полное решение:

import re   

blk_reg=re.compile(r'^<[ \t]*begin[ \t]*>[^<]*([\s\S]*?)^<[ \t]*end[ \t]*>', flags=re.M)
data_reg=re.compile(r'<[ \t]*([^ \t>]*)[ \t]*>[ \t]*(\S+)[ \t]*<[ \t]*(?:\1)[ \t]*>')

with open (your_file) as f:
    for blk in blk_reg.finditer(f.read()):
        data={m.group(1):m.group(2) for m in data_reg.finditer(blk.group(1))}
        print(data)

Печать:

{'datapt': '1', 'xunit': 'mass', 'xval': '150.000097', 'yunit': 'abs', 'yval': '0.000000'}
{'datapt': '2', 'xunit': 'mass', 'xval': '150.000245', 'yunit': 'abs', 'yval': '0.000000'}

Чтобы создать файл CSV, просто используйте отдельные элементы словаря, полученные из каждого блока тегов.

0 голосов
/ 18 июня 2020

Вы можете прочитать каждую строку файла выборочных данных, используя file_object. Когда мы видим xval или yval, мы анализируем строку, чтобы получить значение.

Извлечение значений:

Сначала разделите строку (s.split('>') ), что дает

["< xval ", " 150.000097 < xval >"

Сделайте второе разбиение на втором значении (s.split('>')[1].split('<')) выше, чтобы получить:

["[ 150.000097 ", " xval >"]

Наконец, возьмите первый val сверху и удалите начальные / конечные пробелы (s.split('>')[1].split('<')[0].strip()), чтобы получить:

150.000097

Наконец, мы записываем значения, которые мы извлекли, в файл csv.

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

Код:

sampledata.txt

< begin >
    < datapt > 1 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000097 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >

< begin >
    < datapt > 2 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000245 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >
file_object = open("./sampledata.txt", 'r')

def getValFromLine(s):
    return s.split('>')[1].split('<')[0].strip()

x_list = []
y_list = []
for line in file_object:
    if 'xval' in line:
        x_list.append(getValFromLine(line))

    if 'yval' in line:
        y_list.append(getValFromLine(line))

print(x_list)
print(y_list)

# writing to csv file
csv = open('myfile.csv', 'w')
for i in range(min(len(x_list), len(x_list))):
    csv.write(f'{x_list[i]},{y_list[i]}\n')
csv.close()

Результат:

['150.000097', '150.000245']
['0.000000', '0.000000']

myfile.csv

150.000097,0.000000
150.000245,0.000000
0 голосов
/ 18 июня 2020

Самый простой способ разобрать это - просто взять все значения x и y, а затем соединить их вместе.

Конечно, может быть проблема, когда есть значение x, но нет соответствующее значение y. В этом случае вам нужно будет знать, когда вы «начинаете» и «заканчиваете» новую группу значений.

#!/usr/bin/env python

import re

SAMPLE_TEXT = '''
< begin >
    < datapt > 1 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000097 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >

< begin >
    < datapt > 2 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000245 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >
'''

num_tag_pattern = lambda key: re.compile('<\s*' + key + '\s*>\s*(\d[.\d]+)\s*<\s*' + key + '\s*>')

x_pat, y_pat = num_tag_pattern('xval'), num_tag_pattern('yval')

def parse_points(text):
    x_vals, y_vals = [], []

    for line in text.strip().split('\n'):
        parse_float(line.strip(), x_pat, x_vals)
        parse_float(line.strip(), y_pat, y_vals)

    return list(zip(x_vals, y_vals))

def parse_float(line, pattern, values):
    m = pattern.match(line.strip())
    if m is not None:
        values.append(float(m[1]))

if __name__ == '__main__':
    print(parse_points(SAMPLE_TEXT))  # [(150.000097, 0.0), (150.000245, 0.0)]

Обновление

Вот более общий c, более безопасная версия, которая фактически анализирует исходные данные в JSON перед преобразованием их в точки.

Я даже добавил исключения, если есть какие-либо отсутствующие теги.

main.py

#!/usr/bin/env python

import re

TAG_BEGIN = re.compile(r'<\s*begin\s>', re.IGNORECASE)
TAG_END   = re.compile(r'<\s*end\s>', re.IGNORECASE)
TAG_DATA  = re.compile(r'<\s*(\w+)\s*>\s*(.+)\s*<\s*\1\s*>')

def parse_to_json(fname, strict=True):
    records, curr_record = [], None
    with open(fname) as fp: 
        for line in fp:
            m = TAG_BEGIN.match(line.strip())
            if m is not None:
                if curr_record is not None:
                    raise Exception('previous object was not closed, missing <END> tag')
                curr_record = {}
                continue
            m = TAG_END.match(line.strip())
            if m is not None:
                if curr_record is None:
                    raise Exception('missing <BEGIN> tag')
                records.append(curr_record)
                curr_record = None
                continue
            m = TAG_DATA.match(line.strip())
            if m is not None:
                if curr_record is None:
                    raise Exception('previous object closed, missing <BEGIN> tag')
                curr_record[m[1]] = m[2]
    if strict:
        if curr_record is not None:
            raise Exception('reached EOF, missing <END> tag')
    else:
        if curr_record is not None:
            records.append(curr_record) # Just add what we got...
    return records

def map_to_point(record, x_field, y_field):
    return (
        float(record[x_field]) if x_field in record else 0.0,
        float(record[y_field]) if y_field in record else 0.0
    )

def parse_points(fname, x_field='x', y_field='y', strict=True):
    return list(map(lambda record: map_to_point(record, x_field, y_field), parse_to_json(fname, strict)))

if __name__ == '__main__':
    print(parse_points('data.txt', x_field='xval', y_field='yval', strict=True))

data.txt

< begin >
    < datapt > 1 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000097 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >

< begin >
    < datapt > 2 < datapt >
    < xunit > mass < xunit >
    < xval > 150.000245 < xval >
    < yunit > abs < yunit >
    < yval > 0.000000 < yval >
< end >
0 голосов
/ 18 июня 2020

Вот что-то не очень эффективное, так как он считывает все в память и выполняет итерацию по строкам два раза, но, надеюсь, это легко понять - и 17000 точек таким образом должно быть выполнено на любой современной машине:

f = open('myfile.txt','r') #Open the file
lines = f.readlines() #Read the lines into an array in memory
x_vals = [line.replace('< xval >','').strip() for line in lines if line.find('xval') != -1]
y_vals = [line.replace('< yval >','').strip() for line in lines if line.find('yval') != -1]
csv = open('myfile.csv','w') #Open the output CSV file for writing
csv.writelines(["%s,%s" % x for x in zip(x_vals,y_vals)]) #Unpack the tuples into a CSV line (May need to tweak the end of the string if doing it in different OSs)
csv.close()

Строки 3 и 4 в основном перебирают все строки, ищущие < xval > и < yval >, используя find(), который возвращает -1, если строка не найдена, затем заменяет эту строку пустой строкой и получает избавиться от оставшихся пробелов с помощью strip()

. Строка 6 создает список кортежей из списков, полученных в строках 3 и 4, и объединяет их с помощью функции zip(). Мы распаковываем кортеж с помощью оператора % в строку формата "%s,%s" - нам может потребоваться использовать другое окончание строки (ie, добавив \r, если программа запущена на Linux, но с использованием файла Windows, например) и, возможно, другую строку формата, например строку с плавающей запятой %f.

Надеюсь, это поможет!

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