Я нашел этот код в Интернете, который может анализировать текстовый файл определенного типа c, как показано ниже:
# RELION; version 3.0
data_images
loop_
_rlnCoordinateX #1
_rlnCoordinateY #2
_rlnHelicalTubeID #3
_rlnAngleTiltPrior #4
_rlnAnglePsiPrior #5
_rlnHelicalTrackLength #6
_rlnAnglePsiFlipRatio #7
_rlnImageName #8
_rlnMicrographName #9
_rlnMagnification #10
_rlnDetectorPixelSize #11
_rlnCtfMaxResolution #12
_rlnCtfFigureOfMerit #13
_rlnVoltage #14
_rlnDefocusU #15
_rlnDefocusV #16
_rlnDefocusAngle #17
_rlnSphericalAberration #18
_rlnCtfBfactor #19
_rlnCtfScalefactor #20
_rlnPhaseShift #21
_rlnAmplitudeContrast #22
_rlnOriginX #23
_rlnOriginY #24
3041.398896 3692.419723 1 90.000000 63.534898 0.000000 0.500000 000001@Extract/job011/Movies/Microtubules_02563.mrcs MotionCorr/job003/Movies/Microtubules_02563.mrc 10000.000000 5.480000 5.830000 0.124704 300.000000 7457.819824 6964.129883 33.520000 2.700000 0.000000 1.000000 0.000000 0.100000 0.031176 2.475269e-32
3068.235643 3638.511334 1 90.000000 63.534898 60.218978 0.500000 000002@Extract/job011/Movies/Microtubules_02563.mrcs MotionCorr/job003/Movies/Microtubules_02563.mrc 10000.000000 5.480000 5.830000 0.124704 300.000000 7457.819824 6964.129883 33.520000 2.700000 0.000000 1.000000 0.000000 0.100000 0.000000 0.000000
3095.072390 3584.602946 1 90.000000 63.534898 120.437956 0.500000 000003@Extract/job011/Movies/Microtubules_02563.mrcs MotionCorr/job003/Movies/Microtubules_02563.mrc 10000.000000 5.480000 5.830000 0.124704 300.000000 7457.819824 6964.129883 33.520000 2.700000 0.000000 1.000000 0.000000 0.100000 0.000000 0.000000
3121.909136 3530.694558 1 90.000000 63.534898 180.656934 0.500000 000004@Extract/job011/Movies/Microtubules_02563.mrcs MotionCorr/job003/Movies/Microtubules_02563.mrc 10000.000000 5.480000 5.830000 0.124704 300.000000 7457.819824 6964.129883 33.520000 2.700000 0.000000 1.000000 0.000000 0.100000 0.000000 0.000000
Код (два класса и несколько строк в конце для их вызова) :
import os
import sys
import argparse
from collections import OrderedDict, namedtuple
class Column:
def __init__(self, name, type=None):
self._name = name
# Get the type from the LABELS dict, assume str by default
self._type = type
def __str__(self):
return self._name
def __cmp__(self, other):
return self._name == str(other)
class Table:
"""
Class to hold and manipulate tabular data for EM processing programs.
"""
def __init__(self, **kwargs):
self.clear()
if 'fileName' in kwargs:
if 'columns' in kwargs:
raise Exception("Please provide either 'columns' or 'fileName',"
" but not both.")
fileName = kwargs.get('fileName')
tableName = kwargs.get('tableName', None)
self.read(fileName, tableName)
elif 'columns' in kwargs:
self._createColums(kwargs['columns'])
def clear(self):
self.Row = None
self._columns = OrderedDict()
self._rows = []
def clearRows(self):
""" Remove all the rows from the table, but keep its columns. """
self._rows = []
def addRow(self, *args, **kwargs):
self._rows.append(self.Row(*args, **kwargs))
def readStar(self, inputFile, tableName=None):
"""
:param inputFile: Provide the input file from where to read the data.
The file pointer will be moved until the last data line of the
requested table.
:return:
"""
self.clear()
dataStr = 'data_%s' % (tableName or '')
self._findDataLine(inputFile, dataStr)
# Find first column line and parse all columns
line, foundLoop = self._findLabelLine(inputFile)
colNames = []
values = []
while line.startswith('_'):
parts = line.split()
colNames.append(parts[0][1:])
if not foundLoop:
values.append(parts[1])
line = inputFile.readline().strip()
self._createColums(colNames)
if not foundLoop:
self.addRow(*values)
else:
# Parse all data lines
while line:
self.addRow(*line.split())
line = inputFile.readline().strip()
def read(self, fileName, tableName=None):
with open(fileName) as f:
self.readStar(f, tableName)
def writeStar(self, outputFile, tableName=None, singleRow=False):
"""
Write a Table in Star format to the given file.
:param outputFile: File handler that should be already opened and
in the position to write.
:param tableName: The name of the table to write.
:param singleRow: If True, don't write loop_, just label - value pairs.
"""
outputFile.write("\ndata_%s\n\n" % (tableName or ''))
if self.size() == 0:
return
if singleRow:
m = max([len(c) for c in self._columns.keys()]) + 5
lineFormat = "_{:<%d} {:>10}\n" % m
row = self._rows[0]
for col, value in row._asdict().iteritems():
outputFile.write(lineFormat.format(col, value))
outputFile.write('\n\n')
return
outputFile.write("loop_\n")
# Write column names
for col in self._columns.values():
outputFile.write("_%s \n" % col)
# Take a hint for the columns width from the first row
widths = [len(str(v)) for v in self._rows[0]]
# Check middle and last row, just in case ;)
for index in [len(self)//2, -1]:
for i, v in enumerate(self._rows[index]):
w = len(str(v))
if w > widths[i]:
widths[i] = w
lineFormat = " ".join("{:>%d} " % (w + 1) for w in widths)
# Write data rows
for row in self._rows:
outputFile.write(lineFormat.format(*row))
outputFile.write('\n')
outputFile.write('\n')
def write(self, output_star, tableName=None):
with open(output_star, 'w') as output_file:
self.writeStar(output_file, tableName)
def printStar(self, tableName=None):
self.writeStar(sys.stdout, tableName)
def size(self):
return len(self._rows)
def getColumns(self):
return self._columns.values()
def getColumnValues(self, colName):
"""
Return the values of a given column
:param colName: The name of an existing column to retrieve values.
:return: A list with all values of that column.
"""
if colName not in self._columns:
raise Exception("Not existing column: %s" % colName)
return [getattr(row, colName) for row in self._rows]
def __len__(self):
return self.size()
def __iter__(self):
for item in self._rows:
yield item
def __getitem__(self, item):
return self._rows[item]
def __setitem__(self, key, value):
self._rows[key] = value
# --------- Internal implementation methods ------------------------
def _addColumn(self, nameOrTuple):
"""
:param nameOrTuple: This parameter should be either a string or
a tuple (string, type).
"""
if isinstance(nameOrTuple, str):
col = Column(nameOrTuple)
elif isinstance(nameOrTuple, tuple):
col = Column(nameOrTuple[0], nameOrTuple[1])
else:
raise Exception("Invalid input as column, "
"should be either string or tuple.")
self._columns[str(col)] = col
def _createColums(self, columnList):
self.clear()
for col in columnList:
self._addColumn(col)
self._createRowClass()
def _createRowClass(self):
self.Row = namedtuple('Row', [str(c) for c in self._columns])
def _findDataLine(self, inputFile, dataStr):
""" Raise an exception if the desired data string is not found.
Move the line pointer after the desired line if found.
"""
line = inputFile.readline()
while line:
if line.startswith(dataStr):
return line
line = inputFile.readline()
raise Exception("%s block was not found")
def _findLabelLine(self, inputFile):
line = ''
foundLoop = False
l = inputFile.readline()
while l:
if l.startswith('_'):
line = l
break
elif l.startswith('loop_'):
foundLoop = True
l = inputFile.readline()
return line.strip(), foundLoop
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Script to manipulate metadata files.")
add = parser.add_argument # shortcut
add("input", help="Input metadata filename. ", nargs='?', default="")
add("output",
help="Output metadata filename, if no provided, print to stdout. ",
nargs='?', default="")
add("-l", "--limit", type=int, default=0,
help="Limit the number of rows processed, useful for testing. ")
#add("-v", "--verbosity", action="count", default=0)
args = parser.parse_args()
if '@' in args.input:
tableName, fileName = args.input.split('@')
else:
tableName, fileName = None, args.input
if not os.path.exists(fileName):
raise Exception("Input file '%s' does not exists. " % fileName)
tableIn = Table(fileName=fileName, tableName=tableName)
# Create another table with same columns
tableOut = Table(columns=[str(c) for c in tableIn.getColumns()])
limit = args.limit
for i, row in enumerate(tableIn):
if limit > 0 and i == limit:
break
tableOut.addRow(*row)
if args.output:
tableOut.write(args.output, tableName)
else:
tableOut.printStar(tableName)
Проблема и несколько вопросов: я хочу использовать этот код, чтобы изменить значение определенного столбца и переписать вышеуказанный текстовый файл (Ps, это тривиальный скрипт python, но я хочу выполнить sh эту задачу, используя приведенный выше объектно-ориентированный код).
У меня также есть несколько вопросов по поводу этого кода. Единственным аргументом, который я предоставляю этому сценарию, является текстовый файл, но я вижу эту строку здесь, если args.output: tableOut.write (args.output, tableName), который подразумевает, что возможны другие аргументы, что приведет к переписыванию файла, вместо того, чтобы просто напечатать его, как будет выполняться код, если вы просто запустите его в приведенном выше текстовом файле. Кроме того, если '@' в args.input :, какова цель этого условия, зачем мне вводить @ в аргументах этого сценария ?! (код не поставляется с документацией)
Моя попытка решения (так как я не практикую объектно-ориентированную python): я подумал, что, возможно, я смогу добавить эту функцию в класс Table и затем добавьте следующую строку в окончательный сценарий, чтобы изменить определенное значение столбца на входные значения от пользователя.
def _changeColumnsValue(self, colName, colValue):
if colName not in self._columns:
raise Exception("Not existing column: %s" % colName)
for row in self._rows:
setattr(row, colName, colValue)
change_value = input('Which column would you like to change, and to what value? Example input: _rlnVoltage 200')
tableOut._changeColumnsValue(change_value.split()[0], change_value.split()[1])