Я был бы признателен за комментарии по разбору сложного текстового файла с разделителями табуляции с использованием Python

#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. Используйте данные из вашего вопроса. Это может быть, когда я справился с этим, чтобы удалить специальные символы табуляции.