У меня есть большой файл, в котором есть огромное количество данных. Мне нужно извлечь 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".
Тогда мне нужно среднее значение всех этих массивов только в одном массиве. Только один массив, состоящий из среднего значения каждого элемента в одной позиции в каждом массиве для всего файла. Я работал над этим некоторое время, но я еще не смог извлечь данные правильно.
Большое спасибо, и это не для домашних заданий. Вы совет будете помогать развитию науки! =)
Спасибо,
Я бы предложил использовать сопрограмму (которая в основном представляет собой генератор, который может принимать значения, если вы незнакомы), чтобы поддерживать средний показатель как вы перебираете ваш файл.
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]]
Предполагая, что это не домашнее задание, я думаю, что регулярное выражение является излишним для проблемы. Если вы знаете, что вам нужно три строки после того, как вы начинаете с "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) в этой ситуации, чтобы поддерживать ручку файла в синхронизации, когда вы хотите вывести из нее следующие три строки. Спасибо!
while True/readline/break
, но использование while True/readline/break
- неправильный путь. Просто используйте for line in fh:
for line in fh:
есть проблемы, когда вы пытаетесь прочитать 3 строки в цикле? Или цикл for не заботится о том, что позиция дескриптора файла продвинулась с момента его предыдущей итерации?
Итак, вы можете сделать это:
Алгоритм:
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. Я не знаю, как вы хотите сделать усреднение, поэтому, если вы опубликуете это, я могу включить его в этот алгоритм. Кроме того, вы можете написать функцию, чтобы взять мой массив и вычислить средние значения.
Надеюсь, что это поможет
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)]
Основываясь на сообщениях 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
filename
должно быть строкой имени файла. Таким образом, 'history.dat'
может быть этим.