36 KiB
36 KiB
Data Analysis for CWFT Experiments Runfile¶
Atmospheric Chemistry Lab Course 2026 Edition
Contact nicole.leemann@psi.ch if you encounter any bugs.
Import¶
In [ ]:
import numpy as np import pandas as pd import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots from pathlib import Path from babel.dates import format_date from scipy.optimize import curve_fit import re import ast
In [ ]:
import ACLC26_data_analysis_module as dam
Initialize Experiment Class¶
In [ ]:
# Input from_summary = False # set to true to load parameters from a summary file instead of manual input savename = "20251202_FeCit_CA_Nitrate_1_10_10_RH80" # name that will be used for saving the processed data and data analysis summary of the experiment #savename = "20251204_FeCit_CA_Nitrate_1_10_10_RH50" #savename = "20251203_CA_Nitrate_1_1_RH80" savefolder = Path("C:/Users/leeman_n/OneDrive - Paul Scherrer Institut/Documents/Teaching/2026_AtmosChemLabCourse/ACLC26_DataAnalysis/ACLC26_Output") / savename # name of the folder to save the outputs and summary of the experiment color_dict = { # colors that will be used for plotting "T360U_CO2": ["#807F80"], # CO2 "T200_NOx": ["#F08080", "#84B8E9", "#C5A1F5"], # NO, NO2, NOx "Ecophysics_NOx_899": ["#CC2626","#0B60AF", "#5533B4"], # NO, NO2, NOx "ICAD_HONO": ["#db2fa8", "#7ddfc9"], # HONO, NO2 "ICAD_NO2": ["#6cdf72"] # NO2 } if from_summary: FeCit_N = dam.init_from_summary(savefolder, savename, color_dict) # initialize from summary else: # manual input exp_name = "FeCit/CA/Nitrate 1:10:10 RH 80% (2.12.2026)" # for plot title (or comparisons) #instrument_list = ["T360U_CO2", "T200_NOx", "Ecophysics_NOx_899", "ICAD_HONO", "ICAD_NO2", "Humidity_Sensors"] instrument_list = ["T360U_CO2", "Ecophysics_NOx_899", "ICAD_HONO", "ICAD_NO2", "Humidity_Sensors"] # list of all instruments used setup_dict = { "p_room": 979, # ambient pressure [mbar] "T_room": 23, # room temperature [°C] "T_tube": 18, # CWFT temperature [°C] "T_hum": 15, # humidifier temperature [°C] "flow_N2": 1300, # MFC set flow for N2 "flow_O2": 300, # MFC set flow for O2 "dilution": 3000, # MFC set flow for N2 dilution "dil_BTOF": 1000, # MFC set flow for BTOF dilution "tube_length": 32.6, # tube length [cm] "tube_diam": 1.64, # tube inner diameter [cm] "vol_coat": 1000 # volume of coating solution [muL] } exp_start = "2025-12-02 09:00:00" # YYYY-MM-DD hh:mm:ss (for locating the correct data) time_tube_in = "2025-12-02 14:58:19" # switch from bypass to tube flow time_lights_on = "2025-12-02 15:18:25" # irradiation period time_lights_off = "2025-12-02 15:53:37" time_lights_on_2 = None # YYYY-MM-DD hh:mm:ss or None # second irradiation period (optional) time_lights_off_2 = None time_tube_out = "2025-12-02 16:09:08" # switch from tube to bypass flow exp_end = "2025-12-02 18:00:00" # for start and end: choose big enough interval that also includes the timeshifted data FeCit_N = dam.experiment(exp_name, instrument_list, setup_dict, exp_start, exp_end, time_tube_in, time_lights_on, time_lights_off, time_tube_out, color_dict, savefolder, time_lights_on_2, time_lights_off_2) # initialize
In [ ]:
# set up folders for saving processed data and plots savefolder.mkdir(parents=True, exist_ok=True) # create folder if it doesn't exist already (parent folder) savefolder_data = Path(savefolder) / "Processed_Data" # csv files of processed data for each instrument savefolder_data.mkdir(parents=True, exist_ok=True) savefolder_plots = Path(savefolder) / "Plots" # all saved plots savefolder_plots.mkdir(parents=True, exist_ok=True)
Load raw data¶
In [ ]:
if "T360U_CO2" in FeCit_N.instruments: FeCit_N.get_rawdata_T360U_CO2() display(FeCit_N.rawdata_co2.head()) print("Size: ", FeCit_N.rawdata_co2.shape)
In [ ]:
# if "T200_NOx" in FeCit_N.instruments: # FeCit_N.get_rawdata_T200_NOx() # display(FeCit_N.rawdata_nox.head()) # print("Size: ", FeCit_N.rawdata_nox.shape)
In [ ]:
if "Humidity_Sensors" in FeCit_N.instruments: FeCit_N.get_rawdata_RH() display(FeCit_N.rawdata_rh.head()) print("Size: ", FeCit_N.rawdata_rh.shape)
In [ ]:
if "ICAD_HONO" in FeCit_N.instruments: FeCit_N.get_rawdata_ICAD_HONO() display(FeCit_N.rawdata_icad_hono.head()) print("Size: ", FeCit_N.rawdata_icad_hono.shape)
In [ ]:
if "ICAD_NO2" in FeCit_N.instruments: FeCit_N.get_rawdata_ICAD_NO2() display(FeCit_N.rawdata_icad_no2.head()) print("Size: ", FeCit_N.rawdata_icad_no2.shape)
In [ ]:
if "Ecophysics_NOx_899" in FeCit_N.instruments: FeCit_N.get_rawdata_NOx_eco() display(FeCit_N.rawdata_nox_eco.head()) print("Size: ", FeCit_N.rawdata_nox_eco.shape)
Preview Plot¶
For checking and adjusting alignment between instruments
In [ ]:
# copy the output to the cell below for easier adjustment ;) # set pd.Timestamp("None") to None! print(f"FeCit_N.time_tube_in = pd.Timestamp(\"{FeCit_N.time_tube_in}\")") print(f"FeCit_N.time_lights_on = pd.Timestamp(\"{FeCit_N.time_lights_on}\")") print(f"FeCit_N.time_lights_off = pd.Timestamp(\"{FeCit_N.time_lights_off}\")") print(f"FeCit_N.time_lights_on_2 = pd.Timestamp(\"{FeCit_N.time_lights_on_2}\")") print(f"FeCit_N.time_lights_off_2 = pd.Timestamp(\"{FeCit_N.time_lights_off_2}\")") print(f"FeCit_N.time_tube_out = pd.Timestamp(\"{FeCit_N.time_tube_out}\")")
In [ ]:
# from_summary = False # uncomment this to make adjustments to the parameters from the summary if from_summary==False: # only overwrite if not loading from summary # set timeshift of instruments (align times to CO2 analyzer) FeCit_N.timeshift_nox = pd.Timedelta(minutes=0, seconds=0) FeCit_N.timeshift_nox_eco = pd.Timedelta(minutes=56, seconds=34) FeCit_N.timeshift_icad_hono = pd.Timedelta(minutes=61, seconds=50) FeCit_N.timeshift_icad_no2 = pd.Timedelta(minutes=60, seconds=0) FeCit_N.timeshift_rh = pd.Timedelta(minutes=0, seconds=0) # for demo # FeCit_N.timeshift_nox_eco = pd.Timedelta(minutes=60, seconds=0) # FeCit_N.timeshift_icad_hono = pd.Timedelta(minutes=60, seconds=0) # FeCit_N.timeshift_icad_no2 = pd.Timedelta(minutes=60, seconds=0) # set irradiation time periods & tube in / out times FeCit_N.time_tube_in = pd.Timestamp("2025-12-02 14:58:19") FeCit_N.time_lights_on = pd.Timestamp("2025-12-02 15:18:25") FeCit_N.time_lights_off = pd.Timestamp("2025-12-02 15:53:37") FeCit_N.time_lights_on_2 = None FeCit_N.time_lights_off_2 = None FeCit_N.time_tube_out = pd.Timestamp("2025-12-02 16:09:08") # FeCit_N.time_tube_in = pd.Timestamp("2025-12-04 11:47:25") # FeCit_N.time_lights_on = pd.Timestamp("2025-12-04 12:00:27") # FeCit_N.time_lights_off = pd.Timestamp("2025-12-04 12:35:41") # FeCit_N.time_lights_on_2 = None # FeCit_N.time_lights_off_2 = None # FeCit_N.time_tube_out = pd.Timestamp("2025-12-04 13:35:26")
In [ ]:
preview = FeCit_N.preview_plot(FeCit_N.instruments) # clean up data and make preview plot #preview.update_xaxes(range=(pd.Timestamp("2025-12-04 10:30:00"), pd.Timestamp("2025-12-04 14:30:00"))) # set x-axis range manually! preview.update_yaxes(range=(0,5), secondary_y=False) # y-axis range (left) preview.show() #preview.write_html(f"{savefolder_plots}/{savename}_Preview_Plot.html")
In [ ]:
if "Humidity_Sensors" in FeCit_N.instruments: rhtdp_plot = FeCit_N.RHTDP_plot() #rhtdp_plot.update_xaxes(range=(pd.Timestamp("2025-11-07 13:00:00"), pd.Timestamp("2025-11-07 17:00:00"))) #rhtdp_plot.update_yaxes(title_text="RH [%]", range=[10, 35], row=1, col=1) #rhtdp_plot.show() # uncomment to show the plot
Background Fitting¶
In [ ]:
# add column with numeric timeaxis to all datasets if "T360U_CO2" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_co2, ts=pd.Timedelta(minutes=0, seconds=0)) if "T200_NOx" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_nox, ts=FeCit_N.timeshift_nox) if "Ecophysics_NOx_899" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_nox_eco, ts=FeCit_N.timeshift_nox_eco) if "ICAD_HONO" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_icad_hono, ts=FeCit_N.timeshift_icad_hono, ts_sign="+") if "ICAD_NO2" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_icad_no2, ts=FeCit_N.timeshift_icad_no2, ts_sign="+") if "Humidity_Sensors" in FeCit_N.instruments: FeCit_N.get_num_timeaxis(FeCit_N.predata_rh, ts=FeCit_N.timeshift_rh) FeCit_N.predata_icad_hono.head() # preview FeCit_N.predata_co2.head()
In [ ]:
# CO2 if "T360U_CO2" in FeCit_N.instruments: df_data = FeCit_N.predata_co2.copy() if from_summary==False: FeCit_N.bkgint_co2 = ['2025-12-02 12:15:00', '2025-12-02 13:30:00'] # manual input interval for bkg fit pred_start, pred_end = pd.Timestamp(FeCit_N.bkgint_co2[0]), pd.Timestamp(FeCit_N.bkgint_co2[1]) # if load from summary # fit the background with linear function and return plot & fit parameters bkgfit_plot, popt, pcov, residuals = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw CO2 [ppm]", mode="lin") bkgfit_plot.update_layout(height=500, width=1500) # figure size bkgfit_plot.update_yaxes(range=(2,4)) #FeCit_N.add_timemarkers(bkgfit_plot) # add yellow rectangle to mark irradiation time bkgfit_plot.show() #bkgfit_plot.write_html(f"{savefolder_plots}/{savename}_Background_T360U_CO2.html") # save interactive plot bkgfit_plot.write_image(f"{savefolder_plots}/{savename}_Background_T360U_CO2.png") # save static plot FeCit_N.bkgcorr_co2 = [popt] # save the parameters of the background fit print(popt) # residuals_plot = px.scatter(x=df_data["Time since Irradiation [min]"],y=residuals, title="residuals") # residuals_plot.update_layout(height=500, width=1500) # residuals_plot.show() # y_true = df_data["raw CO2 [ppm]"].to_numpy() # ss_res = (residuals**2).sum() # ss_tot = ((y_true - y_true.mean())**2).sum() # r2 = 1 - ss_res / ss_tot # print(r2)
In [ ]:
# NOx eco if "Ecophysics_NOx_899" in FeCit_N.instruments: df_data = FeCit_N.predata_nox_eco.copy() if from_summary==False: FeCit_N.bkgint_nox_eco = ['2025-12-02 13:00:00', '2025-12-02 15:15:00'] # manual input #FeCit_N.bkgint_nox_eco = ['2025-12-04 10:40:00', '2025-12-04 12:00:00'] pred_start, pred_end = pd.Timestamp(FeCit_N.bkgint_nox_eco[0]), pd.Timestamp(FeCit_N.bkgint_nox_eco[1]) # if load from summary # individual plots bkgfit_plot_poly, popt_poly, pcov_poly, resi_poly = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="poly") #bkgfit_plot_poly.show() #bkgfit_plot_logdec, popt_logdec, pcov_logdec, resi_logdec = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="log_decay") #bkgfit_plot_logdec.show() bkgfit_plot1, popt1, pcov1, resi1 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO [ppb]", mode="lin") #bkgfit_plot1.show() bkgfit_plot2, popt2, pcov2, resi2 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="lin") #bkgfit_plot2.show() bkgfit_plot3, popt3, pcov3, resi3 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NOx [ppb]", mode="lin") #bkgfit_plot3.show() # combined plot fig = make_subplots( rows=2, cols=2, subplot_titles=["NO", "NO2 lin", "NOx", "NO2 log decay"], horizontal_spacing = 0.03, vertical_spacing = 0.1 ) for trace in bkgfit_plot1.data: fig.add_trace(trace, row=1, col=1) for trace in bkgfit_plot2.data: fig.add_trace(trace, row=1, col=2) for trace in bkgfit_plot3.data: fig.add_trace(trace, row=2, col=1) #for trace in bkgfit_plot_logdec.data: # fig.add_trace(trace, row=2, col=2) fig.update_layout( showlegend=True, title_text="Ecophysics NOx 899" ) fig.update_layout(height=750, width=1500) #FeCit_N.add_timemarkers(fig) fig.show() #fig.write_html(f"{savefolder_plots}/{savename}_Background_Ecophysics_NOx_899.html") fig.write_image(f"{savefolder_plots}/{savename}_Background_Ecophysics_NOx_899.png") # save static plot FeCit_N.bkgcorr_nox_eco = [popt1, popt2, popt3] # save fit parameters print(FeCit_N.bkgcorr_nox_eco)
In [ ]:
# NOx if "T200_NOx" in FeCit_N.instruments: df_data = FeCit_N.predata_nox.copy() if from_summary==False: FeCit_N.bkgint_nox = ['2025-12-02 13:00:00', '2025-12-02 15:15:00'] # manual input pred_start, pred_end = pd.Timestamp(FeCit_N.bkgint_nox[0]), pd.Timestamp(FeCit_N.bkgint_nox[1]) # if load from summary bkgfit_plot1, popt1, pcov1, resi1 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO [ppb]", mode="lin") #bkgfit_plot1.show() bkgfit_plot2, popt2, pcov2, resi2 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="lin") #bkgfit_plot2.show() bkgfit_plot3, popt3, pcov3, resi3 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NOx [ppb]", mode="lin") #bkgfit_plot3.show() fig = make_subplots( rows=2, cols=2, subplot_titles=["NO", "NO2", "NOx", " "], horizontal_spacing = 0.03, vertical_spacing = 0.1 ) for trace in bkgfit_plot1.data: fig.add_trace(trace, row=1, col=1) for trace in bkgfit_plot2.data: fig.add_trace(trace, row=1, col=2) for trace in bkgfit_plot3.data: fig.add_trace(trace, row=2, col=1) fig.update_layout( #height=800, showlegend=True, title_text="T200 NOx" ) #FeCit_N.add_timemarkers(fig) fig.update_layout(height=750, width=1500) fig.show() #fig.write_html(f"{savefolder_plots}/{savename}_Background_T200_NOx.html") fig.write_image(f"{savefolder_plots}/{savename}_Background_T200_NOx.png") # save static plot FeCit_N.bkgcorr_nox = [popt1, popt2, popt3] # save fit parameters print(FeCit_N.bkgcorr_nox)
In [ ]:
# ICAD HONO if "ICAD_HONO" in FeCit_N.instruments: df_data = FeCit_N.predata_icad_hono.copy() if from_summary==False: FeCit_N.bkgint_icad_hono = ['2025-12-02 11:00:00', '2025-12-02 15:00:00'] # manual input pred_start, pred_end = pd.Timestamp(FeCit_N.bkgint_icad_hono[0]), pd.Timestamp(FeCit_N.bkgint_icad_hono[1]) # if load from summary bkgfit_plot1, popt1, pcov1, resi1 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw HONO [ppb]", mode="lin") #bkgfit_plot1.show() bkgfit_plot2, popt2, pcov2, resi2 = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="lin") #bkgfit_plot2.show() fig = make_subplots( rows=1, cols=2, subplot_titles=["HONO", "NO2"], horizontal_spacing = 0.03, vertical_spacing = 0.1 ) for trace in bkgfit_plot1.data: fig.add_trace(trace, row=1, col=1) for trace in bkgfit_plot2.data: fig.add_trace(trace, row=1, col=2) #FeCit_N.add_timemarkers(fig) fig.update_layout(height=450, width=1500, title_text="ICAD HONO") fig.show() #fig.write_html(f"{savefolder_plots}/{savename}_Background_ICAD_HONO.html") fig.write_image(f"{savefolder_plots}/{savename}_Background_ICAD_HONO.png") # save static plot FeCit_N.bkgcorr_icad_hono = [popt1, popt2] # save fit parameters print(FeCit_N.bkgcorr_icad_hono)
In [ ]:
# ICAD NO2 if "ICAD_NO2" in FeCit_N.instruments: df_data = FeCit_N.predata_icad_no2.copy() if from_summary==False: FeCit_N.bkgint_icad_no2 = ['2025-12-02 12:00:00', '2025-12-02 15:00:00'] # manual input pred_start, pred_end = pd.Timestamp(FeCit_N.bkgint_icad_no2[0]), pd.Timestamp(FeCit_N.bkgint_icad_no2[1]) # if load from summary bkgfit_plot, popt, pcov, residuals = FeCit_N.get_bkg_fit(df_data, pred_start, pred_end, param="raw NO2 [ppb]", mode="lin") bkgfit_plot.update_layout(height=500, width=1500) #FeCit_N.add_timemarkers(bkgfit_plot) bkgfit_plot.show() #bkgfit_plot.write_html(f"{savefolder_plots}/{savename}_Background_ICAD_NO2.html") bkgfit_plot.write_image(f"{savefolder_plots}/{savename}_Background_ICAD_NO2.png") # save static plot FeCit_N.bkgcorr_icad_no2 = [popt] # save fit parameters print(FeCit_N.bkgcorr_icad_no2)
Final Processing¶
Finish processing the instrument data (correct for timeshifts, background and dilution) and save the cleaned dataframes in a csv file (one per instrument)
In [ ]:
FeCit_N.process_data(savefolder, savename) FeCit_N.data_co2.head() # preview
Summary¶
In [ ]:
savefile = f"{savefolder}/{savename}_DA_Summary.txt" # save summary of data analysis in this file FeCit_N.get_summary(savefile)
Plots¶
One Instrument per Figure¶
In [ ]:
# CO2 if "T360U_CO2" in FeCit_N.instruments: #data = pd.read_csv(f"{savefolder}/Processed_Data/{savename}_Data_T360U_CO2.csv", sep=';') # get data from file data = FeCit_N.data_co2.copy() # get data from experiment fig_co2 = FeCit_N.plot_one_instrument(instr="T360U_CO2", data=data, plot_raw = False) fig_co2.update_xaxes(range=(-30, 60)) fig_co2.update_yaxes(range=(-2,35)) #fig_co2.update_yaxes(range=(-0.05,0.5)) fig_co2.update_layout(height=500, width=1500) fig_co2.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_co2.show() #fig_co2.write_html(f"{savefolder_plots}/{savename}_Plot_T360U_CO2.html") fig_co2.write_image(f"{savefolder_plots}/{savename}_Plot_T360U_CO2.png") # save static plot
In [ ]:
# NOx if "T200_NOx" in FeCit_N.instruments: #data = pd.read_csv(f"{savefolder}/Processed_Data/{savename}_Data_T200_NOx.csv", sep=';') # get data from file data = FeCit_N.data_nox.copy() # get data from experiment fig_nox = FeCit_N.plot_one_instrument(instr="T200_NOx", data=data, plot_raw = False) fig_nox.update_xaxes(range=(-30, 60)) #fig_nox.update_yaxes(range=(-10,40)) fig_nox.update_layout(height=500, width=1500) fig_nox.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_nox.show() #fig_nox.write_html(f"{savefolder_plots}/{savename}_Plot_T200_NOx.html") fig_nox.write_image(f"{savefolder_plots}/{savename}_Plot_T200_NOx.png") # save static plot
In [ ]:
# NOx eco if "Ecophysics_NOx_899" in FeCit_N.instruments: #data = pd.read_csv(f"{savefolder}/Processed_Data/{savename}_Data_Ecophysics_NOx_899.csv", sep=';') # get data from file data = FeCit_N.data_nox_eco.copy() # get data from experiment fig_nox_eco = FeCit_N.plot_one_instrument(instr="Ecophysics_NOx_899", data=data, plot_raw = False) fig_nox_eco.update_xaxes(range=(-30, 60)) #fig_nox_eco.update_yaxes(range=(-5,80)) fig_nox_eco.update_layout(height=500, width=1500) fig_nox_eco.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_nox_eco.show() #fig_nox_eco.write_html(f"{savefolder_plots}/{savename}_Plot_Ecophysics_NOx_899.html") fig_nox_eco.write_image(f"{savefolder_plots}/{savename}_Plot_Ecophysics_NOx_899.png") # save static plot
In [ ]:
# ICAD HONO if "ICAD_HONO" in FeCit_N.instruments: inst = "ICAD_HONO" #data = pd.read_csv(f"{savefolder}/Processed_Data/{savename}_Data_ICAD_HONO.csv", sep=';') # get data from file data = FeCit_N.data_icad_hono.copy() # get data from experiment fig_hono = FeCit_N.plot_one_instrument(instr="ICAD_HONO", data=data, plot_raw = False) fig_hono.update_xaxes(range=(-30, 60)) #fig_hono.update_yaxes(range=(-2,5)) fig_hono.update_layout(height=500, width=1500) fig_hono.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_hono.show() #fig_hono.write_html(f"{savefolder_plots}/{savename}_Plot_ICAD_HONO.html") fig_hono.write_image(f"{savefolder_plots}/{savename}_Plot_ICAD_HONO.png") # save static plot
In [ ]:
# ICAD NO2 if "ICAD_NO2" in FeCit_N.instruments: #data = pd.read_csv(f"{savefolder}/Processed_Data/{savename}_Data_ICAD_NO2.csv", sep=';') # get data from file data = FeCit_N.data_icad_no2.copy() # get data from experiment fig_no2 = FeCit_N.plot_one_instrument(instr="ICAD_NO2", data=None, plot_raw = False) fig_no2.update_xaxes(range=(-30, 60)) fig_no2.update_yaxes(range=(-1,8)) fig_no2.update_layout(height=500, width=1500) fig_no2.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_no2.show() #fig_no2.write_html(f"{savefolder_plots}/{savename}_Plot_ICAD_NO2.html") fig_no2.write_image(f"{savefolder_plots}/{savename}_Plot_ICAD_NO2.png") # save static plot
In [ ]:
if "Humidity_Sensors" in FeCit_N.instruments: rh_plot = FeCit_N.RHTDP_plot(num_timeaxis=True) # make RH-temperature-dewpoint plot FeCit_N.add_timemarkers(rh_plot) # add yellow rectangle to mark irradiation period rh_plot.update_layout(height=500, width=1500, showlegend=True) rh_plot.update_xaxes(range=(-30, 60)) #rh_plot.update_yaxes(range=(20, 30), row=1) #rh_plot.update_yaxes(range=(-0, 10), row=3) rh_plot.update_yaxes(range=(10, 30), row=1) rh_plot.update_yaxes(range=(-5, 5), row=3) rh_plot.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) # dummy legend for irradiation rh_plot.add_trace( go.Scatter( x=[None], y=[None], mode="markers", marker=dict( size=12, symbol="square", color="gold", opacity=0.3 ), name="Irradiation", showlegend=True, ), row=1, col=1 ) rh_plot.show() #rh_plot.write_html(f"{savefolder_plots}/{savename}_Plot_Humidity_Sensors.html") rh_plot.write_image(f"{savefolder_plots}/{savename}_Plot_Humidity_Sensors.png") # save static plot
Multiple Instruments per Figure¶
Note: Do not plot CO2 here as it will use the wrong yaxis unit label. Use plot_multiple_2axes to plot CO2 with other traces (below).
In [ ]:
instrs = ["ICAD_HONO", "ICAD_NO2"] # instruments to plot title = "ICADs" if set(instrs).issubset(set(FeCit_N.instruments)): # if chosen instruments were all actually used fig_icad = FeCit_N.plot_multiple(title, instrs) fig_icad.update_xaxes(range=(-30, 60)) fig_icad.update_yaxes(range=(-5,45)) fig_icad.update_layout(height=500, width=1500) fig_icad.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig_icad.show() #fig_icad.write_html(f"{savefolder_plots}/{savename}_Plot_ICADs.html") #fig_icad.write_image(f"{savefolder_plots}/{savename}_Plot_ICADs.png") # save static plot else: print("One or more instruments in instrs are not in instrument_list. Skip plot.")
Multiple Instruments Figures with 2 y-axes:
In [ ]:
instrs1 = ["T360U_CO2"] # instruments to plot on left axis (never mix CO2 with other instruments because of different units) instrs2 = FeCit_N.instruments[1:-1] # everything except the first (CO2) title = "All parameters" if set(instrs1).issubset(set(FeCit_N.instruments)) and set(instrs2).issubset(set(FeCit_N.instruments)): fig = FeCit_N.plot_multiple_2axes(title, instrs1, instrs2) fig.update_xaxes(range=(-30, 60)) ymin, ymax = -5, 35 # y-axis range #ymin, ymax = -0.5, 1 fig.update_yaxes(range=(ymin,ymax), secondary_y=False) fig.update_yaxes(range=(ymin*10,ymax*10), secondary_y=True) # right axis scaled based on left axis fig.update_layout(height=600, width=1800) fig.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig.show() fig.write_html(f"{savefolder_plots}/{savename}_Plot_All.html") # save interactive plot fig.write_image(f"{savefolder_plots}/{savename}_Plot_All.png") # save static plot else: print("One or more instruments in instrs1 or instrs2 are not in instrument_list. Skip plot.")
In [ ]:
instrs1 = ["Ecophysics_NOx_899"] # instruments to plot on left axis instrs2 = ["ICAD_HONO", "ICAD_NO2"] # instruments to plot on right axis title = "NOx analyser vs ICADs" if set(instrs1).issubset(set(FeCit_N.instruments)) and set(instrs2).issubset(set(FeCit_N.instruments)): fig = FeCit_N.plot_multiple_2axes(title, instrs1, instrs2) fig.update_xaxes(range=(-30, 60)) #fig.update_yaxes(range=(-10,40)) fig.update_layout(height=500, width=1500) fig.update_layout( margin=dict(l=70, r=210, t=70, b=70) # control margins to keep the plot area consistent between plots ) fig.show() else: print("One or more instruments in instrs1 or instrs2 are not in instrument_list. Skip plot.")
In [ ]: