Source code for PyLTSpice.raw.raw_read

# coding=utf-8

# -------------------------------------------------------------------------------
#    ____        _   _____ ____        _
#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
#          |___/                |_|
#
# Name:        raw_read.py
# Purpose:     Process LTSpice output files and align data for usage in a spreadsheet
#              tool such as Excel, or Calc.
#
# Author:      Nuno Brum (nuno.brum@gmail.com)
#
# Licence:     refer to the LICENSE file
# -------------------------------------------------------------------------------

"""
This module reads data from an LTSpice RAW file.
The main class object is the RawRead which is initialized with the filename of the RAW file to be processed.
The object wil read the file and construct a structure of objects which can be used to access the data inside the
RAW file.
To understand why this is done so, in the next section follows a brief explanation of what is contained inside a RAW
file.
In case RAW file contains stepped data detected, i.e. when the .STEP command is used, then it will also try to open the
simulation LOG file and read the stepping information.

RAW File Structure
==================

This section is written to help understand the why the structure of classes is defined as it is. You can gladly skip
this section and get right down to business by seeing the examples section below.

The RAW file starts with a text preamble that contains information about the names of the traces the order they
appear on the binary part and some extra information.
In the preamble, the lines are always started by one of the following identifiers:

   + Title:          => Contains the path of the source .asc file used to make the simulation preceded by *

   + Date:           => Date when the simulation started

   + Plotname:       => Name of the simulation. The known Simulation Types are:
                       * Operation Point
                       * DC transfer characteristic
                       * AC Analysis
                       * Transient Analysis
                       * Noise Spectral Density - (V/Hz½ or A/Hz½)
                       * Transfer Function

   + Flags:          => Flags that are used in this plot. The simulation can have any combination of these flags.
                      * "real" -> The traces in the raw file contain real values. As for example on a TRAN simulation.
                      * "complex" -> Traces in the raw file contain complex values. As for example on an AC simulation.
                      * "forward" -> Tells whether the simulation has more than one point. DC transfer
                        characteristic, AC Analysis, Transient Analysis or Noise Spectral Density have the forward flag.
                        Operating Point and Transfer Function don't have this flag activated.
                      * "log" -> The preferred plot view of this data is logarithmic.
                      * "stepped" -> The simulation had .STEP primitives.
                      * "FastAccess" -> Order of the data is changed to speed up access. See Binary section for details.

   + No. Variables:  => number of variables contained in this dataset. See section below for details.

   + No. Points:     => number of points per each variable in

   + Offset:         => when the saving of data started

   + Command:        => Name of the simulator executable generating this file.

   + Backannotation: => Backannotation alerts that occurred during simulation

   + Variables:      => a list of variable, one per line as described below

   + Binary:         => Start of the binary section. See section below for details.

Variables List
--------------
The variable list contains the list of measurements saved in the raw file. The order of the variables defines how they
are stored in the binary section. The format is one variable per line, using the following format:

<tab><ordinal number><tab><measurement><tab><type of measurement>

Here is an example:

.. code-block:: text

    0	time	time
    1	V(n001)	   voltage
    2	V(n004)	   voltage
    3	V(n003)	   voltage
    4	V(n006)	   voltage
    5	V(adcc)    voltage
    6	V(n002)	   voltage
    7	V(3v3_m)   voltage
    8	V(n005)	   voltage
    9	V(n007)	   voltage
    10	V(24v_dsp) voltage
    11	I(C3)	   device_current
    12	I(C2)	   device_current
    13	I(C1)	   device_current
    14	I(I1)	   device_current
    15	I(R4)	   device_current
    16	I(R3)	   device_current
    17	I(V2)	   device_current
    18	I(V1)	   device_current
    19	Ix(u1:+)   subckt_current
    20	Ix(u1:-)   subckt_current

Binary Section
--------------
The binary section of .RAW file is where the data is usually written, unless the user had explicitly specified an ASCII
representation. In this case this section is replaced with a "Values" section.
LTSpice stores data directly onto the disk during simulation, writing per each time or frequency step the list of
values, as exemplified below for a .TRAN simulation.

     <timestamp 0><trace1 0><trace2 0><trace3 0>...<traceN 0>

     <timestamp 1><trace1 1><trace2 1><trace3 1>...<traceN 1>

     <timestamp 2><trace1 2><trace2 2><trace3 2>...<traceN 2>

     ...

     <timestamp T><trace1 T><trace2 T><trace3 T>...<traceN T>
     
Depending on the type of simulation the type of data changes.
On TRAN simulations the timestamp is always stored as 8 bytes float (double) and trace values as 4 bytes (single).
On AC simulations the data is stored in complex format, which includes a real part and an imaginary part, each with 8
bytes.
The way we determine the size of the data is dividing the total block size by the number of points, then taking only
the integer part.

Fast Access
-----------

Once a simulation is done, the user can ask LTSpice to optimize the data structure in such that variables are stored
contiguously as illustrated below.

     <timestamp 0><timestamp 1>...<timestamp T>

     <trace1 0><trace1 1>...<trace1 T>

     <trace2 0><trace2 1>...<trace2 T>

     <trace3 0><trace3 1>...<trace3 T>

     ...

     <traceN T><traceN T>...<tranceN T>

This can speed up the data reading. Note that this transformation is not done automatically. Transforming data to Fast
Access must be requested by the user. If the transformation is done, it is registered in the Flags: line in the
header. RawReader supports both Normal and Fast Access formats

Classes Defined
===============

The .RAW file is read during the construction (constructor method) of an `RawRead` object. All traces on the RAW
file are uploaded into memory.

The RawRead class then has all the methods that allow the user to access the Axis and Trace Values. If there is
any stepped data (.STEP primitives), the RawRead class will try to load the log information from the same
directory as the raw file in order to obtain the STEP information.

Follows an example of the RawRead class usage. Information on the RawRead methods can be found here.

Examples
========

The example below demonstrates the usage of the RawRead class. It reads a .RAW file and uses the matplotlib
library to plot the results of three traces in two subplots. ::

    import matplotlib.pyplot as plt  # Imports the matplotlib library for plotting the results

    LTR = RawRead("some_random_file.raw")  # Reads the RAW file contents from file

    print(LTR.get_trace_names())  # Prints the contents of the RAW file. The result is a list, and print formats it.
    print(LTR.get_raw_property())  # Prints all the properties found in the Header section.

    plt.figure()  # Creates the canvas for plotting

    vin = LTR.get_trace('V(in)')  # Gets the trace data. If Numpy is installed, then it comes in numpy array format.
    vout = LTR.get_trace('V(out)') # Gets the second trace.

    steps = LTR.get_steps()  # Gets the step information. Returns a list of step numbers, ex: [0,1,2...]. If no steps
                             # are present on the RAW file, returns only one step : [0] .

    fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)  # Creates the two subplots. One on top of the other.

    for ax in (ax1, ax2):  # Crates a grid on all the plots.
        ax.grid(True)

    plt.xlim([0.9e-3, 1.2e-3])  # Optionally, limits the X axis to just a subrange.

    x = LTR.get_axis(0)  # Retrieves the time vector that will be used as X axis. Uses STEP 0
    ax1.plot(x, vin.get_wave(0)) # On first plot plots the first STEP (=0) of Vin

    for step in steps:  # On the second plot prints all the STEPS of the Vout
        x = LTR.get_axis(step)  # Retrieves the time vector that will be used as X axis.
        ax2.plot(x, vout.get_wave(step))

    plt.show()  # Creates the matplotlib's interactive window with the plots.

"""

__author__ = "Nuno Canto Brum <nuno.brum@gmail.com>"
__copyright__ = "Copyright 2022, Fribourg Switzerland"

import os

from collections import OrderedDict
from struct import unpack
from typing import Union, List, Tuple, Dict
from pathlib import Path

from .raw_classes import Axis, TraceRead, DummyTrace, SpiceReadException
from ..utils.detect_encoding import detect_encoding

import numpy as np
from numpy import zeros, complex128, float32, float64, frombuffer, angle
import logging
_logger = logging.getLogger("PyLTSpice.RawRead")

def read_float64(f):
    """
    Reads a 64-bit float value, normally associated with the plot X axis.
    The codification is done as follows:

    =====  === === === ===   === === === ===
    bit#   7   6   5   4     3   2   1   0
    =====  === === === ===   === === === ===
    Byte7  SGM SGE E9  E8    E7  E6  E5  E4
    Byte6  E3  E2  E1  E0    M51 M50 M49 M48
    Byte5  M47 M46 M45 M44   M43 M42 M41 M40
    Byte4  M39 M38 M37 M36   M35 M34 M33 M32
    Byte3  M31 M30 M29 M28   M27 M26 M25 M24
    Byte2  M23 M22 M21 M20   M19 M18 M17 M16
    Byte1  M15 M14 M13 M12   M11 M10 M9  M8
    Byte0  M7  M6  M5  M4    M3  M2  M1  M0
    =====  === === === ===   === === === ===

    Legend:

    SGM - Signal of Mantissa: 0 - Positive 1 - Negative

    SGE - Signal of Exponent: 0 - Positive 1 - Negative

    E[9:0] - Exponent

    M[51:0] - Mantissa.

    :param f: data stream to convert to float value
    :type f: file
    :returns: double precision float
    :rtype: float
    """
    s = f.read(8)
    return unpack("d", s)[0]


def read_complex(f):
    """
    Used to convert a 16 byte stream into a complex data point. Usually used for the .AC simulations.
    The encoding is the same as for the set_pointB8() but two values are encoded. First one is the real part and
    the second is the complex part.

    :param f: data stream
    :type f: file
    :return: complex value
    :rtype: complex
    """
    s = f.read(16)
    (re, im) = unpack('dd', s)
    return complex(re, im)


def read_float32(f):
    """
    Reads a 32bit float (single precision) from a stream. This is how most real values are stored in the RAW file.
    This codification uses 4 bytes as follows:

    =====  === === === ===   === === === ===
    bit#   7   6   5   4     3   2   1   0
    =====  === === === ===   === === === ===
    Byte3  SGM SGE E6  E5    E4  E3  E2  E1
    Byte2  E0  M22 M21 M20   M19 M18 M17 M16
    Byte1  M15 M14 M13 M12   M11 M10 M9  M8
    Byte0  M7  M6  M5  M4    M3  M2  M1  M0
    =====  === === === ===   === === === ===

    Legend:

    SGM - Signal of Mantissa: 0 - Positive 1 - Negative

    SGE - Signal of Exponent: 0 - Positive 1 - Negative

    E[6:0] - Exponent

    M[22:0] - Mantissa.

    :param f: data stream to read from
    :type f: file
    :returns: float value
    :rtype: float
    """
    s = f.read(4)
    return unpack("f", s)[0]


def consume4bytes(f):
    """Used to advance the file pointer 4 bytes"""
    f.read(4)


def consume8bytes(f):
    """Used to advance the file pointer 8 bytes"""
    f.read(8)


def consume16bytes(f):
    """Used to advance the file pointer 16 bytes"""
    f.read(16)


