def __getter__(self, name, instantiate=True): """Loads the specified name from a file on disk. Parameters: name (key type): The canonical mapping key to get the dataObject. By default the baseFolder class uses a :py:class:`regexpDict` to store objects in. Keyword Arguments: instatiate (bool): IF True (default) then always return a :py:class:`Stoner.Core.Data` object. If False, the __getter__ method may return a key that can be used by it later to actually get the :py:class:`Stoner.Core.Data` object. Returns: (metadataObject): The metadataObject """ assertion(name is not None, "Cannot get an anonympus entry!") try: # Try the parent methods first return super(DiskBasedFolder, self).__getter__(name, instantiate=instantiate) except (AttributeError, IndexError, KeyError): pass # Find a filename and load fname = name if path.exists(name) else path.join(self.directory, name) try: tmp = self.type(self.loader(fname, **self.extra_args)) except StonerUnrecognisedFormat: return None if not isinstance(getattr(tmp, "filename", None), string_types): tmp.filename = path.basename(fname) # Process file hooks tmp = self.on_load_process(tmp) tmp = self._update_from_object_attrs(tmp) # Store the result self.__setter__(name, tmp) return tmp
def __parse_VSM(self, header_line=3, data_line=3, header_delim=","): """An intrernal function for parsing deliminated data without a leading column of metadata.copy Keyword Arguments: header_line (int): The line in the file that contains the column headers. If None, then column headers are auotmatically generated. data_line (int): The line on which the data starts header_delim (strong): The delimiter used for separating header values Returns: Nothing, but modifies the current object. Note: The default values are configured fir read VSM data files """ try: with io.open(self.filename, errors="ignore", encoding="utf-8") as f: for i, line in enumerate(f): if i == 0: self["Timestamp"] = line.strip() check = datetime.strptime(self["Timestamp"], "%a %b %d %H:%M:%S %Y") if check is None: raise Core.StonerLoadError("Not a VSM file ?") elif i == 1: assertion(line.strip() == "") elif i == 2: header_string = line.strip() elif i == header_line: unit_string = line.strip() column_headers = [ "{} ({})".format(h.strip(), u.strip()) for h, u in zip(header_string.split(header_delim), unit_string.split(header_delim)) ] elif i > 3: break except (StonerAssertionError, ValueError, AssertionError, TypeError) as e: raise Core.StonerLoadError("Not a VSM File" + str(e.args)) self.data = np.genfromtxt( self.filename, dtype="float", usemask=True, skip_header=data_line - 1, missing_values=["6:0", "---"], invalid_raise=False, ) self.data = np.ma.mask_rows(self.data) cols = self.data.shape[1] self.data = np.reshape(self.data.compressed(), (-1, cols)) self.column_headers = column_headers self.setas(x="H_vsm (T)", y="m (emu)") # pylint: disable=not-callable
def crop_text(self, copy=False): """Crop the bottom text area from a standard Kermit image stack Returns: (self): cropped image """ assertion( (self[0].shape == AN_IM_SIZE or self[0].shape == IM_SIZE), "Need a full sized Kerr image to crop" ) # check it's a normal image crop = (0, IM_SIZE[1], 0, IM_SIZE[0]) self.crop_stack(box=crop)
def _get_scalebar(self): """Get the length in pixels of the image scale bar""" box = (0, 419, 519, 520) # row where scalebar exists im = self.crop(box=box, copy=True) im = im.astype(float) im = (im - im.min()) / (im.max() - im.min()) im = exposure.rescale_intensity(im, in_range=(0.49, 0.5)) # saturate black and white pixels im = exposure.rescale_intensity(im) # make sure they're black and white im = np.diff(im[0]) # 1d numpy array, differences lim = [np.where(im > 0.9)[0][0], np.where(im < -0.9)[0][0]] # first occurance of both cases assertion(len(lim) == 2, "Couldn't find scalebar") return lim[1] - lim[0]
def _read_uvwdata(self, filename, fmt, lineno): """Read the numerical data taking account of the format.""" if fmt == "Text": uvwdata = np.genfromtxt(self.filename, skip_header=lineno + 2) elif fmt == "Binary 4": if self["version"] == 1: dt = np.dtype(">f4") else: dt = np.dtype("<f4") with io.open(filename, "rb") as bindata: bindata.seek(self._ptr) uvwdata = np.fromfile(bindata, dtype=dt, count=1 + self["xnodes"] * self["ynodes"] * self["znodes"] * self["valuedim"]) assertion( uvwdata[0] == 1234567.0, "Binary 4 format check value incorrect ! Actual Value was {}" .format(uvwdata[0]), ) uvwdata = uvwdata[1:] uvwdata = np.reshape(uvwdata, (-1, self["valuedim"])) elif fmt == "Binary 8": if self["version"] == 1: dt = np.dtype(">f8") else: dt = np.dtype("<f8") with io.open(filename, "rb") as bindata: bindata.seek(self._ptr) uvwdata = np.fromfile(bindata, dtype=dt, count=1 + self["xnodes"] * self["ynodes"] * self["znodes"] * self["valuedim"]) assertion( (uvwdata[0] == 123456789012345.0), "Binary 4 format check value incorrect ! Actual Value was {}" .format(uvwdata[0]), ) uvwdata = np.reshape(uvwdata, (-1, self["valuedim"])) else: raise StonerLoadError("Unknow OVF Format {}".format(fmt)) return uvwdata
def peaks(self, **kargs): """Locates peaks and/or troughs in a column of data by using SG-differentiation. Args: ycol (index): the column name or index of the data in which to search for peaks width (int or float): the expected minium halalf-width of a peak in terms of the number of data points (int) or distance in x (float). This is used in the differnetiation code to find local maxima. Bigger equals less sensitive to experimental noise, smaller means better eable to see sharp peaks poly (int): the order of polynomial to use when differentiating the data to locate a peak. Must >=2, higher numbers will find sharper peaks more accurately but at the risk of finding more false positives. Keyword Arguments: significance (float): used to decide whether a local maxmima is a significant peak. Essentially just the curvature of the data. Bigger means less sensistive, smaller means more likely to detect noise. Default is the maximum curvature/(2*width) xcol (index or None): name or index of data column that p[rovides the x-coordinate (default None) peaks (bool): select whether to measure peaks in data (default True) troughs (bool): select whether to measure troughs in data (default False) sort (bool): Sor the results by significance of peak modify (book): If true, then the returned object is a copy of self with only the peaks/troughs left in the data. full_data (bool): If True (default) then all columns of the data at which peaks in the *ycol* column are found. *modify* true implies *full_data* is also true. If *full_data* is False, then only the x-column values of the peaks are returned. Returns: (various): If *modify* is true, then returns a the AnalysisMixin with the data set to just the peaks/troughs. If *modify* is false (default), then the return value depends on *ycol* and *xcol*. If *ycol* is not None and *xcol* is None, then returns conplete rows of data corresponding to the found peaks/troughs. If *xcol* is not None, or *ycol* is None and *xcol* is None, then returns a 1D array of the x positions of the peaks/troughs. See Also: User guide section :ref:`peak_finding` """ width = kargs.pop("width", int(len(self) / 20)) peaks = kargs.pop("peaks", True) troughs = kargs.pop("troughs", False) poly = kargs.pop("poly", 2) assertion( poly >= 2, "poly must be at least 2nd order in peaks for checking for significance of peak or trough" ) sort = kargs.pop("sort", False) modify = kargs.pop("modify", False) full_data = kargs.pop("full_data", True) _ = self._col_args(scalar=False, xcol=kargs.pop("xcol", None), ycol=kargs.pop("ycol", None)) xcol, ycol = _.xcol, _.ycol if isIterable(ycol): ycol = ycol[0] if isinstance( width, float): # Convert a floating point width unto an integer. xmin, xmax = self.span(xcol) width = int(len(self) * width / (xmax - xmin)) width = max(width, poly + 1) setas = self.setas.clone # pylint: disable=E0203 self.setas = "" d1 = self.SG_Filter(ycol, xcol=xcol, points=width, poly=poly, order=1).ravel() d2 = self.SG_Filter( ycol, xcol=xcol, points=2 * width, poly=poly, order=2).ravel() # 2nd differential requires more smoothing # We're going to ignore the start and end of the arrays index_offset = int(width / 2) d1 = d1[index_offset:-index_offset] d2 = d2[index_offset:-index_offset] # Pad the ends of d2 with the mean value pad = np.mean(d2[index_offset:-index_offset]) d2[:index_offset] = pad d2[-index_offset:] = pad # Set the significance from the 2nd ifferential if not already set significance = kargs.pop( "significance", np.max(np.abs(d2)) / (2 * width)) # Base an apriori significance on max d2y/dx2 / 20 if isinstance(significance, int): # integer significance is inverse to floating significance = np.max( np.abs(d2) ) / significance # Base an apriori significance on max d2y/dx2 / 20 d2_interp = interp1d(np.arange(len(d2)), d2, kind="cubic") # Ensure we have some X-data if xcol is None: xdata = np.arange(len(self)) else: xdata = self.column(xcol) xdata = interp1d(np.arange(len(self)), xdata, kind="cubic") possible_peaks = np.array( threshold(0, d1, rising=troughs, falling=peaks)) curvature = np.abs(d2_interp(possible_peaks)) # Filter just the significant peaks possible_peaks = np.array([ p for ix, p in enumerate(possible_peaks) if abs(curvature[ix]) > significance ]) # Sort in order of significance if sort: possible_peaks = np.take( possible_peaks, np.argsort(np.abs(d2_interp(possible_peaks)))) xdat = xdata(possible_peaks + index_offset) if modify: self.data = self.interpolate(xdat, xcol=xcol, kind="cubic") ret = self elif full_data: ret = self.interpolate(xdat, kind="cubic", xcol=False) else: ret = xdat self.setas = setas # Return - but remembering to add back on the offset that we took off due to differentials not working at # start and end return ret
def _load(self, filename=None, *args, **kargs): """Load function. File format has space delimited columns from row 3 onwards.""" if filename is None or not filename: self.get_filename("r") else: self.filename = filename self._ptr = 0 with io.open(self.filename, "r", errors="ignore", encoding="utf-8") as data: # Slightly ugly text handling line = next(data) self._ptr += len(line) line = line.strip() if "OOMMF: rectangular mesh" in line: if "v1.0" in line: self["version"] = 1 elif "v2.0" in line: self["version"] = 2 else: raise StonerLoadError( "Cannot determine version of OOMMFF file") else: # bug out oif we don't like the header raise StonerLoadError( "Not n OOMMF OVF File: opening line eas {}".format(line)) pattern = re.compile(r"#\s*([^\:]+)\:\s+(.*)$") i = None for i, line in enumerate(data): self._ptr += len(line) line.strip() if line.startswith( "# Begin: Data"): # marks the start of the trext break elif line.startswith("# Begin:") or line.startswith("# End:"): continue else: res = pattern.match(line) if res is not None: key = res.group(1) val = res.group(2) self[key] = string_to_type(val) else: raise StonerLoadError("Failed to understand metadata") fmt = re.match(r".*Data\s+(.*)", line).group(1).strip() assertion( (self["meshtype"] == "rectangular"), "Sorry only OVF files with rectnagular meshes are currently supported.", ) if self["version"] == 1: if self["meshtype"] == "rectangular": self["valuedim"] = 3 else: self["valuedim"] = 6 uvwdata = self._read_uvwdata(filename, fmt, i) x = (np.linspace(self["xmin"], self["xmax"], self["xnode"] + 1)[:-1] + self["xbase"]) * 1e9 y = (np.linspace(self["ymin"], self["ymax"], self["ynode"] + 1)[:-1] + self["ybase"]) * 1e9 z = (np.linspace(self["zmin"], self["zmax"], self["znode"] + 1)[:-1] + self["zbase"]) * 1e9 (y, z, x) = (np.ravel(i) for i in np.meshgrid(y, z, x)) self.data = np.column_stack((x, y, z, uvwdata)) column_headers = ["X (nm)", "Y (nm)", "Z (nm)", "U", "V", "W"] self.setas = "xyzuvw" self.column_headers = column_headers return self