Использование python для извлечения нескольких строк из файла данных

1

У меня есть большой файл, в котором есть огромное количество данных. Мне нужно извлечь 3 строки каждые 5000 строк. Формат файла данных выглядит следующим образом:

...

O_sh          9215    1.000000   -2.304400   
 -1.0680E+00  1.3617E+00 -5.7138E+00  
O_sh          9216    1.000000   -2.304400  
 -8.1186E-01 -1.7454E+00 -5.8169E+00  
timestep    501      9216         0         3    0.000500  
   20.54      -11.85       35.64      
  0.6224E-02   23.71       35.64      
  -20.54      -11.86       35.64      
Li               1    6.941000    0.843200
  3.7609E-02  1.1179E-01  4.1032E+00
Li               2    6.941000    0.843200
  6.6451E-02 -1.3648E-01  1.0918E+01

...

Мне нужны три строки после строки, начинающейся с "timestep", поэтому в этом случае мне нужен массив 3x3:

   20.54      -11.85       35.64      
  0.6224E-02   23.71       35.64      
  -20.54      -11.86       35.64  

в выходном файле для каждого раза, когда появляется слово "timestep".

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

Большое спасибо, и это не для домашних заданий. Вы совет будете помогать развитию науки! =)

Спасибо,

  • 0
    Это домашнее задание? Является ли регулярное выражение требованием или только первым подходом, который вы пробовали?
Теги:

5 ответов

2
Лучший ответ

Я бы предложил использовать сопрограмму (которая в основном представляет собой генератор, который может принимать значения, если вы незнакомы), чтобы поддерживать средний показатель как вы перебираете ваш файл.

def running_avg():
    count, sum = 0, 0
    value = yield None
    while True:
        if value:
            sum += value
            count += 1
        value = yield(sum/count)

# array for keeping running average
array = [[running_avg() for y in range(3)] for x in range(3)]

# advance to first yield before we begin
[[elem.next() for elem in row] for row in array]

with open('data.txt') as f:
    idx = None
    for line in f:
        if idx is not None and idx < 3:
            for i, elem in enumerate(line.strip().split()):
                array[idx][i].send(float(elem))
            idx += 1
        if line.startswith('timestep'):
            idx = 0

Чтобы получить преобразование array в список средних значений, просто вызовите каждый метод coroutine next, он вернет текущее среднее значение:

averages = [[elem.next() for elem in row] for row in array]

И вы получите что-то вроде:

averages = [[20.54, -11.85, 35.64], [0.006224, 23.71, 35.64], [-20.54, -11.86, 35.64]]
  • 0
    Я не совсем уверен, что здесь произошло, но вы конвертируете массивы 3x3 в списки 9x1, а затем берете среднее значение по всему списку, что не совсем то, что мне нужно. Вместо этого мне нужно сложить каждый из этих списков, а затем разделить каждый элемент этого списка на количество выборок.
  • 0
    О, я тогда не понял, я искал то, что вы сказали в комментарии к другому ответу. Я обновлю свой ответ, чтобы отразить это.
Показать ещё 11 комментариев
3

Предполагая, что это не домашнее задание, я думаю, что регулярное выражение является излишним для проблемы. Если вы знаете, что вам нужно три строки после того, как вы начинаете с "timestep", почему бы не подойти к проблеме таким образом:

Matrices = []

with open('data.txt') as fh:
  for line in fh:
    # If we see timestep put the next three lines in our Matrices list.
    if line.startswith('timestep'):
      Matrices.append([next(fh) for _ in range(3)])

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

  • 1
    Идея while True/readline/break , но использование while True/readline/break - неправильный путь. Просто используйте for line in fh:
  • 0
    Не будете использовать for line in fh: есть проблемы, когда вы пытаетесь прочитать 3 строки в цикле? Или цикл for не заботится о том, что позиция дескриптора файла продвинулась с момента его предыдущей итерации?
Показать ещё 1 комментарий
1

Итак, вы можете сделать это:

Алгоритм:

Read the file line by line
if the line starts with "timestep":
    read the next three lines
    take the average as needed

код:

def getArrays(f):
    answer = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    count = 0
    line = f.readline()
    while line:
        if line.strip().startswith("timestep"):
            one, two, three = getFloats(f.readline().strip()), getFloats(f.readline().strip()), getFloats(f.readline().strip())
            answer[0][0] = ((answer[0][0]*count) + one[0])/(count+1)
            answer[0][1] = ((answer[0][0]*count) + one[1])/(count+1)
            answer[0][2] = ((answer[0][0]*count) + one[2])/(count+1)

            answer[1][0] = ((answer[0][0]*count) + two[0])/(count+1)
            answer[1][1] = ((answer[0][0]*count) + two[1])/(count+1)
            answer[1][2] = ((answer[0][0]*count) + two[2])/(count+1)

            answer[2][0] = ((answer[0][0]*count) + three[0])/(count+1)
            answer[2][1] = ((answer[0][0]*count) + three[1])/(count+1)
            answer[2][2] = ((answer[0][0]*count) + three[2])/(count+1)
        line = f.readline()
        count += 1
    return answer