[docs]class RawRead(object): """Class for reading LTSpice wave Files. It can read all types of Files. If stepped data is detected, it will also try to read the corresponding LOG file so to retrieve the stepped data. :param raw_filename: The file containing the RAW data to be read :type raw_filename: str | pahtlib.Path :param traces_to_read: A string or a list containing the list of traces to be read. If None is provided, only the header is read and all trace data is discarded. If a '*' wildcard is given or no parameter at all then all traces are read. :type traces_to_read: str, list or tuple :key headeronly: Used to only load the header information and skip the trace data entirely. Use `headeronly=True`. """ header_lines = ( "Title", "Date", "Plotname", "Output", "Flags", "No. Variables", "No. Points", "Offset", "Command", "Variables", "Backannotation" ) ACCEPTED_PLOTNAMES = ( 'AC Analysis', 'DC transfer characteristic', 'Operating Point', 'Transient Analysis', 'Transfer Function', 'Noise Spectral Density', 'Frequency Response Analysis', ) def __init__(self, raw_filename: str, traces_to_read: Union[str, List[str], Tuple[str, ...], None] = '*', **kwargs): self.verbose = kwargs.get('verbose', True) raw_filename = Path(raw_filename) if traces_to_read is not None: assert isinstance(traces_to_read, (str, list, tuple)), "traces_to_read must be a string, a list or None" raw_file_size = os.stat(raw_filename).st_size # Get the file size in order to know the data size raw_file = open(raw_filename, "rb") ch = raw_file.read(6) if ch.decode(encoding='utf_8') == 'Title:': self.encoding = 'utf_8' sz_enc = 1 line = 'Title:' elif ch.decode(encoding='utf_16_le') == 'Tit': self.encoding = 'utf_16_le' sz_enc = 2 line = 'Tit' else: raise RuntimeError("Unrecognized encoding") if self.verbose: _logger.debug(f"Reading the file with encoding: '{self.encoding}' ") # Storing the filename as part of the dictionary self.raw_params = OrderedDict(Filename=raw_filename) # Initializing the dict that contains all raw file info self.backannotations = [] # Storing backannotations header = [] binary_start = 6 while True: ch = raw_file.read(sz_enc).decode(encoding=self.encoding, errors='replace') binary_start += sz_enc if ch == '\n': if self.encoding == 'utf_8': # must remove the \r line = line.rstrip('\r') header.append(line) if line in ('Binary:', 'Values:'): self.raw_type = line break line = "" else: line += ch for line in header: k, _, v = line.partition(':') if k == 'Variables': break self.raw_params[k] = v.strip() self.nPoints = int(self.raw_params['No. Points'], 10) self.nVariables = int(self.raw_params['No. Variables'], 10) has_axis = self.raw_params['Plotname'] not in ('Operating Point', 'Transfer Function',) self._traces = [] self.steps = None self.axis = None # Creating the axis self.flags = self.raw_params['Flags'].split() if 'complex' in self.raw_params['Flags'] or self.raw_params['Plotname'] == 'AC Analysis': numerical_type = 'complex' else: if 'QSPICE' in self.raw_params['Command']: # QSPICE uses doubles for everything numerical_type = 'double' else: numerical_type = 'real' i = header.index('Variables:') ivar = 0 for line in header[i + 1:-1]: # Parse the variable names idx, name, var_type = line.lstrip().split('\t') if has_axis and ivar == 0: # If it has an axis, it should be always read if numerical_type == 'real': axis_numerical_type = 'double' else: axis_numerical_type = numerical_type self.axis = Axis(name, var_type, self.nPoints, axis_numerical_type) trace = self.axis elif (traces_to_read == "*") or (name in traces_to_read): if has_axis: # Reads data trace = TraceRead(name, var_type, self.nPoints, self.axis, numerical_type) else: # If an Operation Point or Transfer Function, only one point per step trace = TraceRead(name, var_type, self.nPoints, None, 'real') else: trace = DummyTrace(name, var_type) self._traces.append(trace) ivar += 1 if traces_to_read is None or len(self._traces) == 0: # The read is stopped here if there is nothing to read. raw_file.close() return if kwargs.get("headeronly", False): raw_file.close() return if self.verbose: _logger.info("File contains {} traces, reading {}".format(ivar, len([trace for trace in self._traces if not isinstance(trace, DummyTrace)]))) if self.raw_type == "Binary:": # Will start the reading of binary values # But first check whether how data is stored. self.block_size = (raw_file_size - binary_start) // self.nPoints self.data_size = self.block_size // self.nVariables scan_functions = [] for trace in self._traces: if self.data_size == 8: if isinstance(trace, DummyTrace): fun = consume8bytes else: fun = read_float64 elif self.data_size == 16: if isinstance(trace, DummyTrace): fun = consume16bytes else: fun = read_complex else: # data size is only 4 bytes if len(scan_functions) == 0: # This is the axis fun = read_float64 else: if isinstance(trace, DummyTrace): fun = consume4bytes else: fun = read_float32 scan_functions.append(fun) if "fastaccess" in self.raw_params["Flags"]: if self.verbose: _logger.debug("Binary RAW file with Fast access") # Fast access means that the traces are grouped together. for i, var in enumerate(self._traces): if isinstance(var, DummyTrace): # TODO: replace this by a seek raw_file.read(self.nPoints * self.data_size) else: if self.data_size == 8: s = raw_file.read(self.nPoints * 8) var.data = frombuffer(s, dtype=float64) elif self.data_size == 16: s = raw_file.read(self.nPoints * 16) var.data = frombuffer(s, dtype=complex) else: if i == 0: s = raw_file.read(self.nPoints * 8) var.data = frombuffer(s, dtype=float64) else: s = raw_file.read(self.nPoints * 4) var.data = frombuffer(s, dtype=float32) else: if self.verbose: _logger.debug("Binary RAW file with Normal access") # This is the default save after a simulation where the traces are scattered for point in range(self.nPoints): for i, var in enumerate(self._traces): value = scan_functions[i](raw_file) if value is not None and not isinstance(var, DummyTrace): var.data[point] = value elif self.raw_type == "Values:": if self.verbose: _logger.debug("ASCII RAW File") # Will start the reading of ASCII Values for point in range(self.nPoints): first_var = True for var in self._traces: line = raw_file.readline().decode(encoding=self.encoding, errors='ignore') if first_var: first_var = False s_point = line.split("\t", 1)[0] if point != int(s_point): _logger.error("Error Reading File") break value = line[len(s_point):-1] else: value = line[:-1] if not isinstance(var, DummyTrace): var.data[point] = float(value) else: raw_file.close() raise SpiceReadException("Unsupported RAW File. ""%s""" % self.raw_type) raw_file.close() # Setting the properties in the proper format self.raw_params["No. Points"] = self.nPoints self.raw_params["No. Variables"] = self.nVariables self.raw_params["Variables"] = [var.name for var in self._traces] # Now Purging Dummy Traces i = 0 while i < len(self._traces): if isinstance(self._traces[i], DummyTrace): del self._traces[i] else: i += 1 # Finally, Check for Step Information if "stepped" in self.raw_params["Flags"]: try: self._load_step_information(raw_filename) except SpiceReadException: _logger.warning("LOG file for ""%s"" not found or problems happened while reading it.\n" " Auto-detecting steps" % raw_filename) if has_axis: number_of_steps = 0 for v in self.axis.data: if v == self.axis.data[0]: number_of_steps += 1 else: number_of_steps = self.nPoints self.steps = [{'run': i + 1} for i in range(number_of_steps)] if self.steps is not None: if has_axis: # Individual access to the Trace Classes, this information is stored in the Axis # which is always in position 0 self._traces[0]._set_steps(self.steps)
[docs] def get_raw_property(self, property_name=None): """ Get a property. By default, it returns all properties defined in the RAW file. :param property_name: name of the property to retrieve. :type property_name: str :returns: Property object :rtype: str :raises: ValueError if the property doesn't exist """ if property_name is None: return self.raw_params elif property_name in self.raw_params.keys(): return self.raw_params[property_name] else: raise ValueError("Invalid property. Use %s" % str(self.raw_params.keys()))
[docs] def get_trace_names(self): """ Returns a list of exiting trace names of the RAW file. :return: trace names :rtype: list[str] """ return [trace.name for trace in self._traces]
[docs] def get_trace(self, trace_ref: Union[str, int]): """ Retrieves the trace with the requested name (trace_ref). :param trace_ref: Name of the trace or the index of the trace :type trace_ref: str or int :return: An object containing the requested trace :rtype: DataSet subclass :raises IndexError: When a trace is not found """ if isinstance(trace_ref, str): for trace in self._traces: if trace_ref.upper() == trace.name.upper(): # The trace names are case-insensitive # assert isinstance(trace, DataSet) return trace raise IndexError(f"{self} doesn't contain trace \"{trace_ref}\"\n" f"Valid traces are {[trc.name for trc in self._traces]}") else: return self._traces[trace_ref]
[docs] def get_wave(self, trace_ref: Union[str, int], step: int = 0): """ Retrieves the trace data with the requested name (trace_ref), optionally providing the step number. :param trace_ref: Name of the trace or the index of the trace :type trace_ref: str or int :param step: Optional parameter specifying which step to retrieve. :type step: int :return: A numpy array containing the requested waveform. :rtype: numpy.array :raises IndexError: When a trace is not found """ return self.get_trace(trace_ref).get_wave(step)
[docs] def get_time_axis(self, step: int = 0): """ *(Deprecated)* Use get_axis method instead This function is equivalent to get_trace('time').get_time_axis(step) instruction. It's workaround on a LTSpice issue when using 2nd Order compression, where some values on the time trace have a negative value.""" return self.get_trace('time').get_time_axis(step)
[docs] def get_axis(self, step: int = 0): """ This function is equivalent to get_trace(0).get_wave(step) instruction. It also implements a workaround on a LTSpice issue when using 2nd Order compression, where some values on the time trace have a negative value. :param step: Step number :type step: int :returns: Array with the X axis :rtype: list[float] or numpy.array """ if self.axis: axis = self.get_trace(0) assert isinstance(axis, Axis), "This RAW file does not have an axis." return axis.get_wave(step) else: raise RuntimeError("This RAW file does not have an axis.")
[docs] def get_len(self, step: int = 0) -> int: """ Returns the length of the data at the give step index. :param step: Optional parameter the step index. :type step: int :return: The number of data points :rtype: int """ return self.axis.get_len(step)
def _load_step_information(self, filename: Path): # Find the extension of the file if not filename.suffix == ".raw": raise SpiceReadException("Invalid Filename. The file should end with '.raw'") logfile = filename.with_suffix(".log") try: encoding = detect_encoding(logfile, "Circuit:") log = open(logfile, 'r', errors='replace', encoding=encoding) except OSError: raise SpiceReadException("Step information needs the '.log' file generated by LTSpice") except UnicodeError: raise SpiceReadException("Unable to parse log file and obtain .STEP information. Check Unicode") for line in log: if line.startswith(".step"): step_dict = {} for tok in line[6:-1].split(' '): key, value = tok.split('=') try: # Tries to convert to float for backward compatibility value = float(value) except ValueError: pass # Leave value as a string to accommodate cases like temperature steps. # Temperature steps have the form '.step temp=25°C' step_dict[key] = value if self.steps is None: self.steps = [step_dict] else: self.steps.append(step_dict) log.close() def __getitem__(self, item): """Helper function to access traces by using the [ ] operator.""" return self.get_trace(item)
[docs] def get_steps(self, **kwargs): """Returns the steps that correspond to the query set in the * * kwargs parameters. Example: :: raw_read.get_steps(V5=1.2, TEMP=25) This will return all steps in which the voltage source V5 was set to 1.2V and the TEMP parameter is 24 degrees. This feature is only possible if a .log file with the same name as the .raw file exists in the same directory. Note: the correspondence between step numbers and .STEP information is stored on the .log file. :key kwargs: key-value arguments in which the key correspond to a stepped parameter or source name, and the value is the stepped value. :return: The steps that match the query :rtype: list[int] """ if self.steps is None: return [0] # returns a single step else: if len(kwargs) > 0: ret_steps = [] # Initializing an empty array i = 0 for step_dict in self.steps: for key in kwargs: ll = step_dict.get(key, None) if ll is None: break elif kwargs[key] != ll: break else: ret_steps.append(i) # All the step parameters match i += 1 return ret_steps else: return range(len(self.steps)) # Returns all the steps
[docs] def export(self, columns: list = None, step: Union[int, List[int]] = -1, **kwargs) -> Dict[str, list]: """ Returns a native python class structure with the requested trace data and steps. It consists of an ordered dictionary where the columns are the keys and the values are lists with the data. This function is used by the export functions. :param step: Step number to retrieve. If not given, it will return all steps :type step: int :param columns: List of traces to use as columns. Default is all traces :type columns: list :param kwargs: Additional arguments to pass to the pandas.DataFrame constructor :type kwargs: \*\*dict :return: A pandas DataFrame :rtype: pandas.DataFrame """ if columns is None: columns = self.get_trace_names() # if no columns are given, use all traces else: if self.axis and self.axis.name not in columns: # If axis is not in the list, add it columns.insert(0, self.axis.name) if isinstance(step, list): steps_to_read = step # If a list of steps is given, use it elif step == -1: steps_to_read = self.get_steps(**kwargs) # If no step is given, read all steps else: steps_to_read = [step] # If a single step is given, pass it as a list step_columns = [] if len(step_columns) > 1: for step_dict in self.steps[0]: for key in step_dict: step_columns.append(key) data = OrderedDict() # Create the headers with the column names and empty lists for col in columns: data[col] = [] for col in step_columns: data[col] = [] # Read the data for step in steps_to_read: for col in columns: data[col] += list(self.get_wave(col, step)) for col in step_columns: data[col] += [self.steps[step][col]] * len(data[columns[0]]) return data
[docs] def to_dataframe(self, columns: list = None, step: Union[int, List[int]] = -1, **kwargs): """ Returns a pandas DataFrame with the requested data. :param step: Step number to retrieve. If not given, it :type step: int :param columns: List of traces to use as columns. Default is all traces :type columns: list :param kwargs: Additional arguments to pass to the pandas.DataFrame constructor :type kwargs: \*\*dict :return: A pandas DataFrame :rtype: pandas.DataFrame """ try: import pandas as pd except ImportError: raise ImportError("The 'pandas' module is required to use this function.\n" "Use 'pip install pandas' to install it.") data = self.export(columns=columns, step=step, **kwargs) return pd.DataFrame(data, **kwargs)
[docs] def to_csv(self, filename: Union[str, Path], columns: list = None, step: Union[int, List[int]] = -1, separator=',', **kwargs): """ Saves the data to a CSV file. :param filename: Name of the file to save the data to :type filename: str :param columns: List of traces to use as columns. Default is all traces :type columns: list :param step: Step number to retrieve. If not given, it :type step: int :param separator: separator to use in the CSV file :type separator: str :param kwargs: Additional arguments to pass to the pandas.DataFrame.to_csv function :type kwargs: \*\*dict """ try: import pandas as pd except ImportError: use_pandas = False else: use_pandas = True if use_pandas: df = self.to_dataframe(columns=columns, step=step) df.to_csv(filename, sep=separator, **kwargs) else: # Export to CSV using python built-in functions data = self.export(columns=columns, step=step) with open(filename, 'w') as f: f.write(separator.join(data.keys()) + '\n') for i in range(len(data[columns[0]])): f.write(separator.join([str(data[col][i]) for col in data.keys()]) + '\n')
[docs] def to_excel(self, filename: Union[str, Path], columns: list = None, step: Union[int, List[int]] = -1, **kwargs): """ Saves the data to an Excel file. :param filename: Name of the file to save the data to :type filename: str :param columns: List of traces to use as columns. Default is all traces :type columns: list :param step: Step number to retrieve. If not given, it :type step: int :param kwargs: Additional arguments to pass to the pandas.DataFrame.to_excel function :type kwargs: \*\*dict """ try: import pandas as pd except ImportError: raise ImportError("The 'pandas' module is required to use this function.\n" "Use 'pip install pandas' to install it.") df = self.to_dataframe(columns=columns, step=step) df.to_excel(filename, **kwargs)
# Backward compatibility naming LTSpiceRawRead = RawRead