Source code for kalasiris.Histogram

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2019-2020, Ross A. Beyer (rbeyer@seti.org)
#
# Reuse is permitted under the terms of the license.
# The AUTHORS file and the LICENSE file are at the
# top level of this library.

import collections
import csv
import subprocess
from .k_funcs import hist_k


[docs] class Histogram(collections.abc.Sequence): """Reads the output from ISIS hist and provides it as a sequence. The resulting Histogram object primarily behaves like a list, where that list represents the rows of the ISIS hist output. Each of those rows is a :func:`collections.namedtuple` which contains the elements of each row, referenced by their column names. The Histogram object also has some dictionary-like capabilities, in order to get at the values listed in the ISIS hist output in the section before the numerical output. """ def __init__(self, histinfo): self.histinfo = histinfo try: (self.dictionary, self.headers, self.hist_list) = self.parse(histinfo) except StopIteration: try: (self.dictionary, self.headers, self.hist_list) = self.parse( hist_k(histinfo) ) except subprocess.CalledProcessError: with open(histinfo, "r") as f: ( self.dictionary, self.headers, self.hist_list, ) = self.parse(f.read()) def __str__(self): return str(self.dictionary) def __repr__(self): return f"{self.__class__.__name__}('{self.histinfo}')" def __len__(self): return len(self.hist_list) def __getitem__(self, key): try: return self.dictionary[key] except KeyError: return self.hist_list[key] def __iter__(self): return self.hist_list.__iter__() def __contains__(self, item): if item in self.dictionary: return True else: return item in self.hist_list
[docs] def keys(self): """Gets the keys from the initial portion of the hist output file. These will be items like 'Cube', 'Band', 'Average', etc. """ return self.dictionary.keys()
[docs] def values(self): """Gets the values from the initial portion of the hist output file.""" return self.dictionary.values()
[docs] @staticmethod def parse(histinfo: str) -> tuple: """Takes a string (expecting the output of ISIS ``hist``), and parses the output. A three-element namedtuple is returned: the first element is a *dictionary* of the name:value information at the top of the file, the second element is a *list* of the of the fields that decorate the top of the histogram rows, and the third element is a *list* of HistRow :func:`collections.namedtuple` objects that represent each row of the hist output. The contents of the file that results from ISIS ``hist`` look like this:: Cube: PSP_010502_2090_RED5_0.EDR_Stats.cub Band: 1 Average: 6490.68 [... other lines like this ...] His Pixels: 0 Hrs Pixels: 0 DN,Pixels,CumulativePixels,Percent,CumulativePercent 3889,1,1,4.88281e-05,4.88281e-05 3924,1,2,4.88281e-05,9.76563e-05 3960,2,4,9.76563e-05,0.000195313 [... more comma-separated lines like above ...] But this function sees it like this:: k: v k: v k: v [... other lines like this ...] k: v k: v h,h,h,h,h n,n,n,n,n n,n,n,n,n n,n,n,n,n [... more comma-separated lines like above ...] Where each of the letters above is a string value that the parser reads. First, it takes all of the ``k`` and ``v`` elements and saves them as the keys and values in the returned dictionary. Second, the ``h`` elements are returned them as the list of fieldnames. Third, it reads the lines with ``n`` and stores them as ``namedtuples`` in the returned list. """ d = dict() hist_rows = [] for line in filter(lambda x: ":" in x, str(histinfo).splitlines()): (k, v) = line.split(":") # d[k.strip()] = v.strip() d.setdefault(k.strip(), v.strip()) reader = csv.reader(filter(lambda x: "," in x, str(histinfo).splitlines())) fieldnames = next(reader) HistRow = collections.namedtuple("HistRow", fieldnames) for row in map(HistRow._make, reader): hist_rows.append(row) HistParsed = collections.namedtuple( "HistParsed", ["info", "fieldnames", "data"] ) return HistParsed(d, fieldnames, hist_rows)