def output_df(merge_df, modes): """ This function saves the merged dataframe to a CSV file """ # output the dataframe if requested if (modes['out_dir'] != None) & ('file' in modes['plots']): path_out_df = prep_out_file(modes, plot="file", out_type=".csv") try: merge_df.to_csv(path_out_df) except IOError: if modes['verbose'] >= 1: print("WARNING: unable to output to file:\n\t" + path_out_df) if (modes['out_dir'] == None) & ('file' in modes['plots']): if modes['verbose'] >= 1: print("ERROR: file output requested, but no directory selected.")
def analysis_nd(merge_df, modes, m_keys, sources): ''' This function carries out all plotting and calculations needed for a n-d dataset (i.e. multiple frequencies) Future iterations may include optional arguments to enable selection of the plots that are preferred ''' if modes['verbose'] >= 2: print("Carrying out multi-frequency Analysis") if "spectra" in modes["plots"]: plot_spectra_nf(merge_df, m_keys, modes, sources) if any(plot in modes["plots"] for plot in ["alt", "az", "ew"]): if all(coord in merge_df for coord in ["alt", "az", "az_ew"]): plot_altaz_values_nf(merge_df, m_keys, modes, sources) else: if modes['verbose'] >= 1: print( "Warning: Alt-Azimuth plotting selected, but not available!" ) #calculates the figures of merit at each independent variable #return values are stored as possible future outputs ind_dfs = {} #checks to see if there are differences to analyse if (any("diff" in col_name for col_name in merge_df.columns)): foms = [] if "corr" in modes["plots"]: foms.append("corr") if "rmse" in modes["plots"]: foms.append("rmse") for fom in foms: ind_dfs = plot_fom_vs_ind(merge_df, m_keys, modes, fom) #calculates the overall figure of merit between scope and model fom_1 = calc_fom_1d(merge_df, m_keys, fom) #prints that coefficient for each key and correlation for i in range(len(m_keys)): out_str=("The "+str(m_keys[i])+"-channel "+\ gen_pretty_name(fom)+" is "+str(fom_1[i])) if modes['out_dir'] == None: print(out_str) else: plt_file = prep_out_file(modes, plot=fom, dims="1d", channel=m_keys[i], out_type="txt") out_file = open(plt_file, 'a') out_file.write(out_str) out_file.close() else: if "corr" in modes["plots"]: if modes['verbose'] >= 1: print( "Warning: Correlation selected, but no differences available" ) if "rmse" in modes["plots"]: if modes['verbose'] >= 1: print("Warning: RMSE selected, but no differences available") str_channel = channel_maker(m_keys, modes) if modes['out_dir'] != None: for plot_item in ind_dfs: #prints the correlations to a file path_out_df = prep_out_file(modes, plot=plot_item, channel=str_channel, out_type=".csv") try: ind_dfs[plot_item].to_csv(path_out_df) except IOError: if modes['verbose'] >= 1: print("WARNING: Unable to output to file:\n\t" + path_out_df) return (ind_dfs)
def analysis_1d(merge_df, modes, m_keys, sources): ''' This function carries out all plotting and calculations needed for a 1-d dataset (i.e. one frequency) Future iterations may include optional arguments to enable selection of the plots that are preferred ''' if modes['verbose'] >= 2: print("Carrying out 1-frequency Analysis") # if "spectra" in modes["plots"]: # #plots the values for each channel # plot_values_1f(merge_df, m_keys, modes) # # if "diff" in modes["plots"]: # #plots the differences in the values # plot_diff_values_1f(merge_df, m_keys, modes) if "spectra" in modes["plots"]: #plots the values for each channel plots_1f(merge_df, m_keys, modes, "Time", sources) if ((all(coord in merge_df for coord in ["alt", "az", "az_ew"])) and (any(plot in modes["plots"] for plot in ["alt", "az", "ew"]))): alt_var, az_var, az_var_ew = get_alt_az_var(merge_df, modes) if "alt" in modes["plots"]: plots_1f(merge_df, m_keys, modes, alt_var, sources) if "az" in modes["plots"]: plots_1f(merge_df, m_keys, modes, az_var, sources) elif any(plot in modes["plots"] for plot in ["alt", "az", "ew"]): if modes['verbose'] >= 1: print("Warning: Horizontal coordinates selected but unavailable") #checks to see if there are differences to analyse if (any("diff" in col_name for col_name in merge_df.columns)): foms = [] if "corr" in modes["plots"]: foms.append("corr") if "rmse" in modes["plots"]: foms.append("rmse") for fom in foms: fom_results = calc_fom_1d(merge_df, m_keys, fom) for i in range(len(m_keys)): out_str = ("The " + str(m_keys[i]) + "-channel " + gen_pretty_name(fom) + " is " + str(fom_results[i])) if modes['out_dir'] == None: print("\n" + out_str + "\n") else: #creates an output-friendly string for the channel str_channel = channel_maker(m_keys, modes) plt_file = prep_out_file(modes, plot=fom, dims="1d", channel=str_channel, out_type="txt") out_file = open(plt_file, 'a') out_file.write(out_str) out_file.close() else: if "corr" in modes["plots"]: if modes['verbose'] >= 1: print( "Warning: Correlation selected, but no differences available" ) if "rmse" in modes["plots"]: if modes['verbose'] >= 1: print("Warning: RMSE selected, but no differences available")
def calc_fom_nd(in_df, var_str, m_keys, modes, fom="rmse"): """ This function calculates a figure of merit between the scope and model values for the specified channels as they are distributed against another column of the dataframe merge_df which is identified by var_str in current versions, useable values for var_str are "Time" and "Freq" in current versions, useable values for fom are "rmse" and "corr" """ print("probe: ", modes['colour']) if modes['verbose'] >= 2: print("Calculating the " + gen_pretty_name(fom) + " between observed and model data.") # creates empty lists for the Errors n_foms = [] for i in range(len(m_keys)): n_foms.append([]) # identifies allthe unique values of the variable in the column unique_vals = in_df[var_str].unique() unique_vals = np.sort(unique_vals) # iterates over all unique values for unique_val in unique_vals: # creates a dataframe with only the elements that match the current # unique value unique_merge_df = in_df[in_df[var_str] == unique_val].copy().reset_index(drop=True) # uses this unique value for and the 1-dimensional calc_fom_1d function # to calculate the Figure of merit for each channel n_fom = calc_fom_1d(unique_merge_df, m_keys, fom) # appends these to the list for i in range(len(m_keys)): n_foms[i].append(n_fom[i]) # creates an overlaid plot of how the Figure of Merit between model and scope # varies for each of the channels against var_str if modes['verbose'] >= 2: print("Plotting the " + gen_pretty_name(fom) + " between model and scope for " + channel_maker(m_keys, modes, ", ") + " against " + gen_pretty_name(var_str)) if modes['colour'] in ["matching", "matching_dark"] and len(m_keys) == 1: text_colour = colour_models(m_keys[0]) elif modes['colour'] in ["dark", "matching_dark"]: text_colour = "white" else: # light or None text_colour = "black" mpl.rcParams.update({ 'text.color': text_colour, 'axes.labelcolor': text_colour, 'xtick.color': text_colour, 'ytick.color': text_colour }) mpl.rc('axes', edgecolor=text_colour) fig, ax = plt.subplots() if modes['dpi'] is None: pass else: fig.set_dpi(modes['dpi']) if modes['image_size'] is None: pass # do nothing else: fig.set_size_inches(modes['image_size']) if modes['colour'] in ["dark", "matching_dark"]: ax.set_facecolor('black') fig.patch.set_facecolor('black') graph_title = "\n".join([modes['title'],"Plot of the "+gen_pretty_name(fom)+\ " in "]) for key in m_keys: plt.plot(plottable(unique_vals, var_str), n_foms[m_keys.index(key)], label=key + '_' + fom, color=colour_models(key)) graph_title = add_key(graph_title, m_keys, key) # calculates and adds title with frequency in MHz graph_title = graph_title + "-channels over " + gen_pretty_name(var_str) plt.title(graph_title, wrap=True) # rotates the labels. This is necessary for timestamps plt.xticks(rotation=90) plt.legend(frameon=False) plt.xlabel(gen_pretty_name(var_str, units=True), wrap=True) # prints or saves the plot if modes['out_dir'] == None: plt.show() else: # creates an output-friendly string for the channel str_channel = list_to_string(m_keys, '_') plt_file = prep_out_file(modes, plot=fom, ind_var=var_str, channel=str_channel, out_type=modes['image_type']) if modes['verbose'] >= 2: print("Saving: " + plt_file) try: plt.savefig(plt_file, bbox_inches='tight', facecolor=fig.get_facecolor(), edgecolor='none') except ValueError: if modes['verbose'] >= 1: print("ERROR: Unable to save file, showing instead.") try: plt.show() except: print("Unable to show file.") plt.close() # returns the correlation lists if needed return (n_foms)
def four_var_plot(in_df, modes, var_x, var_y, var_z, var_y2, source, plot_name=""): """ Plots a two part plot of four variables from merge_df as controlled by modes. Plot 1 is a 3-d colour plot with x, y and z variables controlled by arguments. Plot 2 is a 2-d scatter plot with the same x parameter and another y variable var_z must be one of the dependent variables """ if modes['verbose'] >= 2: print("Plotting " + gen_pretty_name(source) + "\nfor " + gen_pretty_name(var_z) + " against " + gen_pretty_name(var_x) + " and " + gen_pretty_name(var_y) + " and " + gen_pretty_name(var_y2, plot_name) + " against " + gen_pretty_name(var_x)) if modes['colour'] in ["matching", "matching_dark"]: text_colour = colour_models(var_y) elif modes['colour'] == "dark": text_colour = "white" else: # light or None text_colour = "black" mpl.rcParams.update({ 'text.color': text_colour, 'axes.labelcolor': text_colour, 'xtick.color': text_colour, 'ytick.color': text_colour }) mpl.rc('axes', edgecolor=text_colour) fig, ax = plt.subplots() if modes['dpi'] is None: pass else: fig.set_dpi(modes['dpi']) if modes['image_size'] is None: pass # do nothing else: fig.set_size_inches(modes['image_size']) if modes['colour'] in ["dark", "matching_dark"]: ax.set_facecolor('black') fig.patch.set_facecolor('black') # create a 2 X 2 grid gs = grd.GridSpec(2, 2, height_ratios=[1, 1], width_ratios=[20, 1], wspace=0.1) plt.subplot(gs[0]) upper_title = ("Plot of " + gen_pretty_name(source) + "\nfor " + gen_pretty_name(var_z) + " against " + gen_pretty_name(var_x) + " and " + gen_pretty_name(var_y)) label = "\n".join([modes["title"], upper_title]) plt.title(label, wrap=True) sep = get_source_separator(source) # try: # # plots the channel in a colour based on its name # plt.tripcolor(plottable(in_df,var_x), # plottable(in_df,var_y), # plottable(in_df,(var_z+sep+source)), # cmap=plt.get_cmap(colour_models(var_z+'_s'))) # # except RuntimeError: # if modes['verbose'] >=1: # print("ERROR: Data not suitable for 3d colour plot. Possible alternatives: animated plots") # plots the channel in a colour based on its name colours = plt.get_cmap(colour_models(var_z + '_s')) if (modes["three_d"] in ["colour", "color"]): try: x_vals = plottable(in_df, var_x) y_vals = plottable(in_df, var_y) if "percent" in modes["scale"]: z_vals = plottable(in_df, var_z) * 100 else: z_vals = plottable(in_df, var_z) if modes["scale"] == "log": # finds the limits of the z variable maxz = np.max(z_vals) minz = np.min(z_vals) # if the values go below zero, then plot with symmetric log, otherwise use log plotting if minz <= 0: log_lim = 10 linthresh = max([abs(maxz), abs(minz)]) / log_lim norm = SymLogNorm(linthresh, linscale=1.0, vmin=minz, vmax=maxz, clip=False) else: norm = LogNorm() p = plt.tripcolor(x_vals, y_vals, z_vals, cmap=colours, norm=norm) else: p = plt.tripcolor(x_vals, y_vals, z_vals, cmap=colours) except RuntimeError: if modes['verbose'] >= 1: print( "ERROR: Data not suitable for 3d colour plot. Possible alternatives: contour/animated plots" ) elif (modes["three_d"] in ["contour"]): try: cols = np.unique(in_df[var_y]).shape[0] x_vals = np.array(in_df[var_x]).reshape(-1, cols) y_vals = np.array(in_df[var_y]).reshape(-1, cols) if "percent" in modes["scale"]: z_vals = np.array(in_df[(var_z + sep + source)]).reshape( -1, cols) * 100 else: z_vals = np.array(in_df[(var_z + sep + source)]).reshape( -1, cols) plt.contour(x_vals, y_vals, z_vals, cmap=colours) except: if modes['verbose'] >= 1: print( "ERROR: Data not suitable for 3d contour plot. Possible alternatives: colour/animated plots" ) # TODO: fix percentile plotting limits plt.clim(np.percentile(plottable(in_df, (var_z + sep + source)), 5), np.percentile(plottable(in_df, (var_z + sep + source)), 95)) # plots axes plt.xticks([]) plt.ylabel(gen_pretty_name(var_y, units=True), wrap=True) # color bar in it's own axis colorAx = plt.subplot(gs[1]) if "percent" in modes["scale"]: cb = plt.colorbar(p, cax=colorAx, format='%.3g%%') else: cb = plt.colorbar(p, cax=colorAx) cb.set_label(source + " for " + var_z) plt.subplot(gs[2]) lower_title = ("Plot of "+gen_pretty_name(var_y2, plot_name)+" against "+\ gen_pretty_name(var_x)) plt.title(lower_title, wrap=True) # plots the scattergraph plt.plot(plottable(in_df, var_x), plottable(in_df, var_y2), color=colour_models(var_y2), marker=".", linestyle="None") plt.xlabel(gen_pretty_name(var_x, units=True), wrap=True) plt.ylabel(gen_pretty_name(var_y2, units=True), wrap=True) plt.legend(frameon=False) # prints or saves the plot if modes['out_dir'] == None: plt.show() else: plt_file = prep_out_file(modes, source=source, plot=var_x, dims="nd", channel=var_z, ind_var=var_y, plot_name=plot_name, out_type=modes['image_type']) if modes['verbose'] >= 2: print("Saving: " + plt_file) try: plt.savefig(plt_file, bbox_inches='tight', facecolor=fig.get_facecolor(), edgecolor='none') except ValueError: if modes['verbose'] >= 1: print("ERROR: Unable to save file, showing instead.") try: plt.show() except: print("Unable to show file.") plt.close()
def plot_1f(merge_df, m_keys, modes, sources, var_str): #creates an overlaid plot of how the sources #varies for each of the channels against var_str title = "Plot of " for source in sources: title = add_key(title, sources, source) title = title + " for " for key in m_keys: title = add_key(title, m_keys, key) title = title + "-channels over " + gen_pretty_name(var_str) freq_in = modes['freq'][0] freq_MHz = freq_in / 1e6 title = title + "\nat a Frequency of {:7.3f} MHz".format(freq_MHz) if modes['verbose'] >= 2: print(title) if modes['colour'] in ["matching", "matching_dark"] and len(m_keys) == 1: text_colour = colour_models(m_keys[0]) elif modes['colour'] in ["dark", "matching_dark"]: text_colour = "white" else: # light or None text_colour = "black" mpl.rcParams.update({ 'text.color': text_colour, 'axes.labelcolor': text_colour, 'xtick.color': text_colour, 'ytick.color': text_colour }) mpl.rc('axes', edgecolor=text_colour) fig, ax = plt.subplots() if modes['dpi'] is None: pass else: fig.set_dpi(modes['dpi']) if modes['image_size'] is None: pass # do nothing else: fig.set_size_inches(modes['image_size']) if modes['colour'] in ["dark", "matching_dark"]: ax.set_facecolor('black') fig.patch.set_facecolor('black') for key in m_keys: for source in sources: sep = get_source_separator(source) var_y_vals = plottable(merge_df, (key + sep + source)) # sets the y axis scale to percentage if requested. if 'percent' in modes['scale']: var_y_vals = var_y_vals * 100 ax.plot(plottable(merge_df, var_str), var_y_vals, label=key + sep + source, color=colour_models(key + sep + source)) ax.set_title(title, wrap=True) ax.legend(frameon=False) # plots the axis labels rotated so they're legible ax.tick_params(labelrotation=45) ax.set_xlabel(gen_pretty_name(var_str, units=True)) # sets the y axis scale to logarithmic if requested. if 'log' in modes['scale']: ax.set_yscale('log') # sets the y axis scale to percentage if requested. if 'percent' in modes['scale']: ax.yaxis.set_major_formatter(mtick.PercentFormatter()) # prints or saves the plot if modes['out_dir'] == None: plt.show() else: str_sources = channel_maker(sources, modes) plt_file = prep_out_file(modes, plot="vals", dims="1d", ind_var=var_str, channel=list_to_string(m_keys, '_'), source=str_sources, freq=min(merge_df.Freq), out_type="png") if modes['verbose'] >= 2: print("Saving: " + plt_file) try: plt.savefig(plt_file, bbox_inches='tight', facecolor=fig.get_facecolor(), edgecolor='none') except ValueError: if modes['verbose'] >= 1: print("ERROR: Unable to save file, showing instead.") try: plt.show() except: print("Unable to show file.") plt.close() return (0)
def plot_3d_graph(merge_df, key, modes, source, var_x, var_y): """ This function generates 3d colour plots against frequency and time for the given value for a given channel """ sep = get_source_separator(source) if modes['verbose'] >= 2: print("Generating a 3-d plot of " + gen_pretty_name(source) + " for " + key) if modes['colour'] in ["matching", "matching_dark"]: text_colour = colour_models(key) elif modes['colour'] == "dark": text_colour = "white" else: # light or None text_colour = "black" mpl.rcParams.update({ 'text.color': text_colour, 'axes.labelcolor': text_colour, 'xtick.color': text_colour, 'ytick.color': text_colour }) mpl.rc('axes', edgecolor=text_colour) fig, ax = plt.subplots() if modes['dpi'] is None: pass else: fig.set_dpi(modes['dpi']) if modes['image_size'] is None: pass # do nothing else: fig.set_size_inches(modes['image_size']) if modes['colour'] in ["dark", "matching_dark"]: ax.set_facecolor('black') fig.set_facecolor('black') graph_title = "\n".join([ modes['title'], ("Plot of the " + gen_pretty_name(source) + " for " + key + "-channel \nover " + gen_pretty_name(var_x) + " and " + gen_pretty_name(var_y) + ".") ]) plt.title(graph_title, wrap=True) var_z = (key + sep + source) # plots the channel in a colour based on its name colours = plt.get_cmap(colour_models(key + '_s')) if modes["three_d"] in ["colour", "color"]: try: x_vals = plottable(merge_df, var_x) y_vals = plottable(merge_df, var_y) if "percent" in modes["scale"]: z_vals = plottable(merge_df, var_z) * 100 else: z_vals = plottable(merge_df, var_z) if "log" in modes["scale"]: maxz = np.max(z_vals) minz = np.min(z_vals) # if the values go below zero, then plot with symmetric log, otherwise use log plotting if minz <= 0: log_lim = 10 linthresh = max([abs(maxz), abs(minz)]) / log_lim norm = SymLogNorm(linthresh, linscale=1.0, vmin=minz, vmax=maxz, clip=False) else: norm = LogNorm() p = plt.tripcolor(x_vals, y_vals, z_vals, cmap=colours, norm=norm) else: p = plt.tripcolor(x_vals, y_vals, z_vals, cmap=colours) except RuntimeError: if modes['verbose'] >= 1: print( "ERROR: Data not suitable for 3d colour plot. Possible alternatives: contour/animated plots" ) elif modes["three_d"] in ["contour"]: try: cols = np.unique(merge_df[var_y]).shape[0] x_vals = np.array(merge_df[var_x]).reshape(-1, cols) y_vals = np.array(merge_df[var_y]).reshape(-1, cols) if "percent" in modes["scale"]: z_vals = np.array(merge_df[var_z]).reshape(-1, cols) else: z_vals = np.array(merge_df[var_z]).reshape(-1, cols) * 100 plt.contour(x_vals, y_vals, z_vals, cmap=colours) except: if modes['verbose'] >= 1: print( "ERROR: Data not suitable for 3d contour plot. Possible alternatives: colour/animated plots" ) plt.legend(frameon=False) if var_x in ['d_Time']: # plots x-label using start time plt.xlabel(gen_pretty_name(var_x, units=True) + "\nStart Time: " + str(min(merge_df.Time)), wrap=True) else: plt.xlabel(gen_pretty_name(var_x, units=True), wrap=True) plt.ylabel(gen_pretty_name(var_y, units=True), wrap=True) if "percent" in modes["scale"]: plt.colorbar(format='%.3g%%') else: plt.colorbar() plt.tight_layout # prints or saves the plot if modes['out_dir'] is None: plt.show() else: plt_file = prep_out_file(modes, source=source, plot="vals", dims="nd", channel=key, out_type=modes['image_type']) if modes['verbose'] >= 2: print("plotting: " + plt_file) try: plt.savefig(plt_file, bbox_inches='tight', facecolor=fig.get_facecolor(), edgecolor='none') except ValueError: if modes['verbose'] >= 1: print("ERROR: Unable to save file, showing instead.") try: plt.show() except: print("Unable to show file.") plt.close()
def animated_plot(merge_df, modes, var_x, var_ys, var_t, sources, time_delay=20): """ Produces an animated linegraph(s) with the X, Y and T variables specified """ if modes['colour'] in ["matching", "matching_dark"] and len(var_ys) == 1: text_colour = colour_models(var_ys[0]) elif modes['colour'] in ["dark", "matching_dark"]: text_colour = "white" else: # light or None or matching and multiple y text_colour = "black" mpl.rcParams.update({ 'text.color': text_colour, 'axes.labelcolor': text_colour, 'xtick.color': text_colour, 'ytick.color': text_colour }) mpl.rc('axes', edgecolor=text_colour) fig, ax = plt.subplots() if modes['dpi'] is None: pass else: fig.set_dpi(modes['dpi']) if modes['image_size'] is None: pass # do nothing else: fig.set_size_inches(modes['image_size']) if modes['colour'] in ["dark", "matching_dark"]: ax.set_facecolor('black') fig.patch.set_facecolor('black') # hard coded for now, need to parameterise percentile_gap = 0 # 5 multiplier = 1 # 1.5 # sets default values for max_ and min_y max_y = np.nextafter(0, 1) # makes max and min values distinct min_y = 0 # Plot a scatter that persists (isn't redrawn) and the initial line. var_t_vals = np.sort(merge_df[var_t].unique()) var_t_val = var_t_vals[0] # str_channel = list_to_string(var_ys,", ") if var_t == "Time": var_t_string = str(var_t_val).rstrip('0').rstrip('.') elif var_t == "Freq": freq_MHz = var_t_val / 1e6 var_t_string = "{:7.3f} MHz".format(freq_MHz) else: var_t_string = ("%.4f" % var_t_val).rstrip('0').rstrip('.') title = "Plot of " for source in sources: title = add_key(title, sources, source) title = title + " for " for var_y in var_ys: title = add_key(title, var_ys, var_y) title = (title + "-channels over " + gen_pretty_name(var_x) + " at\n" + gen_pretty_name(var_t)) if modes['verbose'] >= 2: print("Generating an Animated " + title) title = "\n".join([modes["title"], title + " of " + var_t_string]) ax.set_title(title, wrap=True) var_x_vals = plottable( merge_df.loc[merge_df[var_t] == var_t_val].reset_index(drop=True), var_x) lines = [] for i in range(len(var_ys)): var_y = var_ys[i] for source in sources: sep = get_source_separator(source) var_y_vals = plottable( merge_df.loc[merge_df[var_t] == var_t_val].reset_index( drop=True), (var_y + sep + source)) var_y_vals_all = merge_df[var_y + sep + source] # sets the y axis scale to percentage if requested. if 'percent' in modes['scale']: var_y_vals = var_y_vals * 100 line, = ax.plot(var_x_vals, var_y_vals, color=colour_models(var_y + sep + source)) lines.append(line) # code to set x and y limits. local_min_y = np.percentile(var_y_vals_all, percentile_gap) * multiplier min_y = min(min_y, local_min_y) # min_y = 0#min(merge_df[(var_y+"_"+source)].min(),0) local_max_y = np.percentile(var_y_vals_all, 100 - percentile_gap) * multiplier max_y = max(max_y, local_max_y) # sets the y axis scale to logarithmic if requested. if 'log' in modes['scale']: ax.set_yscale('log') # sets the y axis scale to percentage if requested. if 'percent' in modes['scale']: ax.yaxis.set_major_formatter(mtick.PercentFormatter()) if min_y > 0 and 'linear' in modes['scale']: min_y = 0 ax.set_ylim(min_y, max_y) ax.set_xlabel(gen_pretty_name(var_x, units=True), wrap=True) ax.set_ylabel(channel_maker(var_ys, modes, ", ") + " flux\n(arbitrary units)", wrap=True) ax.legend(frameon=False) if modes['out_dir'] is None: repeat_option = True else: repeat_option = False # creates a global variable as animations only work with globals if "anim" not in globals(): global anim anim = [] else: pass anim.append( FuncAnimation(fig, update_a, frames=range(len(var_t_vals)), interval=time_delay, fargs=(merge_df, modes, var_x, var_ys, var_t, sources, lines, ax), repeat=repeat_option)) ax.set_aspect('auto') plt.subplots_adjust(top=0.80) # TODO: automate this so it's not fixed if modes['out_dir'] is not None: str_channel = channel_maker(var_ys, modes) str_sources = list_to_string(sources, "_") # str_channel = list_to_string(var_ys,", ") plot_name = var_x + "_over_" + var_t plt_file = prep_out_file(modes, source=str_sources, plot=plot_name, dims="nd", channel=str_channel, out_type=modes['image_type']) try: anim[len(anim) - 1].save(plt_file, dpi=80, writer='pillow', savefig_kwargs={ 'facecolor': fig.get_facecolor(), 'edgecolor': 'none' }) except ValueError: if modes['verbose'] >= 1: print("ERROR: Unable to save file, try showing instead.") try: plt.show() except: print("ERROR: Unable to show file.") # plt.close() # TODO: fix this so it works else: plt.show() # will just loop the animation forever.