Source code for pytplot.store_data

# Copyright 2020 Regents of the University of Colorado. All Rights Reserved.
# Released under the MIT license.
# This software was developed at the University of Colorado's Laboratory for Atmospheric and Space Physics.
# Verify current version before use at: https://github.com/MAVENSDC/PyTplot

from __future__ import division
import pandas as pd
import numpy as np
import datetime
from .del_data import del_data
import pytplot
import xarray as xr
from pytplot import tplot_utilities as utilities
import copy
tplot_num = 1


[docs]def store_data(name, data=None, delete=False, newname=None, attr_dict={}): """ This function creates a "Tplot Variable" based on the inputs, and stores this data in memory. Tplot Variables store all of the information needed to generate a plot. Parameters: name : str Name of the tplot variable that will be created data : dict A python dictionary object. 'x' should be a 1-dimensional array that represents the data's x axis. Typically this data is time, represented in seconds since epoch (January 1st 1970) 'y' should be the data values. This can be 2 dimensions if multiple lines or a spectrogram are desired. 'v' is optional, and is only used for spectrogram plots. This will be a list of bins to be used. If this is provided, then 'y' should have dimensions of x by z. 'v1/v2/v3/etc' are also optional, and are only used for to spectrogram plots. These will act as the coordinates for 'y' if 'y' has numerous dimensions. By default, 'v2' is plotted in spectrogram plots. 'x' and 'y' can be any data format that can be read in by the pandas module. Python lists, numpy arrays, or any pandas data type will all work. delete : bool, optional Deletes the tplot variable matching the "name" parameter newname: str Renames TVar to new name attr_dict: dict A dictionary object of attributes (these do not affect routines in pytplot, this is merely to keep metadata alongside the file) .. note:: If you want to combine multiple tplot variables into one, simply supply the list of tplot variables to the "data" parameter. This will cause the data to overlay when plotted. Returns: None Examples: >>> # Store a single line >>> import pytplot >>> x_data = [1,2,3,4,5] >>> y_data = [1,2,3,4,5] >>> pytplot.store_data("Variable1", data={'x':x_data, 'y':y_data}) >>> # Store a two lines >>> x_data = [1,2,3,4,5] >>> y_data = [[1,5],[2,4],[3,3],[4,2],[5,1]] >>> pytplot.store_data("Variable2", data={'x':x_data, 'y':y_data}) >>> # Store a spectrogram >>> x_data = [1,2,3] >>> y_data = [ [1,2,3] , [4,5,6], [7,8,9] ] >>> v_data = [1,2,3] >>> pytplot.store_data("Variable3", data={'x':x_data, 'y':y_data, 'v':v_data}) >>> # Combine two different line plots >>> pytplot.store_data("Variable1and2", data=['Variable1', 'Variable2']) >>> #Rename TVar >>> pytplot.store_data('a', data={'x':[0,4,8,12,16], 'y':[1,2,3,4,5]}) >>> pytplot.store_data('a',newname='f') """ global tplot_num create_time = datetime.datetime.now() # If delete is specified, we are just deleting the variable if delete is True: del_data(name) return False if data is None and newname is None: print('Please provide data.') return False # If newname is specified, we are just renaming the variable if newname is not None: pytplot.tplot_rename(name, newname) return True # If the data is a list instead of a dictionary, user is looking to overplot if isinstance(data, list): base_data = _get_base_tplot_vars(data) # Copying the first variable to use all of its plot options # However, we probably want each overplot to retain its original plot option pytplot.data_quants[name] = copy.deepcopy(pytplot.data_quants[base_data[0]]) pytplot.data_quants[name].attrs = copy.deepcopy(pytplot.data_quants[base_data[0]].attrs) pytplot.data_quants[name].name = name pytplot.data_quants[name].attrs['plot_options']['overplots'] = base_data[1:] return True # if the data table doesn't contain an 'x', assume this is a non-record varying variable if 'x' not in data.keys(): values = np.array(data.pop('y')) pytplot.data_quants[name] = {'data': values} pytplot.data_quants[name]['name'] = name return True times = data.pop('x') values = np.array(data.pop('y')) # If given a list of datetime objects, convert times to seconds since epoch. if any(isinstance(t, datetime.datetime) for t in times): for tt, time in enumerate(times): times[tt] = (time-datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)).total_seconds() # If given a list of datetime string, convert times to seconds since epoch elif any(isinstance(t, str) for t in times): for tt, time in enumerate(times): times[tt] = pytplot.tplot_utilities.str_to_int(time) if len(times) != len(values): print("The lengths of x and y do not match!") return False times = np.array(times) trange = [np.nanmin(times), np.nanmax(times)] # Figure out the 'v' data spec_bins_exist = False if 'v' in data or 'v1' in data or 'v2' in data or 'v3' in data: # Generally the data is 1D, but occasionally # the bins will vary in time. spec_bins_exist = True if 'v' in data: spec_bins = data['v'] spec_bins_dimension = 'v' else: spec_bins = data['v2'] spec_bins_dimension = 'v2' if type(spec_bins) is not pd.DataFrame: try: spec_bins = pd.DataFrame(spec_bins) except: if spec_bins_dimension=='v': spec_bins = np.arange(1, len(values[0])+1) else: spec_bins = np.arange(1, len(values[0][0]) + 1) spec_bins = pd.DataFrame(spec_bins) if len(spec_bins.columns) != 1: # The spec_bins are time varying spec_bins_time_varying = True if len(spec_bins) != len(times): print("Length of v and x do not match. Cannot create tplot variable.") return else: spec_bins = spec_bins.transpose() spec_bins_time_varying = False else: spec_bins = None # Provide another dimension if values are more than 1 dimension if len(values.shape) > 1: data['v'] = None if len(values.shape) > 2: data['v2'] = None if len(values.shape) > 3: data['v3'] = None # Set up xarray dimension and coordinates coordinate_list = sorted(list(data.keys())) dimension_list = [d + '_dim' for d in coordinate_list] temp = xr.DataArray(values, dims=['time']+dimension_list, coords={'time': ('time', times)}) if spec_bins_exist: try: if spec_bins_time_varying: temp.coords['spec_bins'] = (('time', spec_bins_dimension+'_dim'), spec_bins.values) else: temp.coords['spec_bins'] = (spec_bins_dimension+'_dim', np.squeeze(spec_bins.values)) except ValueError: print('Conflicting size for at least one dimension') for d in coordinate_list: if data[d] is None: continue try: d_dimension = pd.DataFrame(data[d]) if len(d_dimension.columns) != 1: if len(d_dimension) != len(times): print("Length of",d,"and time do not match. Cannot create coordinate for it.") continue temp.coords[d] = (('time', d+'_dim'), d_dimension.values) else: d_dimension = d_dimension.transpose() temp.coords[d] = (d+'_dim', np.squeeze(d_dimension.values)) except: print("Could not create coordinate", d+'_dim', "for variable", name) # Set up Attributes Dictionaries xaxis_opt = dict(axis_label='Time') yaxis_opt = dict(axis_label=name) if (spec_bins is None) else dict(axis_label='') zaxis_opt = dict(axis_label='Z-Axis') if (spec_bins is None) else dict(axis_label=name) xaxis_opt['crosshair'] = 'X' yaxis_opt['crosshair'] = 'Y' zaxis_opt['crosshair'] = 'Z' xaxis_opt['x_axis_type'] = 'linear' yaxis_opt['y_axis_type'] = 'linear' zaxis_opt['z_axis_type'] = 'linear' line_opt = {} time_bar = [] extras = dict(panel_size=1, char_size=10, border=True) links = {} # Add dicts to the xarray attrs temp.name = name temp.attrs = copy.deepcopy(attr_dict) if 'plot_options' not in temp.attrs.keys(): temp.attrs['plot_options'] = {} temp.attrs['plot_options']['xaxis_opt'] = xaxis_opt temp.attrs['plot_options']['yaxis_opt'] = yaxis_opt temp.attrs['plot_options']['zaxis_opt'] = zaxis_opt temp.attrs['plot_options']['line_opt'] = line_opt temp.attrs['plot_options']['trange'] = trange temp.attrs['plot_options']['time_bar'] = time_bar temp.attrs['plot_options']['extras'] = extras temp.attrs['plot_options']['create_time'] = create_time temp.attrs['plot_options']['links'] = links temp.attrs['plot_options']['spec_bins_ascending'] = _check_spec_bins_ordering(times, spec_bins) temp.attrs['plot_options']['overplots'] = [] temp.attrs['plot_options']['interactive_xaxis_opt'] = {} temp.attrs['plot_options']['interactive_yaxis_opt'] = {} pytplot.data_quants[name] = temp pytplot.data_quants[name].attrs['plot_options']['yaxis_opt']['y_range'] = utilities.get_y_range(temp) return True
def _get_base_tplot_vars(data): base_vars = [] if not isinstance(data, list): data = [data] for var in data: if isinstance(pytplot.data_quants[var].data, list): base_vars += _get_base_tplot_vars(pytplot.data_quants[var].data) else: base_vars += [var] return base_vars def _check_spec_bins_ordering(times, spec_bins): """ This is a private function, this is run during object creation to check if spec_bins are ascending or descending """ if spec_bins is None: return if len(spec_bins) == len(times): break_top_loop = False for index, row in spec_bins.iterrows(): if row.isnull().values.all(): continue else: for i in row.index: if np.isfinite(row[i]) and np.isfinite(row[i + 1]): ascending = row[i] < row[i + 1] break_top_loop = True break else: continue if break_top_loop: break else: ascending = spec_bins[0].iloc[0] < spec_bins[1].iloc[0] return ascending