def getFloats(line):
    answer = []
    for num in line.split():
        if "E" in num:
            parts = num.split("E")
            base = float(parts[0])
            exp = int(parts[1])
            answer.append(base**exp)
        else:
            answer.append(float(num))
    return answer

answer теперь является списком всех массивов 3x3. Я не знаю, как вы хотите сделать усреднение, поэтому, если вы опубликуете это, я могу включить его в этот алгоритм. Кроме того, вы можете написать функцию, чтобы взять мой массив и вычислить средние значения.

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

  • 0
    Мне просто нужно прямое усреднение по элементам, желательно распечатать в отдельный файл.
  • 0
    Используя этот код, кто-нибудь знает, как получить средний массив 3х3 из этой коллекции всех массивов?
Показать ещё 9 комментариев
0
import re
from itertools import imap

text = '''O_sh          9215    1.000000   -2.304400
 -1.0680E+00  1.3617E+00 -5.7138E+00
O_sh          9216    1.000000   -2.304400
 -8.1186E-01 -1.7454E+00 -5.8169E+00
timestep    501      9216         0         3    0.000500
   20.54      -11.85       35.64
  0.6224E-02   23.71       35.64
  -20.54      -11.86       35.64
Li               1    6.941000    0.843200
  3.7609E-02  1.1179E-01  4.1032E+00
Li               2    6.941000    0.843200
  6.6451E-02 -1.3648E-01  1.0918E+01
O_sh          9215    1.000000   -2.304400
 -1.0680E+00  1.3617E+00 -5.7138E+00
O_sh          9216    1.000000   -2.304400
 -8.1186E-01 -1.7454E+00 -5.8169E+00
timestep    501      9216         0         3    0.000500
   80.80      -14580       42.28
  7.5224E-01   777.1       42.28
  140.54      -33.86       42.28
Li               1    6.941000    0.843200
  3.7609E-02  1.1179E-01  4.1032E+00
Li               2    6.941000    0.843200
  6.6451E-02 -1.3648E-01  1.0918E+01'''

lin = '\r?\n{0}*({1}+){0}+({1}+){0}+({1}+){0}*'
pat = ('^timestep.+'+3*lin).format('[ \t]','[.\deE+-]')
regx = re.compile(pat,re.MULTILINE)

def moy(x):
    return sum(map(float,x))/len(x)

li = map(moy,zip(*regx.findall(text)))
n = len(li)
g = iter(li).next
res = [(g(),g(),g()) for i in xrange(n//3)]
print res

результат

[(50.67, -7295.925, 38.96), (0.379232, 400.40500000000003, 38.96), (60.0, -22.86, 38.96)]
0

Основываясь на сообщениях inspectorG4dget и g.d.d.c, здесь приведена версия, которая должна выполнять чтение, разбор и усреднение. Пожалуйста, укажите мои ошибки!:)

    def averageArrays(filename):
        # initialize average variables then,
        # open the file and iterate through the lines until ...
        answer, count = [[0.0]*3 for _ in range(3)], 0
        with open(filename) as fh:
            for line in fh:
                if line.startswith('timestep'):  # ... we find 'timestep'!
                    # so , we read the three lines and sanitize them
                    # conversion to float happens here, which may be slow
                    raw_mat = [fh.next().strip().split() for _ in range(3)]
                    mat = []
                    for row in raw_mat:
                        mat.append([float(item) for item in row])
                    # now, update the running average, noting overflows as by
                    # http://invisibleblocks.wordpress.com/2008/07/30/long-running-averages-without-the-sum-of-preceding-values/
                    # there are surely more pythonic ways to do this
                    count += 1
                    for r in range(3):
                        for c in range(3):
                            answer[r][c] += (mat[r][c] - answer[r][c]) / count
        return answer
  • 0
    Я получаю эту ошибку, когда ваш код запускается: Файл "cell_vector_extract1.py", строка 23, в файле <module> averageArrays (history) Файл "cell_vector_extract1.py", строка 5, в AverageArrays с открытым (filename) как fh: TypeError: приведение к Unicode: нужна строка или буфер, файл найден
  • 0
    filename должно быть строкой имени файла. Таким образом, 'history.dat' может быть этим.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню