Разделить файл на чанк

Я пытаюсь разделить файл в формате:

@some 
@garbage
@lines
@target G0.S0
@type xy
 -0.108847E+02  0.489034E-04
 -0.108711E+02  0.491023E-04
 -0.108574E+02  0.493062E-04
 -0.108438E+02  0.495075E-04
 -0.108302E+02  0.497094E-04
 ....Unknown line numbers...
&
@target G0.S1
@type xy
 -0.108847E+02  0.315559E-04
 -0.108711E+02  0.316844E-04
 -0.108574E+02  0.318134E-04
 ....Unknown line numbers...
&
@target G1.S0
@type xy
 -0.108847E+02  0.350450E-04
 -0.108711E+02  0.351669E-04
 -0.108574E+02  0.352908E-04
&
@target G1.S1
@type xy
 -0.108847E+02  0.216396E-04
 -0.108711E+02  0.217122E-04
 -0.108574E+02  0.217843E-04
 -0.108438E+02  0.218622E-04

на @target Gx.Sy сочетание является уникальным, и каждый набор данных всегда termineted по &.

мне удалось разделить файл на куски как:

#!/usr/bin/env python3
import os
import sys
import itertools as it
import numpy as np
import matplotlib.pyplot as plt

try:
  filename = sys.argv[1]
  print(filename)
except IndexError:
  print("ERROR: Required filename not provided")

with open(filename, "r") as f:
  for line in f:
    if line.startswith("@target"):
      print(line.split()[-1].split("."))

x=[];y=[]
with open(filename, "r") as f:
  for key,group in it.groupby(f,lambda line: line.startswith('@target')):
    print(key)
    if not key:
        group = list(group)
        group.pop(0)
        # group.pop(-1)
        print(group)
        for i in range(len(group)):
          x.append(group[i].split()[0])
          y.append(group[i].split()[1])
        nx=np.array(x)
        ny=np.array(y)

у меня две проблемы:

1) строки преамбулы перед реальными данными также сгруппированы, поэтому скрипт не работает, если есть какая-либо преамбула. Невозможно предсказать, сколько строк это будет; но я пытаюсь сгруппировать после на @target и

2) я хочу назвать массивы как G0[S0,S0] и G1[S1,S2]; но я не могу этого сделать.

Пожалуйста, Помогите

обновление: Я пытаюсь сохранить эти данные во вложенном массиве np G0[S0, S1,...] Г1[С0,С1,..] чтобы я мог использовать его в matplotlib.

3 ответов


функции ниже получить работу:

import numpy as np
from collections import defaultdict

def read_without_preamble(filename):
    with open(filename, 'r') as f:
        lines = f.readlines()
    for i, line in enumerate(lines):
        if line.startswith('@target'):
            return lines[i:]

def split_into_chunks(lines):
    chunks = defaultdict(dict)
    for line in lines:
        if line.startswith('@target'):
            GS_str = line.strip().split()[-1].split('.')
            G, S = map(lambda x: int(x[1:]), GS_str)
            chunks[G][S] = []
        elif line.startswith('@type xy'):
            pass
        elif line.startswith('&'):
            chunks[G][S] = np.asarray(chunks[G][S])
        else:
            xy_str = line.strip().split()
            chunks[G][S].append(map(float, xy_str))
    return chunks

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

try:
  filename = sys.argv[1]
  print(filename)
except IndexError:
  print("ERROR: Required filename not provided")

data = read_without_preamble(filename)
chunks = split_into_chunks(data)

ступенчато демо

chunks - это словарь, в котором ключ G (либо 0 или 1):

In [415]: type(chunks)
Out[415]: dict

In [416]: for k in chunks.keys(): print(k)
0
1

значение словарь chunks еще один словарь, в котором ключ S (0, 1 или 2 в этом примере) и значение представляет собой массив NumPy, содержащий числовые данные для Gi.Sn. Вы можете получить доступ к этому куску данных следующим образом:chunks[i][n], где индексы i и n ценности G и S, соответственно.

In [417]: type(chunks[0])
Out[417]: dict

In [418]: for k in chunks[0].keys(): print(k)
0
1
2

In [419]: type(chunks[1][2])
Out[419]: numpy.ndarray

In [420]: chunks[1][2]
Out[420]: 
array([[ -1.08851000e+01,   2.53058000e-05],
       [ -1.08715000e+01,   2.55353000e-05],
       [ -1.08579000e+01,   2.57745000e-05],
       [ -1.08443000e+01,   2.60225000e-05],
       [ -1.08306000e+01,   2.62617000e-05],
       [ -1.08170000e+01,   2.65097000e-05],
       [ -1.08034000e+01,   2.67666000e-05]])

chunks[i][n].shape[0] и 2 любой i и n, а chunks[i][n].shape[1] может принимать любое значение, т. е. количество строк, числовых данных может варьироваться от одного блока к другому.

formatted_file.txt

этот файл я использовал в Примере. Он состоит из шести куски, а именно G0.S0, G0.S1, G0.S2, G1.S0, G1.S1 и G1.S2.

@some 
@garbage
@lines
@target G0.S0
@type xy
 -0.108851E+02  0.127435E-03
 -0.108715E+02  0.127829E-03
 -0.108579E+02  0.128191E-03
 -0.108443E+02  0.128502E-03
 -0.108306E+02  0.128726E-03
 -0.108170E+02  0.128838E-03
 -0.108034E+02  0.128751E-03
&
@target G0.S1
@type xy
 -0.108851E+02  0.472694E-04
 -0.108715E+02  0.474233E-04
 -0.108579E+02  0.475837E-04
 -0.108443E+02  0.477448E-04
 -0.108306E+02  0.479052E-04
 -0.108170E+02  0.480669E-04
 -0.108034E+02  0.482279E-04
&
@target G0.S2
@type xy
 -0.108851E+02  0.253654E-04
 -0.108715E+02  0.255956E-04
 -0.108579E+02  0.258346E-04
 -0.108443E+02  0.260825E-04
 -0.108306E+02  0.263303E-04
 -0.108170E+02  0.265781E-04
 -0.108034E+02  0.268349E-04
&
@target G1.S0
@type xy
 -0.108851E+02  0.108786E-03
 -0.108715E+02  0.109216E-03
 -0.108579E+02  0.109651E-03
 -0.108443E+02  0.110116E-03
 -0.108306E+02  0.110552E-03
 -0.108170E+02  0.111011E-03
 -0.108034E+02  0.111489E-03
&
@target G1.S1
@type xy
 -0.108851E+02  0.278045E-04
 -0.108715E+02  0.278711E-04
 -0.108579E+02  0.279384E-04
 -0.108443E+02  0.280050E-04
 -0.108306E+02  0.280723E-04
 -0.108170E+02  0.281395E-04
 -0.108034E+02  0.282074E-04
&
@target G1.S2
@type xy
 -0.108851E+02  0.253058E-04
 -0.108715E+02  0.255353E-04
 -0.108579E+02  0.257745E-04
 -0.108443E+02  0.260225E-04
 -0.108306E+02  0.262617E-04
 -0.108170E+02  0.265097E-04
 -0.108034E+02  0.267666E-04
&

вот подход с использованием генератора и np.genfromtxt. Преимущество: свет на памяти. Он фильтрует файл на ходу, следовательно,не требуют загрузки всего в память для обработки.

обновление:

я оптимизировал код и изменил формат вывода на массив массивов. Если, например,G колеблется между 0...3 и S колеблется между 0...5 затем он создает массив 4x6, содержащий матрицы.

import numpy as np
from itertools import dropwhile, groupby
from operator import itemgetter

def load_chunks(f):
    f = open(f, 'rt') if isinstance(f, str) else f
    f = filter(lambda l: not l.strip() in ("", "&"), f)
    tok = "@target", "@type"
    fg = dropwhile(itemgetter(0), groupby(f, lambda l: not l.split()[0] in tok))
    I, D = [], []
    for k, g in fg:
        info = next(l.split() for l in g)[1]
        I.append([int(key[1:]) for key in info.split('.')])
        D.append(np.genfromtxt((l.encode() for l in next(fg)[1])))
    G, S = np.array(I).T
    res = np.empty((np.max(G)+1, np.max(S)+1), dtype=object)
    res[G, S] = D
    return res

fn = <your_file_name>

ara = load_chunks(fn)

редактировать - Я взял отзывы о моем подходе к списку и решил переключить его на дикт. Это решение имеет то преимущество, что оно легко потребляет память и полностью динамично (т. е. не зависит от знания количества G-блоков априори.


я использовал re пакет, который похож на how numpy ручки ввода/вывода через loadtxt(). Кроме того, так как есть действительно нет смысла создание вложенного массива numpy numpy массивы, я просто возвращаю вложенный встроенный list массивов numpy. Поскольку ваши данные неоднородны, этот подход столь же эффективен (и намного проще):

import numpy as np
import re
from collections import defaultdict

COMMENT_REGEX = re.compile(str('@'))
TERMINATION_REGEX = re.compile(str('&'))
TARGET_REGEX = re.compile(str('@target G(\d+).S(\d+)'))


def load(filename):
    X = []
    g = None
    chunk_arr = []
    chunkd = defaultdict(dict)

    with open(filename) as fh:
        for line in fh:
            # comments match
            if COMMENT_REGEX.match(line):
                target_match = TARGET_REGEX.match(line)
                # look for target info
                if target_match:
                    # start keeping track of g for the new group
                    g, s = [int(x) for x in target_match.groups()]
                    # reset x
                    X = []
            # chunk termination string match
            elif TERMINATION_REGEX.match(line):
                if g is not None:
                    # create a np.array out of the previous chunk's data
                    X = np.array(X)
                    chunkd[g][s] = X
            # data found
            else:
                # append data as a 2-element tuple onto a 1D list
                X.append(tuple([float(x) for x in line.split()]))

    return chunkd

доступ осуществляется просто путем передачи правильной координаты G, S возвращаемому chunk_arr.

arr = load('chunks.txt')
print(arr[1][1])
[[ -1.08847000e+01   4.89034000e-05]
[ -1.08711000e+01   4.91023000e-05]
[ -1.08574000e+01   4.93062000e-05]
[ -1.08438000e+01   4.95075000e-05]
[ -1.08302000e+01   4.97094000e-05]]