def make_plot(df, column_name, output_dir, filename_suffix): # get datetime x-values xs = get_datetime_xs(df) # create a figure fig, ax = plt.subplots(figsize=(15, 4.5)) plt.plot(xs, df[column_name]) # label axes plt.xlabel('Time', fontsize=12) plt.ylabel(column_name, fontsize=12) # calculate Kendall taus and annotate plot tau, p = get_kendell_tau(df[column_name].dropna()) textstr = f'Kendall $\\tau,~p = {tau:.3f}$, ${p:.4f}$' ax.text(0.1, 0.95, textstr, transform=ax.transAxes, fontsize=14, verticalalignment='top') # save the plot output_filename = f'{column_name}' + filename_suffix + '.png' collection_prefix = column_name.split('_')[0] print( f'Plotting {collection_prefix} correlation moving window analysis...' ) plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI) plt.close(fig)
def make_plot(df, column, output_dir): """ Plot STL decomposition results. Parameters ---------- df : DataFrame The input time-series. column : str Column name to run STL on. output_dir : str Directory to save the plot in. """ # run fit res = stl_decomposition(df[column], period) # concert x values to datetime objects xs = get_datetime_xs(df) # formatting default_figsize = plt.rcParams['figure.figsize'] default_fontsize = plt.rcParams['font.size'] plt.rc('figure', figsize=(20, 8)) plt.rc('font', size=15) fig = res.plot() ax_list = fig.axes for ax in ax_list[:-1]: ax.tick_params(labelbottom=False) # set xlabel with datetime object #ax_list[-1].set_xticklabels(xs, rotation=0, va="center") # save plot filename = os.path.join(output_dir, column + '_STL_decomposition.png') plt.savefig(filename, dpi=DPI) plt.close(fig) # undo rc changes plt.rc('figure', figsize=default_figsize) plt.rc('font', size=default_fontsize)
def plot_ews_resiliance(series_name, EWSmetrics_df, Kendalltau_df, dates, output_dir): """ Make early warning signals resiliance plots using the output from the ewstools package. Parameters ---------- series_name : str String containing data collection and time series variable. EWSmetrics_df : DataFrame DataFrame from ewstools containing ews time series. Kendalltau_df : DataFrame DataFrame from ewstools containing Kendall tau values for EWSmetrics_df time series output_dir: str Output dir to save plot in. """ def zoom_out(ys): ymin = ys.mean() - 2 * ((ys.mean() - ys).abs().max()) ymax = ys.mean() + 2 * ((ys.mean() - ys).abs().max()) return [ymin, ymax] def annotate(text, xy=(6, 70), size=10): if 'Kendall' in text: xy = (xy[0], 60) plt.gca().annotate(text, xy=xy, xycoords='axes points', size=size, ha='left', va='top') dates = get_datetime_xs(pd.DataFrame(dates).dropna()) fig, _ = plt.subplots(figsize=(4, 8), sharex='col') ax1 = plt.subplot(611) ys = EWSmetrics_df['State variable'] plt.plot(dates[-len(ys):], ys, color='black') plt.plot(dates[-len(ys):], EWSmetrics_df['Smoothing'], color='red', linestyle='dashed') plt.ylim(zoom_out(ys)) annotate(series_name) ax2 = plt.subplot(612, sharex=ax1) ys = EWSmetrics_df['Residuals'] plt.plot(dates[-len(ys):], ys, color='black', label='Time Series') plt.ylim(zoom_out(ys)) annotate('Residuals') ax3 = plt.subplot(613, sharex=ax1) ys = EWSmetrics_df['Lag-1 AC'] plt.plot(dates[-len(ys):], ys, color='black') plt.ylim(zoom_out(ys)) annotate('Lag-1 AC') tau = Kendalltau_df['Lag-1 AC'].iloc[0] annotate(f'Kendall $\\tau = {tau:.2f}$', size=8) """ys = EWSmetrics_df['Lag-2 AC'] plt.plot(ys, color='navy') plt.ylim(zoom_out(ys)) annotate('Lag-1 AC', xy=(8, 65)) tau = Kendalltau_df['Lag-1 AC'].iloc[0] annotate(f'Kendall $\\tau = {tau:.2f}$', xy=(8, 55), size=8)""" ax4 = plt.subplot(614, sharex=ax1) ys = EWSmetrics_df['Standard deviation'] plt.plot(dates[-len(ys):], ys, color='black') plt.ylim(zoom_out(ys)) annotate('Standard deviation') tau = Kendalltau_df['Standard deviation'].iloc[0] annotate(f'Kendall $\\tau = {tau:.2f}$', size=8) ax5 = plt.subplot(615, sharex=ax1) ys = EWSmetrics_df['Skewness'] plt.plot(dates[-len(ys):], ys, color='black') plt.ylim(zoom_out(ys)) annotate('Skewness') tau = Kendalltau_df['Skewness'].iloc[0] annotate(f'Kendall $\\tau = {tau:.2f}$', size=8) ax6 = plt.subplot(616, sharex=ax1) ys = EWSmetrics_df['Kurtosis'] plt.plot(dates[-len(ys):], ys, color='black') plt.ylim(zoom_out(ys)) annotate('Kurtosis') tau = Kendalltau_df['Kurtosis'].iloc[0] annotate(f'Kendall $\\tau = {tau:.2f}$', size=8) plt.xlabel('Time') # remove vertical space between plots plt.subplots_adjust(hspace=0.0) # tick formatting for ax in [ax1, ax2, ax3, ax4, ax5]: ax.tick_params(axis='both', which='both', bottom=True, top=False, labelbottom=False, left=True, labelleft=True, direction='out', labelsize=8) # show x labels for bottom plot ax6.tick_params(axis='both', which='both', bottom=True, top=False, labelbottom=True, left=True, labelleft=True, direction='out', labelsize=8) # save the plot output_filename = series_name.replace(' ', '-') + '-ews.png' print(f'Plotting {series_name} ews plots...') plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI, bbox_inches='tight') plt.close(fig)
def make_plot(df, column, output_dir, smoothing_option): """ Parameters ---------- df : DataFrame The time-series results for variance and AR1. column : str Column name an offset50 variance column in df. output_dir : str Directory to save the plot in. smoothing_option: str Label for smoothing variable to be used """ # get short string prefix on column name collection_prefix = column.split( '_')[0] if 'offset50' in column else 'precipitation' # hand mismatched NaNs ar1_df = df.dropna(subset=[column.replace('var', 'ar1')]) var_df = df.dropna(subset=[column]) # extract x values and convert to datetime objects ar1_xs = get_datetime_xs(ar1_df) var_xs = get_datetime_xs(var_df) # extract individual time series variance = var_df[column] ar1 = ar1_df[column.replace('var', 'ar1')] ar1_se = ar1_df[column.replace('var', 'ar1_se')] if any([smoothing_option in c for c in df.columns]): variance_smooth = var_df[column.replace( 'offset50_mean', 'offset50_' + smoothing_option + '_mean')] ar1_smooth = ar1_df[column.replace('var', 'ar1').replace( 'offset50_mean', 'offset50_' + smoothing_option + '_mean')] ar1_se_smooth = ar1_df[column.replace('var', 'ar1_se').replace( 'offset50_mean', 'offset50_' + smoothing_option + '_mean')] # create a figure fig, ax = plt.subplots(figsize=(15, 5)) plt.xlabel('Time', fontsize=12) # set up veg y axis color = 'tab:blue' ax.set_ylabel(f'{collection_prefix} AR1', color=color, fontsize=12) ax.tick_params(axis='y', labelcolor=color) # plot unsmoothed vegetation ar1 and std ax.plot(ar1_xs, ar1, label='AR1', linewidth=2, color='tab:blue') ax.fill_between(ar1_xs, ar1 - ar1_se, ar1 + ar1_se, facecolor='blue', alpha=0.1, label='AR1 SE') if any([smoothing_option in c for c in df.columns]): # plot smoothed vegetation ar1 and std ax.plot(ar1_xs, ar1_smooth, label='AR1 Smoothed', linewidth=2, color='tab:blue', linestyle='dotted') ax.fill_between(ar1_xs, ar1_smooth - ar1_se_smooth, ar1_smooth + ar1_se_smooth, facecolor='none', alpha=0.15, label='AR1 SE Smoothed', hatch='X', edgecolor='tab:blue') # set y lim try: # in case there are no ar1 values, the array will be empty ax.set_ylim([ min(ar1 - ar1_se) - 0.8 * max(ar1 + ar1_se), 1.8 * max(ar1 + ar1_se) ]) except: return # plot legend plt.legend(loc='upper left') # duplicate x-axis for variance ax2 = ax.twinx() color = 'tab:red' ax2.set_ylabel(f'{collection_prefix} Variance', color=color, fontsize=12) ax2.tick_params(axis='y', labelcolor=color) # plot variance ax2.plot(var_xs, variance, linewidth=2, color=color, alpha=0.75, label='Variance') if any([smoothing_option in c for c in df.columns]): ax2.plot(var_xs, variance_smooth, linewidth=2, color=color, alpha=0.75, linestyle='dotted', label='Variance Smoothed') # set y lim ax2.set_ylim([0, 2 * max(variance)]) # add legend plt.legend(loc='lower left') # add Kendall tau tau, p = get_kendell_tau(ar1) tau_var, p_var = get_kendell_tau(variance) if any([smoothing_option in c for c in df.columns]): tau_smooth, p_smooth = get_kendell_tau(ar1_smooth) tau_var_smooth, p_var_smooth = get_kendell_tau(variance_smooth) # add to plot textstr = '' if any([smoothing_option in c for c in df.columns]): textstr += f'AR1 Kendall $\\tau,~p$-$\\mathrm{{value}}={tau_smooth:.2f}$, ${p_smooth:.2f}$' textstr += f' (${tau:.2f}$, ${p:.2f}$ unsmoothed)' ax2.text(0.43, 0.95, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') textstr = '' if any([smoothing_option in c for c in df.columns]): textstr += f'Variance Kendall $\\tau,~p$-$\\mathrm{{value}}={tau_var_smooth:.2f}$, ${p_var_smooth:.2f}$' textstr += f' (${tau_var:.2f}$, ${p_var:.2f}$ unsmoothed)' ax2.text(0.43, 0.85, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # layout fig.tight_layout() # save the plot output_filename = collection_prefix + '-AR1-var-' + smoothing_option + '.png' print(f'Plotting {collection_prefix} moving window time series...') plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI) plt.close(fig)
def make_plot(df, veg_prefix, output_dir, veg_prefix_b=None, smoothing_option='smooth'): # handle the case where vegetation and precipitation have mismatched NaNs veg_df = df.dropna(subset=[veg_prefix + '_offset50_mean']) # get vegetation x values to datetime objects veg_xs = get_datetime_xs(veg_df) # get vegetation y values veg_means = veg_df[veg_prefix + '_offset50_mean'] veg_std = veg_df[veg_prefix + '_offset50_std'] # create a figure fig, ax = plt.subplots(figsize=(15, 4.5)) plt.xlabel('Time', fontsize=14) # set up veg y axis color = 'tab:green' ax.set_ylabel(f'{veg_prefix} Offset50', color=color, fontsize=14) ax.tick_params(axis='y', labelcolor=color) ax.set_ylim([ veg_means.min() - 1 * veg_std.max(), veg_means.max() + 3 * veg_std.max() ]) # plot unsmoothed vegetation means ax.plot(veg_xs, veg_means, label='Unsmoothed', linewidth=1, color='dimgray', linestyle='dotted') # add smoothed time series if availible if any([ smoothing_option in c and veg_prefix in c for c in veg_df.columns ]): # get smoothed mean, std veg_means_smooth = veg_df[veg_prefix + '_offset50_' + smoothing_option + '_mean'] veg_stds_smooth = veg_df[veg_prefix + '_offset50_' + smoothing_option + '_std'] # plot smoothed vegetation means and std ax.plot(veg_xs, veg_means_smooth, marker='o', markersize=7, markeredgecolor=(0.9172, 0.9627, 0.9172), markeredgewidth=2, label='Smoothed', linewidth=2, color='green') ax.fill_between(veg_xs, veg_means_smooth - veg_stds_smooth, veg_means_smooth + veg_stds_smooth, facecolor='green', alpha=0.1, label='Std Dev') # plot vegetation legend plt.legend(loc='upper left') # plot precipitation if availible if 'total_precipitation' in df.columns: # handle the case where vegetation and precipitation have mismatched NaNs precip_df = df.dropna(subset=['total_precipitation']) precip_ys = precip_df.total_precipitation # get precipitation x values to datetime objects precip_xs = get_datetime_xs(precip_df) # duplicate axis for preciptation ax2 = ax.twinx() color = 'tab:blue' ax2.set_ylabel(f'Precipitation [mm]', color=color, fontsize=14) ax2.tick_params(axis='y', labelcolor=color) ax2.set_ylim([ min(precip_ys) - 1 * np.array(precip_ys).std(), max(precip_ys) + 2 * np.array(precip_ys).std() ]) # plot precipitation ax2.plot(precip_xs, precip_ys, linewidth=2, color=color, alpha=0.75) # add veg-precip correlation max_corr_smooth, max_corr = get_max_lagged_cor( os.path.dirname(output_dir), veg_prefix) textstr = f'$r_{{t-{max_corr_smooth[1]}}}={max_corr_smooth[0]:.2f}$ ' textstr += f'($r_{{t-{max_corr[1]}}}={max_corr[0]:.2f}$ unsmoothed)' # old correlation just calculates the 0-lag correlation #raw_corr = veg_means.corr(precip_ys) #smoothed_corr = veg_means_smooth.corr(precip_ys) #textstr = f'$r={smoothed_corr:.2f}$ (${raw_corr:.2f}$ unsmoothed)' ax2.text(0.13, 0.95, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # plot second vegetation time series if availible if veg_prefix_b: # handle the case where vegetation and precipitation have mismatched NaNs veg_df_b = df.dropna(subset=[veg_prefix_b + '_offset50_mean']) # get vegetation x values to datetime objects veg_xs_b = get_datetime_xs(veg_df_b) # get vegetation y values veg_means_b = veg_df_b[veg_prefix_b + '_offset50_mean'] #veg_std_b = veg_df[veg_prefix_b+'_offset50_std'] veg_means_smooth_b = veg_df_b[veg_prefix_b + '_offset50_smooth_mean'] veg_stds_smooth_b = veg_df_b[veg_prefix_b + '_offset50_smooth_std'] # plot secondary time series ax3 = ax.twinx() ax3.spines["left"].set_position(("axes", -0.08)) ax3.spines["left"].set_visible(True) color = 'tab:purple' ax3.set_ylabel(veg_prefix_b + ' Offset50', color=color, fontsize=14) ax3.tick_params(axis='y', labelcolor=color) ax3.yaxis.tick_left() ax3.yaxis.set_label_position('left') ax3.set_ylim([ veg_means.min() - 1 * veg_std.max(), veg_means.max() + 3 * veg_std.max() ]) # plot unsmoothed vegetation means ax.plot(veg_xs_b, veg_means_b, label='Unsmoothed', linewidth=1, color='indigo', linestyle='dashed', alpha=0.2) # plot smoothed vegetation means and std ax3.plot(veg_xs_b, veg_means_smooth_b, marker='o', markersize=7, markeredgecolor=(0.8172, 0.7627, 0.9172), markeredgewidth=2, label='Smoothed', linewidth=2, color=color) ax3.fill_between(veg_xs_b, veg_means_smooth_b - veg_stds_smooth_b, veg_means_smooth_b + veg_stds_smooth_b, facecolor='tab:purple', alpha=0.1, label='Std Dev') # add veg-veg correlation vegveg_corr = veg_means.corr(veg_means_b) vegveg_corr_smooth = veg_means_smooth.corr(veg_means_smooth_b) textstr = f'$r_{{vv}}={vegveg_corr_smooth:.2f}$ (${vegveg_corr:.2f}$ unsmoothed)' ax2.text(0.55, 0.85, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # update prefix for filename use veg_prefix = veg_prefix + '+' + veg_prefix_b # add autoregression info veg_means.index = veg_df.date unsmoothed_ar1, unsmoothed_ar1_se = get_AR1_parameter_estimate( veg_means) if any(['smooth' in c and veg_prefix in c for c in veg_df.columns]): veg_means_smooth.index = veg_df.date smoothed_ar1, smoothed_ar1_se = get_AR1_parameter_estimate( veg_means_smooth) else: smoothed_ar1, smoothed_ar1_se = np.NaN, np.NaN ar1_dict = {} ar1_dict['AR1'] = { 'unsmoothed': { 'param': unsmoothed_ar1, 'se': unsmoothed_ar1_se }, 'smoothed': { 'param': smoothed_ar1, 'se': smoothed_ar1_se } } write_to_json(os.path.join(output_dir, veg_prefix + '_stats.json'), ar1_dict) textstr = f'AR$(1)={smoothed_ar1:.2f} \pm {smoothed_ar1_se:.2f}$ (${unsmoothed_ar1:.2f} \pm {unsmoothed_ar1_se:.2f}$ unsmoothed)' ax.text(0.55, 0.95, textstr, transform=ax.transAxes, fontsize=14, verticalalignment='top') # add Kendall tau tau, p = get_kendell_tau(veg_means) if any(['smooth' in c and veg_prefix in c for c in veg_df.columns]): tau_smooth, p_smooth = get_kendell_tau(veg_means_smooth) else: tau_smooth, p_smooth = np.NaN, np.NaN kendall_tau_dict = {} kendall_tau_dict['Kendall_tau'] = { 'unsmoothed': { 'tau': tau, 'p': p }, 'smoothed': { 'tau': tau_smooth, 'p': p_smooth } } write_to_json(os.path.join(output_dir, veg_prefix + '_stats.json'), kendall_tau_dict) textstr = f'$\\tau,~p$-$\\mathrm{{value}}={tau_smooth:.2f}$, ${p:.2f}$ (${tau:.2f}$, ${p_smooth:.2f}$ unsmoothed)' ax.text(0.13, 0.85, textstr, transform=ax.transAxes, fontsize=14, verticalalignment='top') # layout sns.set_style('white') fig.tight_layout() filename_suffix = '_' + smoothing_option # save the plot output_filename = veg_prefix + '-time-series' + filename_suffix + '.png' plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI) plt.close(fig)
def make_plot(df, veg_prefix, col_name, utput_dir): veg_df = df.dropna(subset=[col_name]) # get vegetation x values to datetime objects veg_xs = get_datetime_xs(veg_df) # get vegetation y values veg_means = veg_df[col_name] #veg_means = veg_df[veg_prefix + '_ndvi_veg_mean'] if any([col_name.replace('mean', 'std') == c for c in df.columns]): veg_std = veg_df[col_name.replace('mean', 'std')] # create a figure fig, ax = plt.subplots(figsize=(15, 4.5)) plt.xlabel('Time', fontsize=14) # set up veg y axis color = 'tab:green' ax.set_ylabel(f'{veg_prefix} NDVI', color=color, fontsize=14) ax.tick_params(axis='y', labelcolor=color) if any([col_name.replace('mean', 'std') == c for c in df.columns]): ax.set_ylim([ veg_means.min() - 1 * veg_std.max(), veg_means.max() + 3 * veg_std.max() ]) # plot ndvi #ax.plot(veg_xs, ndvi_means, label='Unsmoothed', linewidth=1, color='dimgray', linestyle='dotted') ax.plot(veg_xs, veg_means, marker='o', markersize=7, markeredgecolor=(0.9172, 0.9627, 0.9172), markeredgewidth=2, label='Smoothed', linewidth=2, color='green') if any([col_name.replace('mean', 'std') == c for c in df.columns]): ax.fill_between(veg_xs, veg_means - veg_std, veg_means + veg_std, facecolor='green', alpha=0.1, label='Std Dev') # plot precipitation if availible if 'total_precipitation' in df.columns: # handle the case where vegetation and precipitation have mismatched NaNs precip_df = df.dropna(subset=['total_precipitation']) precip_ys = precip_df.total_precipitation # get precipitation x values to datetime objects precip_xs = get_datetime_xs(precip_df) # duplicate axis for preciptation ax2 = ax.twinx() color = 'tab:blue' ax2.set_ylabel(f'Precipitation [mm]', color=color, fontsize=14) ax2.tick_params(axis='y', labelcolor=color) ax2.set_ylim([ min(precip_ys) - 1 * np.array(precip_ys).std(), max(precip_ys) + 2 * np.array(precip_ys).std() ]) # plot precipitation ax2.plot(precip_xs, precip_ys, linewidth=2, color=color, alpha=0.75) # add correlation information correlations = get_corrs_by_lag(df[col_name], df['total_precipitation']) max_corr = np.max(np.array(correlations)) max_corr_lag = np.array(np.argmax(correlations)) textstr = f'$r_{{t-{max_corr_lag}}}={max_corr:.2f}$ ' ax2.text(0.13, 0.95, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # layout sns.set_style('white') fig.tight_layout() # save the plot output_filename = veg_prefix + '-ndvi-time-series.png' plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI) plt.close(fig)
def make_plot(df, veg_prefix, output_dir, veg_prefix_b=None, smoothing_option='smooth'): # handle the case where vegetation and precipitation have mismatched NaNs veg_df = df.dropna(subset=[veg_prefix+'_offset50_mean']) # get vegetation x values to datetime objects veg_xs = get_datetime_xs(veg_df) # get vegetation y values veg_means = veg_df[veg_prefix + '_offset50_mean'] veg_std = veg_df[veg_prefix + '_offset50_std'] # create a figure fig, ax = plt.subplots(figsize=(15, 4.5)) plt.xlabel('Time', fontsize=14) # set up veg y axis color = 'tab:green' ax.set_ylabel(f'{veg_prefix} Offset50', color=color, fontsize=14) ax.tick_params(axis='y', labelcolor=color) ax.set_ylim([veg_means.min() - 1*veg_std.max(), veg_means.max() + 3*veg_std.max()]) # plot smoothed vegetation means and std ax.plot(veg_xs, veg_means, marker='o', markersize=7, markeredgecolor=(0.9172, 0.9627, 0.9172), markeredgewidth=2, label='Offset50', linewidth=2, color='green') ax.fill_between(veg_xs, veg_means - veg_std, veg_means + veg_std, facecolor='green', alpha=0.1, label='Std Dev') # # get smoothed mean, std veg_means_smooth = veg_df[veg_prefix+'_offset50_'+smoothing_option+'_mean'] # plot vegetation legend plt.legend(loc='upper left') # plot precipitation if availible if 'total_precipitation' in df.columns: # handle the case where vegetation and precipitation have mismatched NaNs precip_df = df.dropna(subset=['total_precipitation']) precip_ys = precip_df.total_precipitation # get precipitation x values to datetime objects precip_xs = get_datetime_xs(precip_df) # duplicate axis for preciptation ax2 = ax.twinx() color = 'tab:blue' ax2.set_ylabel(f'Precipitation [mm]', color=color, fontsize=14) ax2.tick_params(axis='y', labelcolor=color) ax2.set_ylim([min(precip_ys)-1*np.array(precip_ys).std(), max(precip_ys)+2*np.array(precip_ys).std()]) # plot precipitation ax2.plot(precip_xs, precip_ys, linewidth=2, color=color, alpha=0.75) # add veg-precip correlation max_corr_smooth, max_corr = get_max_lagged_cor(os.path.dirname(output_dir), veg_prefix) textstr = f'$r_{{t-{max_corr[1]}}}={max_corr[0]:.2f}$' # old correlation just calculates the 0-lag correlation #raw_corr = veg_means.corr(precip_ys) #smoothed_corr = veg_means_smooth.corr(precip_ys) #textstr = f'$r={smoothed_corr:.2f}$ (${raw_corr:.2f}$ unsmoothed)' ax2.text(0.13, 0.95, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # plot second vegetation time series if availible if veg_prefix_b: # handle the case where vegetation and precipitation have mismatched NaNs veg_df_b = df.dropna(subset=[veg_prefix_b+'_offset50_mean']) # get vegetation x values to datetime objects veg_xs_b = get_datetime_xs(veg_df_b) # get vegetation y values veg_means_b = veg_df_b[veg_prefix_b+'_offset50_mean'] veg_means_smooth_b = veg_df_b[veg_prefix_b+'_offset50_smooth_mean'] veg_stds_smooth_b = veg_df_b[veg_prefix_b+'_offset50_smooth_std'] # plot secondary time series ax3 = ax.twinx() ax3.spines["left"].set_position(("axes", -0.08)) ax3.spines["left"].set_visible(True) color = 'tab:purple' ax3.set_ylabel(veg_prefix_b + ' Offset50', color=color, fontsize=14) ax3.tick_params(axis='y', labelcolor=color) ax3.yaxis.tick_left() ax3.yaxis.set_label_position('left') ax3.set_ylim([veg_means.min() - 1*veg_std.max(), veg_means.max() + 3*veg_std.max()]) # plot unsmoothed vegetation means ax.plot(veg_xs_b, veg_means_b, label='Unsmoothed', linewidth=1, color='indigo', linestyle='dashed', alpha=0.2) # plot smoothed vegetation means and std ax3.plot(veg_xs_b, veg_means_smooth_b, marker='o', markersize=7, markeredgecolor=(0.8172, 0.7627, 0.9172), markeredgewidth=2, label='Smoothed', linewidth=2, color=color) ax3.fill_between(veg_xs_b, veg_means_smooth_b - veg_stds_smooth_b, veg_means_smooth_b + veg_stds_smooth_b, facecolor='tab:purple', alpha=0.1, label='Std Dev') # add veg-veg correlation vegveg_corr = veg_means.corr(veg_means_b) vegveg_corr_smooth = veg_means_smooth.corr(veg_means_smooth_b) textstr = f'$r_{{vv}} = {vegveg_corr:.2f}$' ax2.text(0.55, 0.85, textstr, transform=ax2.transAxes, fontsize=14, verticalalignment='top') # update prefix for filename use veg_prefix = veg_prefix + '+' + veg_prefix_b # add autoregression info veg_means.index = veg_df.date # layout sns.set_style('white') fig.tight_layout() filename_suffix = '_' + smoothing_option # save the plot output_filename = veg_prefix + '-time-series' + filename_suffix + '.png' plt.savefig(os.path.join(output_dir, output_filename), dpi=DPI)