#python #parsing #text #tabs #delimited
#python #синтаксический анализ #текст #вкладки #с разделителями
Вопрос:
Интересно, может ли кто-нибудь помочь? Я новичок в Python, использую pandas и sklearn для анализа хорошо отформатированных файлов csv, но теперь я хочу извлечь данные для анализа из текстового файла с разделителями табуляции с более сложным форматированием, и я не совсем уверен, с чего начать.
Я привел простой пример файла ниже. По сути, это данные временных рядов, которые фиксируют изменяющиеся величины со временем для объектов в иерархии. Итак, в приведенном ниже примере верхним уровнем может быть «Все автомобили», под которыми будут подгруппы, такие как «мягкие вершины», а также объекты самого низкого уровня, которые будут отдельными автомобилями. Существует несколько строк заголовка, первая всегда фиксирует уровень и название отслеживаемого количества, но есть несколько других строк для переноса единиц измерения (обычно 3 или 4), а последняя строка заголовка также может содержать имена групп или отдельных объектов (например, «Мягкие вершины» или «BMW»). В реальных данных есть 10 столбцов (5 показано в примере) и около 500 дат в таблице (3 в примере). Раздел «Даты» повторяется несколько раз (всегда одни и те же даты), создавая несколько таблиц (в примере я показываю только одно повторение). Поэтому типичный файл может содержать от 200 до 500 тыс. строк.
Я хочу извлечь определенные столбцы и вывести их в файл csv, где я могу работать с ними дальше.
Пример формата, с которым я имею дело, выглядит следующим образом:
File Name
Date All_Cars_MPG All_Cars_Doors Group_MPG Car_MPG
Units Units Units Units
Units Units Units Units
Units Units Soft Tops BMW
Line of tab separated spaces
01-NOV-2015 32.5 4 18.2 25
01-DEC-2015 30.5 4 15.8 22
01-JAN-2016 35.0 5 19.0 26
Line of spaces or tab separated spaces
File name (same as above)
Date Car_Doors Car_MPG Car_Doors Car_Speed
Units Units Units Units
Units Units Units Units
BMW AUDI AUDI NISSAN
Line of tab separated spaces
01-NOV-2015 5 35 2 250
01-DEC-2015 5 12 8 220
01-JAN-2016 6 19 0 260
Я предполагаю, что мне нужно перебирать строки в файлах и извлекать столбцы, которые мне нужны для создания CSV (возможно, путем ввода имен элементов самого низкого уровня, которые в примере все начинаются с буквы «C» для Car), но не уверен, как это настроить. Найдя строку, начинающуюся с C в строке заголовка, как лучше всего добавить имя объекта (из нижней строки заголовка), а затем извлечь столбец данных под ним? И после извлечения одного столбца следующий, который мне понадобится, конечно, будет находиться в другой позиции в стеке таблиц, поэтому я не могу использовать фиксированную позицию в списке или словаре.
Я не ожидаю подробного решения, но если бы кто-нибудь мог выделить необходимый общий подход, я был бы благодарен. (Разделение? Словари?)
Меня особенно интересует самый низкий уровень иерархии, а форма csv будет содержать дату с левой стороны, а затем столько столбцов, сколько требуется справа, следующим образом :
Date,Car_MPG_BMW,Car_Doors_BMW,Car_MPG_AUDI,Car_Doors_AUDI
01-NOV-2015,25,5,35,2
01-DEC-2015,22,5,23,8
01-JAN-2016,26,6,29,0
РЕДАКТИРОВАТЬ, ЧТОБЫ ДОБАВИТЬ:
Вот попытка вставить более точную версию входного файла:
SUMMARY OF RUN
DATE WOPT WOPT WOPT WOPT WOPT WOPT WOPT WOPT WTHP
STB STB STB STB STB STB STB STB PSIA
*10**3
B1A B2 B3 B4 B5 B6 B7 B9 B1A
01-JAN-2046 403847.8 0 8613069. 18449.29 0 0 0 0 0
01-FEB-2046 403847.8 0 8633593. 18471.77 0 0 0 0 0
01-MAR-2046 403847.8 0 8652024. 18492.03 0 0 0 0 0
01-APR-2046 403847.8 0 8671890. 18514.38 0 0 0 0 0
01-MAY-2046 403847.8 0 8689601. 18535.93 0 0 0 0 0
01-JUN-2046 403847.8 0 8707051. 18558.15 0 0 0 0 0
01-JUL-2046 403847.8 0 8723709. 18579.61 0 0 0 0 0
01-AUG-2046 403847.8 0 8740806. 18601.75 0 0 0 0 0
01-SEP-2046 403847.8 0 8757767. 18623.84 0 0 0 0 0
01-OCT-2046 403847.8 0 8774027. 18645.17 0 0 0 0 0
01-NOV-2046 403847.8 0 8790653. 18667.15 0 0 0 0 0
01-DEC-2046 403847.8 0 8806563. 18688.37 0 0 0 0 0
01-JAN-2047 403847.8 0 8822815. 18710.24 0 0 0 0 0
SUMMARY OF RUN
DATE WTHP WTHP WTHP WTHP WTHP WTHP WTHP WTHP WTHP
PSIA PSIA PSIA PSIA PSIA PSIA PSIA PSIA PSIA
B2 B3 B4 B5 B6 B7 B9 B10 B16Z
01-JAN-2046 0 0 0 0 0 0 180.0000 0 0
01-FEB-2046 0 0 0 0 0 0 180.0000 0 0
01-MAR-2046 0 0 0 0 0 0 180.0000 0 0
01-APR-2046 0 0 0 0 0 0 180.0000 0 0
01-MAY-2046 0 0 0 0 0 0 180.0000 0 0
01-JUN-2046 0 0 0 0 0 0 180.0000 0 0
01-JUL-2046 0 0 0 0 0 0 180.0000 0 0
01-AUG-2046 0 0 0 0 0 0 180.0000 0 0
01-SEP-2046 0 0 0 0 0 0 180.0000 0 0
01-OCT-2046 0 0 0 0 0 0 180.0000 0 0
01-NOV-2046 0 0 0 0 0 0 180.0000 0 0
01-DEC-2046 0 0 0 0 0 0 180.0000 0 0
01-JAN-2047 0 0 0 0 0 0 180.0000 0 0
И вот пример выходного файла :
DATE,WOPT_B1A,WOPT_B2,WTHP_B1A,WTHP_B2
01-JAN-2046,403847.8,0,0,0
01-FEB-2046,403847.8,0,0,0
01-MAR-2046,403847.8,0,0,0
01-APR-2046,403847.8,0,0,0
01-MAY-2046,403847.8,0,0,0
01-JUN-2046,403847.8,0,0,0
01-JUL-2046,403847.8,0,0,0
01-AUG-2046,403847.8,0,0,0
01-SEP-2046,403847.8,0,0,0
01-OCT-2046,403847.8,0,0,0
01-NOV-2046,403847.8,0,0,0
01-DEC-2046,403847.8,0,0,0
01-JAN-2047,403847.8,0,0,0
Комментарии:
1.Посмотрите на
pandas
DataFrame
. Вам придется разбирать его построчно, и если он всегда будет следовать одному и тому же формату, вы можете просто[:]
использовать синтаксис или использовать регулярное выражение.2. При этом, пожалуйста, вставьте ваш файл БЕЗ каких-либо объяснений в нем. Покажите точный файл, который вы читаете, и желаемый результат.
3. Привет, ESR, спасибо. Каков наилучший способ опубликовать раздел исходного файла? Чтобы дать справедливое представление повторяющихся таблиц, мне нужно будет вставить около 3000 строк текста, каждая строка шириной 166 символов. Я предполагаю, что не могу загрузить вложение. Если нет, я посмотрю, смогу ли я сгенерировать сокращенную версию и попытаться скопировать в мое исходное сообщение.
4. Попробуйте найти минимальное подмножество, которое соответствует вашим потребностям при вставке. Вы всегда можете загрузить его в git hub, но сначала вам следует попытаться найти минимальный пример.
5. Спасибо ESR, еще несколько выходных данных добавлены выше. Сейчас я изучаю некоторые примеры с использованием регулярных выражений.
Ответ №1:
Итак… В этом коде есть приличное количество предположений, но это работает для примера, который вы привели. Это может работать не для всего, и в некоторых местах его можно ускорить, но я не думаю, что скорость является самой большой проблемой, и я думаю, что вы можете внести необходимые изменения в то, что не работает.
Шаг 1:
Нам нужно превратить .txt-файл в список списков.
def get_tab_delimited_lines(file):
lines = []
with open(file, 'r') as f:
for line in f.readlines():
line = line.split('t') # Split by t (TAB)
line = [x.strip() for x in line] # Remove white space
lines.append(line)
return lines
Шаг 2:
Отделите тело (таблицу) от информации заголовка (столбца).
import re # This should go at the top of the file
def get_header_and_body(lines):
# Lets seperate the header info from the body
header_info = [] # This is the list we will return for header info
body = [] # This is the list we will return for body info
temp_body = []
temp_header_info = []
header = True
for line in lines:
# If the first part of the line is a date
# in the format [a few numbers]-[a few letterss]-[a few numbers]
# Example: 01-JAN-2046
if re.match(r'[0-9] -[A-Z] -[0-9] ', line[0]): # If a date then it is the body
header = False
temp_body.append(line[:-1]) # The last element is always an empty '' so remove it
else: # Else this is header info
header = True
if temp_body: # Append the body if we have any
body.append(temp_body)
temp_body = [] # Reset the temp
if header: # If this is a header
# This is a set of the lines we dont need. If the line
# starts with any of these we will ignore it.
unwanted_starts_to_a_line = {'SUMMARY OF RUN', 'STB', '', 'PSIA'}
# We will also ignore line with things such as *18**.
if line and line[0] not in unwanted_starts_to_a_line and not re.match(r'*[0-9] **', line[0]):
temp_header_info.append(line)
else:
if temp_header_info:
header_info.append(temp_header_info)
temp_header_info = []
if temp_body:
body.append(temp_body)
if temp_header_info:
header_info.append(temp_header_info)
return header_info, body
Шаг 3:
Теперь создайте новые заголовки столбцов, которые вы хотите: я отменяю header_info
, потому что дата не добавляется ни к чему другому. Итак, я переворачиваю обе строки заголовка, объединяю их вместе, а затем возвращаю их в желаемый порядок.
def change_to_table_headers(header_info):
for index in range(len(header_info)):
# print(header_info[index]) # uncomment this to see why I did the `reversed`
# and feel free to remove the `reversed` to see what breaks.
t = list(zip(reversed(header_info[index][0]), reversed(header_info[index][1])))
t.reverse()
t = ['_'.join(x) for x in t]
header_info[index] = ['DATE'] t
Шаг 4:
Соедините все это вместе:
import pandas as pd # This should go at the top of the file
lines = get_tab_delimited_lines('test.txt')
header_info, body = get_header_and_body(lines)
change_to_table_headers(header_info)
for index in range(len(header_info)):
headers = header_info[index]
df = pd.DataFrame(body[index], columns=headers)
print(df)
Теперь, когда он находится в фрейме данных, вы можете отправить его прямо в a csv
или делать с ним все, что захотите.
Приложение:
Я использовал следующее в test.txt
документе для его тестирования.
SUMMARY OF RUN
DATE WOPT WOPT WOPT WOPT WOPT WOPT WOPT WOPT WTHP
STB STB STB STB STB STB STB STB PSIA
*10**3
B1A B2 B3 B4 B5 B6 B7 B9 B1A
01-JAN-2046 403847.8 0 8613069. 18449.29 0 0 0 0 0
01-FEB-2046 403847.8 0 8633593. 18471.77 0 0 0 0 0
01-MAR-2046 403847.8 0 8652024. 18492.03 0 0 0 0 0
01-APR-2046 403847.8 0 8671890. 18514.38 0 0 0 0 0
01-MAY-2046 403847.8 0 8689601. 18535.93 0 0 0 0 0
01-JUN-2046 403847.8 0 8707051. 18558.15 0 0 0 0 0
01-JUL-2046 403847.8 0 8723709. 18579.61 0 0 0 0 0
01-AUG-2046 403847.8 0 8740806. 18601.75 0 0 0 0 0
01-SEP-2046 403847.8 0 8757767. 18623.84 0 0 0 0 0
01-OCT-2046 403847.8 0 8774027. 18645.17 0 0 0 0 0
01-NOV-2046 403847.8 0 8790653. 18667.15 0 0 0 0 0
01-DEC-2046 403847.8 0 8806563. 18688.37 0 0 0 0 0
01-JAN-2047 403847.8 0 8822815. 18710.24 0 0 0 0 0
SUMMARY OF RUN
DATE WTHP WTHP WTHP WTHP WTHP WTHP WTHP WTHP WTHP
PSIA PSIA PSIA PSIA PSIA PSIA PSIA PSIA PSIA
B2 B3 B4 B5 B6 B7 B9 B10 B16Z
01-JAN-2046 0 0 0 0 0 0 180.0000 0 0
01-FEB-2046 0 0 0 0 0 0 180.0000 0 0
01-MAR-2046 0 0 0 0 0 0 180.0000 0 0
01-APR-2046 0 0 0 0 0 0 180.0000 0 0
01-MAY-2046 0 0 0 0 0 0 180.0000 0 0
01-JUN-2046 0 0 0 0 0 0 180.0000 0 0
01-JUL-2046 0 0 0 0 0 0 180.0000 0 0
01-AUG-2046 0 0 0 0 0 0 180.0000 0 0
01-SEP-2046 0 0 0 0 0 0 180.0000 0 0
01-OCT-2046 0 0 0 0 0 0 180.0000 0 0
01-NOV-2046 0 0 0 0 0 0 180.0000 0 0
01-DEC-2046 0 0 0 0 0 0 180.0000 0 0
01-JAN-2047 0 0 0 0 0 0 180.0000 0 0
Комментарии:
1. Фантастическая работа ESR, именно то, что мне нужно для начала. Красиво изложено и объяснено. Я очень благодарен. Я работаю построчно, чтобы понять каждый элемент, а затем я могу его использовать. Я скопировал текст из вашего приложения в новый файл для тестирования, на данный момент получаю «AssertionError: передано 2 столбца, переданные данные содержат 0 столбцов», пытаясь понять это.
2. Используйте данные из вашего вопроса. Это может быть, когда я справился с этим, чтобы удалить специальные символы табуляции.