Source code for pyfstat.utils.io

import logging
import os

import numpy as np

from .converting import get_dictionary_from_lines

logger = logging.getLogger(__name__)


[docs]def read_par( filename=None, label=None, outdir=None, suffix="par", comments=["%", "#"], raise_error=False, ): """Read in a .par or .loudest file, returns a dictionary of the key=val pairs. Notes ----- This can also be used to read in `.loudest` files produced by the `ComputeFstatistic_v2` executable, or any file which has rows of `key=val` data (in which the val can be understood using `eval(val)`). Parameters ---------- filename : str Filename (path) containing rows of `key=val` data to read in. label, outdir, suffix : str, optional If filename is `None`, form the file to read as `outdir/label.suffix`. comments : str or list of strings, optional Characters denoting that a row is a comment. raise_error : bool, optional If True, raise an error for lines which are not comments, but cannot be read. Returns ------- d: dict The `key=val` pairs as a dictionary. """ if filename is None: filename = os.path.join(outdir, "{}.{}".format(label, suffix)) if os.path.isfile(filename) is False: raise ValueError("No file {} found".format(filename)) d = {} with open(filename, "r") as f: d = get_dictionary_from_lines(f, comments, raise_error) return d
[docs]def read_txt_file_with_header(f, names=True, comments="#"): """Wrapper to np.genfromtxt with smarter handling of variable-length commented headers. The header is identified as an uninterrupted block of lines from the beginning of the file, each starting with the given `comments` character. After identifying a header of length `Nhead`, this function then tells `np.genfromtxt()` to skip `Nhead-1` lines (to allow for reading field names from the last commented line before the actual data starts). Parameters ------- f: str Name of the file to read. names: bool Passed on to `np.genfromtxt()`: If True, the field names are read from the last header line. comments: str The character used to indicate the start of a comment. Also passed on to `np.genfromtxt()`. Returns ------- data: np.ndarray The data array read from the file after skipping the header. """ with open(f, "r") as f_opened: Nhead = 0 for line in f_opened: if not line.startswith(comments): break Nhead += 1 data = np.atleast_1d( np.genfromtxt(f, skip_header=Nhead - 1, names=names, comments=comments) ) return data
[docs]def read_parameters_dict_lines_from_file_header( outfile, comments="#", strip_spaces=True ): """Load a list of pretty-printed parameters dictionary lines from a commented file header. Returns a list of lines from a commented file header that match the pretty-printed parameters dictionary format as generated by `BaseSearchClass.get_output_file_header()`. The opening/closing bracket lines (`{`,`}`) are not included. Newline characters at the end of each line are stripped. Parameters ---------- outfile: str Name of a PyFstat-produced output file. comments: str Comment character used to start header lines. strip_spaces: bool Whether to strip leading/trailing spaces. Returns ------- dict_lines: list A list of unparsed pprinted dictionary entries. """ dict_lines = [] with open(outfile, "r") as f_opened: in_dict = False for line in f_opened: if not line.startswith(comments): raise IOError( "Encountered end of {:s}-commented header before finding closing '}}' of parameters dictionary in file '{:s}'.".format( comments, outfile ) ) elif line.startswith(comments + " {"): in_dict = True elif line.startswith(comments + " }"): break elif in_dict: line = line.lstrip(comments).rstrip("\n") if strip_spaces: line = line.strip(" ") dict_lines.append(line) if len(dict_lines) == 0: raise IOError( "Could not parse non-empty parameters dictionary from file '{:s}'.".format( outfile ) ) return dict_lines
[docs]def get_parameters_dict_from_file_header(outfile, comments="#", eval_values=False): """Load a parameters dict from a commented file header. Returns a parameters dictionary, as generated by `BaseSearchClass.get_output_file_header()`, from an output file header. Always returns a proper python dictionary, but the values will be unparsed strings if not requested otherwise. Parameters ---------- outfile: str Name of a PyFstat-produced output file. comments: str Comment character used to start header lines. eval_values: bool If False, return dictionary values as unparsed strings. If True, evaluate each of them. DANGER! Only do this if you trust the source of the file! Returns ------- params_dict: dictionary A dictionary of parameters (with values either as unparsed strings, or evaluated). """ if eval_values: logger.warning( "Will evaluate dictionary values read from file '{:s}'.".format(outfile) ) params_dict = {} dict_lines = read_parameters_dict_lines_from_file_header( outfile, comments="#", strip_spaces=True ) for line in dict_lines: line_split = line.rstrip(",").split(":") # check for a few possible corrupt formats, # though we can't be exhaustive here... if ( (len(line_split) != 2) or np.any([len(s) == 0 for s in line_split]) or (line_split[-1] == ",") or (line_split[0][0] != "'") or (line_split[0][-1] != "'") ): raise IOError( "Line '{:s}' is not of the expected format (# 'key': val,').".format( line ) ) key = line_split[0].strip("'") val = line_split[1].strip(" ") if eval_values: val = eval(val) # DANGER params_dict[key] = val return params_dict